summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:57:31 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:57:31 +0000
commitdc0db358abe19481e475e10c32149b53370f1a1c (patch)
treeab8ce99c4b255ce46f99ef402c27916055b899ee /src/tools/rust-analyzer/crates
parentReleasing progress-linux version 1.71.1+dfsg1-2~progress7.99u1. (diff)
downloadrustc-dc0db358abe19481e475e10c32149b53370f1a1c.tar.xz
rustc-dc0db358abe19481e475e10c32149b53370f1a1c.zip
Merging upstream version 1.72.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/rust-analyzer/crates')
-rw-r--r--src/tools/rust-analyzer/crates/base-db/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/change.rs23
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/fixture.rs90
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/input.rs344
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/lib.rs29
-rw-r--r--src/tools/rust-analyzer/crates/cfg/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/flycheck/Cargo.toml5
-rw-r--r--src/tools/rust-analyzer/crates/flycheck/src/lib.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/Cargo.toml3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr.rs241
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs)170
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs40
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs400
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs1072
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs138
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs68
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs246
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/adt.rs)73
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs60
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/keys.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expander.rs211
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/find_path.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/generics.rs48
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/expr.rs)132
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs)131
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/import_map.rs98
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs91
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs293
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/layout.rs97
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs407
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lower.rs45
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs360
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs149
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs131
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs311
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs (renamed from src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs)85
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres.rs286
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs661
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs141
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs256
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path.rs91
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/pretty.rs80
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs152
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/src.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/test_db.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/visibility.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs151
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs713
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs335
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs256
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/eager.rs240
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs214
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs102
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/name.rs80
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs53
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/quote.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/Cargo.toml12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs84
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/builder.rs50
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs149
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs55
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs119
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs1435
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs377
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs115
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs43
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs51
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs671
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs542
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs931
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs64
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs461
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs218
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs59
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs127
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs157
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/interner.rs147
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs68
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout.rs150
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs106
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs192
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs257
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs85
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs325
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs305
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir.rs343
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs282
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs2209
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs792
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs676
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs1795
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs163
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs617
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs351
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs185
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs45
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs111
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs59
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs74
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs47
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs43
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs234
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs398
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs383
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tls.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/traits.rs55
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/utils.rs154
-rw-r--r--src/tools/rust-analyzer/crates/hir/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/attrs.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/db.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs59
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/display.rs72
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/from_id.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs796
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs66
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs94
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/symbols.rs297
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs538
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs161
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs37
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs248
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs209
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs185
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs51
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs58
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs133
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs41
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs125
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs722
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs47
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs30
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs28
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs43
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs30
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs351
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs111
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs119
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/lib.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs67
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs110
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions.rs132
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs161
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs84
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs149
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs47
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs45
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/item.rs49
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs52
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs111
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs56
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs54
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs60
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs42
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs291
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs52
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs47
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs39
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/assists.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/defs.rs23
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/helpers.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs34
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/lib.rs225
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/line_index.rs314
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs167
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/rename.rs35
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs35
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/source_change.rs125
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs103
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt216
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt372
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs49
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/traits.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs41
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs45
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs175
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs474
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs145
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs232
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs88
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs30
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs91
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs47
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs36
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs177
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs152
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/expand_macro.rs85
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/extend_selection.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs42
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/file_structure.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/fixture.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs54
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs89
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/highlight_related.rs351
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover.rs44
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs333
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs897
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs146
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs113
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs450
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs27
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs93
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs207
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs80
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs144
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs17
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/interpret_function.rs46
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs61
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/moniker.rs122
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/navigation_target.rs213
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/prime_caches.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/references.rs26
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/runnables.rs46
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/signature_help.rs502
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/ssr.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/status.rs241
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs163
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs29
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html24
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html28
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html124
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs2
-rw-r--r--src/tools/rust-analyzer/crates/intern/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/lib.rs95
-rw-r--r--src/tools/rust-analyzer/crates/limit/src/lib.rs1
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/benchmark.rs15
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander.rs5
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs34
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs128
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/lib.rs49
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/parser.rs36
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs74
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs7
-rw-r--r--src/tools/rust-analyzer/crates/parser/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar.rs15
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs91
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs13
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs26
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/items.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs103
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/types.rs24
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/lexed_str.rs56
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/parser.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/shortcuts.rs6
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs4
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs3
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast (renamed from src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rast)0
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rs (renamed from src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast (renamed from src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rast)0
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rs (renamed from src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast18
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast24
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs3
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast26
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs3
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast24
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast120
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs8
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast39
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast38
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast58
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs6
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast102
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast58
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast175
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs4
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast269
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs13
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast76
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs2
-rw-r--r--src/tools/rust-analyzer/crates/paths/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/paths/src/lib.rs16
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml5
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs37
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs10
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs60
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs28
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs2
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs38
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs106
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs156
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs510
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs32
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs89
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs451
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs304
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs81
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs83
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs332
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs166
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs1106
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs139
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs840
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs146
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/cli.rs34
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs28
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs57
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs (renamed from src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs)81
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs (renamed from src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs)79
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server/symbol.rs (renamed from src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs)16
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs (renamed from src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs)16
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs4
-rw-r--r--src/tools/rust-analyzer/crates/profile/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/profile/src/memory_usage.rs6
-rw-r--r--src/tools/rust-analyzer/crates/project-model/Cargo.toml6
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs29
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs92
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs8
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs4
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/project_json.rs39
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs64
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs1881
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs657
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt344
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt344
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt340
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt462
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json6420
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json12816
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml26
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs100
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs11
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs18
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs17
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs421
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs29
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs74
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs41
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs19
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs293
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs17
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs43
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs90
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs22
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs134
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs334
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs (renamed from src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs)237
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs9
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs3
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs71
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs38
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs655
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs10
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs14
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs319
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs59
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs33
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs200
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs89
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs32
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs2
-rw-r--r--src/tools/rust-analyzer/crates/sourcegen/src/lib.rs26
-rw-r--r--src/tools/rust-analyzer/crates/stdx/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/hash.rs80
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/thread.rs102
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/thread/intent.rs287
-rw-r--r--src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs92
-rw-r--r--src/tools/rust-analyzer/crates/syntax/Cargo.toml8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram10
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs17
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs7
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs5
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs21
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs155
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs71
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/lib.rs3
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs3
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs10
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/token_text.rs7
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/validation.rs56
-rw-r--r--src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs4
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/fixture.rs96
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs565
-rw-r--r--src/tools/rust-analyzer/crates/text-edit/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/text-edit/src/lib.rs52
-rw-r--r--src/tools/rust-analyzer/crates/tt/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/tt/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/vfs/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/vfs/src/file_set.rs4
-rw-r--r--src/tools/rust-analyzer/crates/vfs/src/lib.rs23
-rw-r--r--src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs5
572 files changed, 59258 insertions, 18046 deletions
diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml
index f6a1075c1..6001772c8 100644
--- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml
@@ -15,6 +15,10 @@ doctest = false
salsa = "0.17.0-pre.2"
rustc-hash = "1.1.0"
+triomphe.workspace = true
+
+la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
+
# local deps
cfg.workspace = true
profile.workspace = true
diff --git a/src/tools/rust-analyzer/crates/base-db/src/change.rs b/src/tools/rust-analyzer/crates/base-db/src/change.rs
index b57f23457..6a3b36b23 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/change.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/change.rs
@@ -1,19 +1,21 @@
//! Defines a unit of change that can applied to the database to get the next
//! state. Changes are transactional.
-use std::{fmt, sync::Arc};
+use std::fmt;
use salsa::Durability;
+use triomphe::Arc;
use vfs::FileId;
-use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId};
+use crate::{CrateGraph, ProcMacros, SourceDatabaseExt, SourceRoot, SourceRootId};
/// Encapsulate a bunch of raw `.set` calls on the database.
#[derive(Default)]
pub struct Change {
pub roots: Option<Vec<SourceRoot>>,
- pub files_changed: Vec<(FileId, Option<Arc<String>>)>,
+ pub files_changed: Vec<(FileId, Option<Arc<str>>)>,
pub crate_graph: Option<CrateGraph>,
+ pub proc_macros: Option<ProcMacros>,
}
impl fmt::Debug for Change {
@@ -33,7 +35,7 @@ impl fmt::Debug for Change {
}
impl Change {
- pub fn new() -> Change {
+ pub fn new() -> Self {
Change::default()
}
@@ -41,7 +43,7 @@ impl Change {
self.roots = Some(roots);
}
- pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<String>>) {
+ pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<str>>) {
self.files_changed.push((file_id, new_text))
}
@@ -49,6 +51,10 @@ impl Change {
self.crate_graph = Some(graph);
}
+ pub fn set_proc_macros(&mut self, proc_macros: ProcMacros) {
+ self.proc_macros = Some(proc_macros);
+ }
+
pub fn apply(self, db: &mut dyn SourceDatabaseExt) {
let _p = profile::span("RootDatabase::apply_change");
if let Some(roots) = self.roots {
@@ -67,11 +73,14 @@ impl Change {
let source_root = db.source_root(source_root_id);
let durability = durability(&source_root);
// XXX: can't actually remove the file, just reset the text
- let text = text.unwrap_or_default();
+ let text = text.unwrap_or_else(|| Arc::from(""));
db.set_file_text_with_durability(file_id, text, durability)
}
if let Some(crate_graph) = self.crate_graph {
- db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
+ db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH);
+ }
+ if let Some(proc_macros) = self.proc_macros {
+ db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH);
}
}
}
diff --git a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
index 8a7e9dfad..d3abc3870 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
@@ -1,24 +1,27 @@
//! A set of high-level utility fixture methods to use in tests.
-use std::{mem, str::FromStr, sync::Arc};
+use std::{mem, str::FromStr, sync};
use cfg::CfgOptions;
use rustc_hash::FxHashMap;
use test_utils::{
- extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, ESCAPED_CURSOR_MARKER,
+ extract_range_or_offset, Fixture, FixtureWithProjectMeta, RangeOrOffset, CURSOR_MARKER,
+ ESCAPED_CURSOR_MARKER,
};
+use triomphe::Arc;
use tt::token_id::{Leaf, Subtree, TokenTree};
use vfs::{file_set::FileSet, VfsPath};
use crate::{
input::{CrateName, CrateOrigin, LangCrateOrigin},
Change, CrateDisplayName, CrateGraph, CrateId, Dependency, Edition, Env, FileId, FilePosition,
- FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, SourceDatabaseExt,
- SourceRoot, SourceRootId,
+ FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacros, ReleaseChannel,
+ SourceDatabaseExt, SourceRoot, SourceRootId,
};
pub const WORKSPACE: SourceRootId = SourceRootId(0);
pub trait WithFixture: Default + SourceDatabaseExt + 'static {
+ #[track_caller]
fn with_single_file(ra_fixture: &str) -> (Self, FileId) {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
@@ -27,6 +30,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
(db, fixture.files[0])
}
+ #[track_caller]
fn with_many_files(ra_fixture: &str) -> (Self, Vec<FileId>) {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
@@ -35,6 +39,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
(db, fixture.files)
}
+ #[track_caller]
fn with_files(ra_fixture: &str) -> Self {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
@@ -43,6 +48,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
db
}
+ #[track_caller]
fn with_files_extra_proc_macros(
ra_fixture: &str,
proc_macros: Vec<(String, ProcMacro)>,
@@ -54,18 +60,21 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
db
}
+ #[track_caller]
fn with_position(ra_fixture: &str) -> (Self, FilePosition) {
let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
let offset = range_or_offset.expect_offset();
(db, FilePosition { file_id, offset })
}
+ #[track_caller]
fn with_range(ra_fixture: &str) -> (Self, FileRange) {
let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture);
let range = range_or_offset.expect_range();
(db, FileRange { file_id, range })
}
+ #[track_caller]
fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
@@ -100,9 +109,16 @@ impl ChangeFixture {
pub fn parse_with_proc_macros(
ra_fixture: &str,
- mut proc_macros: Vec<(String, ProcMacro)>,
+ mut proc_macro_defs: Vec<(String, ProcMacro)>,
) -> ChangeFixture {
- let (mini_core, proc_macro_names, fixture) = Fixture::parse(ra_fixture);
+ let FixtureWithProjectMeta { fixture, mini_core, proc_macro_names, toolchain } =
+ FixtureWithProjectMeta::parse(ra_fixture);
+ let toolchain = toolchain
+ .map(|it| {
+ ReleaseChannel::from_str(&it)
+ .unwrap_or_else(|| panic!("unknown release channel found: {it}"))
+ })
+ .unwrap_or(ReleaseChannel::Stable);
let mut change = Change::new();
let mut files = Vec::new();
@@ -157,16 +173,16 @@ impl ChangeFixture {
meta.edition,
Some(crate_name.clone().into()),
version,
- meta.cfg.clone(),
meta.cfg,
+ Default::default(),
meta.env,
- Ok(Vec::new()),
false,
origin,
meta.target_data_layout
.as_deref()
.map(Arc::from)
.ok_or_else(|| "target_data_layout unset".into()),
+ Some(toolchain),
);
let prev = crates.insert(crate_name.clone(), crate_id);
assert!(prev.is_none());
@@ -182,7 +198,7 @@ impl ChangeFixture {
default_target_data_layout = meta.target_data_layout;
}
- change.change_file(file_id, Some(Arc::new(text)));
+ change.change_file(file_id, Some(Arc::from(text)));
let path = VfsPath::new_virtual_path(meta.path);
file_set.insert(file_id, path);
files.push(file_id);
@@ -197,15 +213,15 @@ impl ChangeFixture {
Edition::CURRENT,
Some(CrateName::new("test").unwrap().into()),
None,
- default_cfg.clone(),
default_cfg,
- Env::default(),
- Ok(Vec::new()),
+ Default::default(),
+ Env::new_for_test_fixture(),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
default_target_data_layout
.map(|x| x.into())
.ok_or_else(|| "target_data_layout unset".into()),
+ Some(toolchain),
);
} else {
for (from, to, prelude) in crate_deps {
@@ -232,7 +248,7 @@ impl ChangeFixture {
fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string()));
roots.push(SourceRoot::new_library(fs));
- change.change_file(core_file, Some(Arc::new(mini_core.source_code())));
+ change.change_file(core_file, Some(Arc::from(mini_core.source_code())));
let all_crates = crate_graph.crates_in_topological_order();
@@ -241,13 +257,13 @@ impl ChangeFixture {
Edition::Edition2021,
Some(CrateDisplayName::from_canonical_name("core".to_string())),
None,
- CfgOptions::default(),
- CfgOptions::default(),
- Env::default(),
- Ok(Vec::new()),
+ Default::default(),
+ Default::default(),
+ Env::new_for_test_fixture(),
false,
CrateOrigin::Lang(LangCrateOrigin::Core),
target_layout.clone(),
+ Some(toolchain),
);
for krate in all_crates {
@@ -257,12 +273,13 @@ impl ChangeFixture {
}
}
+ let mut proc_macros = ProcMacros::default();
if !proc_macro_names.is_empty() {
let proc_lib_file = file_id;
file_id.0 += 1;
- proc_macros.extend(default_test_proc_macros());
- let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macros);
+ proc_macro_defs.extend(default_test_proc_macros());
+ let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macro_defs);
let mut fs = FileSet::default();
fs.insert(
proc_lib_file,
@@ -270,7 +287,7 @@ impl ChangeFixture {
);
roots.push(SourceRoot::new_library(fs));
- change.change_file(proc_lib_file, Some(Arc::new(source)));
+ change.change_file(proc_lib_file, Some(Arc::from(source)));
let all_crates = crate_graph.crates_in_topological_order();
@@ -279,14 +296,15 @@ impl ChangeFixture {
Edition::Edition2021,
Some(CrateDisplayName::from_canonical_name("proc_macros".to_string())),
None,
- CfgOptions::default(),
- CfgOptions::default(),
- Env::default(),
- Ok(proc_macro),
+ Default::default(),
+ Default::default(),
+ Env::new_for_test_fixture(),
true,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
target_layout,
+ Some(toolchain),
);
+ proc_macros.insert(proc_macros_crate, Ok(proc_macro));
for krate in all_crates {
crate_graph
@@ -305,6 +323,7 @@ impl ChangeFixture {
roots.push(root);
change.set_roots(roots);
change.set_crate_graph(crate_graph);
+ change.set_proc_macros(proc_macros);
ChangeFixture { file_position, files, change }
}
@@ -323,7 +342,7 @@ pub fn identity(_attr: TokenStream, item: TokenStream) -> TokenStream {
ProcMacro {
name: "identity".into(),
kind: crate::ProcMacroKind::Attr,
- expander: Arc::new(IdentityProcMacroExpander),
+ expander: sync::Arc::new(IdentityProcMacroExpander),
},
),
(
@@ -337,7 +356,7 @@ pub fn derive_identity(item: TokenStream) -> TokenStream {
ProcMacro {
name: "DeriveIdentity".into(),
kind: crate::ProcMacroKind::CustomDerive,
- expander: Arc::new(IdentityProcMacroExpander),
+ expander: sync::Arc::new(IdentityProcMacroExpander),
},
),
(
@@ -351,7 +370,7 @@ pub fn input_replace(attr: TokenStream, _item: TokenStream) -> TokenStream {
ProcMacro {
name: "input_replace".into(),
kind: crate::ProcMacroKind::Attr,
- expander: Arc::new(AttributeInputReplaceProcMacroExpander),
+ expander: sync::Arc::new(AttributeInputReplaceProcMacroExpander),
},
),
(
@@ -365,7 +384,7 @@ pub fn mirror(input: TokenStream) -> TokenStream {
ProcMacro {
name: "mirror".into(),
kind: crate::ProcMacroKind::FuncLike,
- expander: Arc::new(MirrorProcMacroExpander),
+ expander: sync::Arc::new(MirrorProcMacroExpander),
},
),
(
@@ -379,7 +398,7 @@ pub fn shorten(input: TokenStream) -> TokenStream {
ProcMacro {
name: "shorten".into(),
kind: crate::ProcMacroKind::FuncLike,
- expander: Arc::new(ShortenProcMacroExpander),
+ expander: sync::Arc::new(ShortenProcMacroExpander),
},
),
]
@@ -428,7 +447,7 @@ fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) {
let (version, origin) = match b.split_once(':') {
Some(("CratesIo", data)) => match data.split_once(',') {
Some((version, url)) => {
- (version, CrateOrigin::CratesIo { repo: Some(url.to_owned()), name: None })
+ (version, CrateOrigin::Local { repo: Some(url.to_owned()), name: None })
}
_ => panic!("Bad crates.io parameter: {data}"),
},
@@ -436,10 +455,9 @@ fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) {
};
(a.to_owned(), origin, Some(version.to_string()))
} else {
- let crate_origin = match &*crate_str {
- "std" => CrateOrigin::Lang(LangCrateOrigin::Std),
- "core" => CrateOrigin::Lang(LangCrateOrigin::Core),
- _ => CrateOrigin::CratesIo { repo: None, name: None },
+ let crate_origin = match LangCrateOrigin::from(&*crate_str) {
+ LangCrateOrigin::Other => CrateOrigin::Local { repo: None, name: None },
+ origin => CrateOrigin::Lang(origin),
};
(crate_str, crate_origin, None)
}
diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index 43388e915..f2e523675 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -6,14 +6,20 @@
//! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
//! actual IO is done and lowered to input.
-use std::{fmt, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc};
+use std::{fmt, mem, ops, panic::RefUnwindSafe, str::FromStr, sync};
use cfg::CfgOptions;
-use rustc_hash::FxHashMap;
-use stdx::hash::{NoHashHashMap, NoHashHashSet};
+use la_arena::{Arena, Idx};
+use rustc_hash::{FxHashMap, FxHashSet};
use syntax::SmolStr;
+use triomphe::Arc;
use tt::token_id::Subtree;
-use vfs::{file_set::FileSet, AnchoredPath, FileId, VfsPath};
+use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
+
+// Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`,
+// then the crate for the proc-macro hasn't been build yet as the build data is missing.
+pub type ProcMacroPaths = FxHashMap<CrateId, Result<(Option<String>, AbsPathBuf), String>>;
+pub type ProcMacros = FxHashMap<CrateId, ProcMacroLoadResult>;
/// Files are grouped into source roots. A source root is a directory on the
/// file systems which is watched for changes. Typically it corresponds to a
@@ -79,17 +85,22 @@ impl SourceRoot {
///
/// `CrateGraph` is `!Serialize` by design, see
/// <https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/architecture.md#serialization>
-#[derive(Debug, Clone, Default /* Serialize, Deserialize */)]
+#[derive(Clone, Default)]
pub struct CrateGraph {
- arena: NoHashHashMap<CrateId, CrateData>,
+ arena: Arena<CrateData>,
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct CrateId(pub u32);
+impl fmt::Debug for CrateGraph {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_map()
+ .entries(self.arena.iter().map(|(id, data)| (u32::from(id.into_raw()), data)))
+ .finish()
+ }
+}
-impl stdx::hash::NoHashHashable for CrateId {}
+pub type CrateId = Idx<CrateData>;
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CrateName(SmolStr);
impl CrateName {
@@ -130,12 +141,22 @@ impl ops::Deref for CrateName {
/// Origin of the crates. It is used in emitting monikers.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CrateOrigin {
- /// Crates that are from crates.io official registry,
- CratesIo { repo: Option<String>, name: Option<String> },
+ /// Crates that are from the rustc workspace
+ Rustc { name: String },
+ /// Crates that are workspace members,
+ Local { repo: Option<String>, name: Option<String> },
+ /// Crates that are non member libraries.
+ Library { repo: Option<String>, name: String },
/// Crates that are provided by the language, like std, core, proc-macro, ...
Lang(LangCrateOrigin),
}
+impl CrateOrigin {
+ pub fn is_local(&self) -> bool {
+ matches!(self, CrateOrigin::Local { .. })
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LangCrateOrigin {
Alloc,
@@ -173,7 +194,7 @@ impl fmt::Display for LangCrateOrigin {
}
}
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct CrateDisplayName {
// The name we use to display various paths (with `_`).
crate_name: CrateName,
@@ -249,10 +270,36 @@ pub type TargetLayoutLoadResult = Result<Arc<str>, Arc<str>>;
pub struct ProcMacro {
pub name: SmolStr,
pub kind: ProcMacroKind,
- pub expander: Arc<dyn ProcMacroExpander>,
+ pub expander: sync::Arc<dyn ProcMacroExpander>,
}
-#[derive(Debug, Clone)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum ReleaseChannel {
+ Stable,
+ Beta,
+ Nightly,
+}
+
+impl ReleaseChannel {
+ pub fn as_str(self) -> &'static str {
+ match self {
+ ReleaseChannel::Stable => "stable",
+ ReleaseChannel::Beta => "beta",
+ ReleaseChannel::Nightly => "nightly",
+ }
+ }
+
+ pub fn from_str(str: &str) -> Option<Self> {
+ Some(match str {
+ "" => ReleaseChannel::Stable,
+ "nightly" => ReleaseChannel::Nightly,
+ _ if str.starts_with("beta") => ReleaseChannel::Beta,
+ _ => return None,
+ })
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CrateData {
pub root_file_id: FileId,
pub edition: Edition,
@@ -265,13 +312,15 @@ pub struct CrateData {
/// `Dependency` matters), this name should only be used for UI.
pub display_name: Option<CrateDisplayName>,
pub cfg_options: CfgOptions,
- pub potential_cfg_options: CfgOptions,
- pub target_layout: TargetLayoutLoadResult,
+ /// The cfg options that could be used by the crate
+ pub potential_cfg_options: Option<CfgOptions>,
pub env: Env,
pub dependencies: Vec<Dependency>,
- pub proc_macro: ProcMacroLoadResult,
pub origin: CrateOrigin,
pub is_proc_macro: bool,
+ // FIXME: These things should not be per crate! These are more per workspace crate graph level things
+ pub target_layout: TargetLayoutLoadResult,
+ pub channel: Option<ReleaseChannel>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -290,7 +339,18 @@ pub struct Env {
entries: FxHashMap<String, String>,
}
-#[derive(Debug, Clone, PartialEq, Eq)]
+impl Env {
+ pub fn new_for_test_fixture() -> Self {
+ Env {
+ entries: FxHashMap::from_iter([(
+ String::from("__ra_is_test_fixture"),
+ String::from("__ra_is_test_fixture"),
+ )]),
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Dependency {
pub crate_id: CrateId,
pub name: CrateName,
@@ -320,12 +380,12 @@ impl CrateGraph {
display_name: Option<CrateDisplayName>,
version: Option<String>,
cfg_options: CfgOptions,
- potential_cfg_options: CfgOptions,
+ potential_cfg_options: Option<CfgOptions>,
env: Env,
- proc_macro: ProcMacroLoadResult,
is_proc_macro: bool,
origin: CrateOrigin,
target_layout: Result<Arc<str>, Arc<str>>,
+ channel: Option<ReleaseChannel>,
) -> CrateId {
let data = CrateData {
root_file_id,
@@ -335,16 +395,44 @@ impl CrateGraph {
cfg_options,
potential_cfg_options,
env,
- proc_macro,
dependencies: Vec::new(),
origin,
target_layout,
is_proc_macro,
+ channel,
};
- let crate_id = CrateId(self.arena.len() as u32);
- let prev = self.arena.insert(crate_id, data);
- assert!(prev.is_none());
- crate_id
+ self.arena.alloc(data)
+ }
+
+ /// Remove the crate from crate graph. If any crates depend on this crate, the dependency would be replaced
+ /// with the second input.
+ pub fn remove_and_replace(
+ &mut self,
+ id: CrateId,
+ replace_with: CrateId,
+ ) -> Result<(), CyclicDependenciesError> {
+ for (x, data) in self.arena.iter() {
+ if x == id {
+ continue;
+ }
+ for edge in &data.dependencies {
+ if edge.crate_id == id {
+ self.check_cycle_after_dependency(edge.crate_id, replace_with)?;
+ }
+ }
+ }
+ // if everything was ok, start to replace
+ for (x, data) in self.arena.iter_mut() {
+ if x == id {
+ continue;
+ }
+ for edge in &mut data.dependencies {
+ if edge.crate_id == id {
+ edge.crate_id = replace_with;
+ }
+ }
+ }
+ Ok(())
}
pub fn add_dep(
@@ -354,17 +442,26 @@ impl CrateGraph {
) -> Result<(), CyclicDependenciesError> {
let _p = profile::span("add_dep");
- // Check if adding a dep from `from` to `to` creates a cycle. To figure
- // that out, look for a path in the *opposite* direction, from `to` to
- // `from`.
- if let Some(path) = self.find_path(&mut NoHashHashSet::default(), dep.crate_id, from) {
+ self.check_cycle_after_dependency(from, dep.crate_id)?;
+
+ self.arena[from].add_dep(dep);
+ Ok(())
+ }
+
+ /// Check if adding a dep from `from` to `to` creates a cycle. To figure
+ /// that out, look for a path in the *opposite* direction, from `to` to
+ /// `from`.
+ fn check_cycle_after_dependency(
+ &self,
+ from: CrateId,
+ to: CrateId,
+ ) -> Result<(), CyclicDependenciesError> {
+ if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) {
let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect();
let err = CyclicDependenciesError { path };
- assert!(err.from().0 == from && err.to().0 == dep.crate_id);
+ assert!(err.from().0 == from && err.to().0 == to);
return Err(err);
}
-
- self.arena.get_mut(&from).unwrap().add_dep(dep);
Ok(())
}
@@ -373,14 +470,20 @@ impl CrateGraph {
}
pub fn iter(&self) -> impl Iterator<Item = CrateId> + '_ {
- self.arena.keys().copied()
+ self.arena.iter().map(|(idx, _)| idx)
+ }
+
+ // FIXME: used for `handle_hack_cargo_workspace`, should be removed later
+ #[doc(hidden)]
+ pub fn iter_mut(&mut self) -> impl Iterator<Item = (CrateId, &mut CrateData)> + '_ {
+ self.arena.iter_mut()
}
/// Returns an iterator over all transitive dependencies of the given crate,
/// including the crate itself.
pub fn transitive_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
let mut worklist = vec![of];
- let mut deps = NoHashHashSet::default();
+ let mut deps = FxHashSet::default();
while let Some(krate) = worklist.pop() {
if !deps.insert(krate) {
@@ -397,11 +500,11 @@ impl CrateGraph {
/// including the crate itself.
pub fn transitive_rev_deps(&self, of: CrateId) -> impl Iterator<Item = CrateId> {
let mut worklist = vec![of];
- let mut rev_deps = NoHashHashSet::default();
+ let mut rev_deps = FxHashSet::default();
rev_deps.insert(of);
- let mut inverted_graph = NoHashHashMap::<_, Vec<_>>::default();
- self.arena.iter().for_each(|(&krate, data)| {
+ let mut inverted_graph = FxHashMap::<_, Vec<_>>::default();
+ self.arena.iter().for_each(|(krate, data)| {
data.dependencies
.iter()
.for_each(|dep| inverted_graph.entry(dep.crate_id).or_default().push(krate))
@@ -424,9 +527,9 @@ impl CrateGraph {
/// come before the crate itself).
pub fn crates_in_topological_order(&self) -> Vec<CrateId> {
let mut res = Vec::new();
- let mut visited = NoHashHashSet::default();
+ let mut visited = FxHashSet::default();
- for krate in self.arena.keys().copied() {
+ for krate in self.iter() {
go(self, &mut visited, &mut res, krate);
}
@@ -434,7 +537,7 @@ impl CrateGraph {
fn go(
graph: &CrateGraph,
- visited: &mut NoHashHashSet<CrateId>,
+ visited: &mut FxHashSet<CrateId>,
res: &mut Vec<CrateId>,
source: CrateId,
) {
@@ -450,31 +553,56 @@ impl CrateGraph {
// FIXME: this only finds one crate with the given root; we could have multiple
pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> {
- let (&crate_id, _) =
+ let (crate_id, _) =
self.arena.iter().find(|(_crate_id, data)| data.root_file_id == file_id)?;
Some(crate_id)
}
+ pub fn sort_deps(&mut self) {
+ self.arena
+ .iter_mut()
+ .for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id));
+ }
+
/// Extends this crate graph by adding a complete disjoint second crate
- /// graph.
+ /// graph and adjust the ids in the [`ProcMacroPaths`] accordingly.
///
- /// The ids of the crates in the `other` graph are shifted by the return
- /// amount.
- pub fn extend(&mut self, other: CrateGraph) -> u32 {
- let start = self.arena.len() as u32;
- self.arena.extend(other.arena.into_iter().map(|(id, mut data)| {
- let new_id = id.shift(start);
- for dep in &mut data.dependencies {
- dep.crate_id = dep.crate_id.shift(start);
+ /// This will deduplicate the crates of the graph where possible.
+ /// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id.
+ /// If the crate dependencies were sorted, the resulting graph from this `extend` call will also have the crate dependencies sorted.
+ pub fn extend(&mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths) {
+ let topo = other.crates_in_topological_order();
+ let mut id_map: FxHashMap<CrateId, CrateId> = FxHashMap::default();
+
+ for topo in topo {
+ let crate_data = &mut other.arena[topo];
+ crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]);
+ crate_data.dependencies.sort_by_key(|dep| dep.crate_id);
+
+ let res = self.arena.iter().find_map(
+ |(id, data)| {
+ if data == crate_data {
+ Some(id)
+ } else {
+ None
+ }
+ },
+ );
+ if let Some(res) = res {
+ id_map.insert(topo, res);
+ } else {
+ let id = self.arena.alloc(crate_data.clone());
+ id_map.insert(topo, id);
}
- (new_id, data)
- }));
- start
+ }
+
+ *proc_macros =
+ mem::take(proc_macros).into_iter().map(|(id, macros)| (id_map[&id], macros)).collect();
}
fn find_path(
&self,
- visited: &mut NoHashHashSet<CrateId>,
+ visited: &mut FxHashSet<CrateId>,
from: CrateId,
to: CrateId,
) -> Option<Vec<CrateId>> {
@@ -500,14 +628,14 @@ impl CrateGraph {
// Work around for https://github.com/rust-lang/rust-analyzer/issues/6038.
// As hacky as it gets.
pub fn patch_cfg_if(&mut self) -> bool {
- let cfg_if = self.hacky_find_crate("cfg_if");
- let std = self.hacky_find_crate("std");
+ // we stupidly max by version in an attempt to have all duplicated std's depend on the same cfg_if so that deduplication still works
+ let cfg_if =
+ self.hacky_find_crate("cfg_if").max_by_key(|&it| self.arena[it].version.clone());
+ let std = self.hacky_find_crate("std").next();
match (cfg_if, std) {
(Some(cfg_if), Some(std)) => {
- self.arena.get_mut(&cfg_if).unwrap().dependencies.clear();
- self.arena
- .get_mut(&std)
- .unwrap()
+ self.arena[cfg_if].dependencies.clear();
+ self.arena[std]
.dependencies
.push(Dependency::new(CrateName::new("cfg_if").unwrap(), cfg_if));
true
@@ -516,21 +644,15 @@ impl CrateGraph {
}
}
- fn hacky_find_crate(&self, display_name: &str) -> Option<CrateId> {
- self.iter().find(|it| self[*it].display_name.as_deref() == Some(display_name))
+ fn hacky_find_crate<'a>(&'a self, display_name: &'a str) -> impl Iterator<Item = CrateId> + 'a {
+ self.iter().filter(move |it| self[*it].display_name.as_deref() == Some(display_name))
}
}
impl ops::Index<CrateId> for CrateGraph {
type Output = CrateData;
fn index(&self, crate_id: CrateId) -> &CrateData {
- &self.arena[&crate_id]
- }
-}
-
-impl CrateId {
- fn shift(self, amount: u32) -> CrateId {
- CrateId(self.0 + amount)
+ &self.arena[crate_id]
}
}
@@ -632,7 +754,7 @@ impl fmt::Display for CyclicDependenciesError {
mod tests {
use crate::CrateOrigin;
- use super::{CfgOptions, CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
+ use super::{CrateGraph, CrateName, Dependency, Edition::Edition2018, Env, FileId};
#[test]
fn detect_cyclic_dependency_indirect() {
@@ -642,39 +764,39 @@ mod tests {
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
let crate2 = graph.add_crate_root(
FileId(2u32),
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
let crate3 = graph.add_crate_root(
FileId(3u32),
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@@ -695,26 +817,26 @@ mod tests {
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
let crate2 = graph.add_crate_root(
FileId(2u32),
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@@ -732,39 +854,39 @@ mod tests {
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
let crate2 = graph.add_crate_root(
FileId(2u32),
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
let crate3 = graph.add_crate_root(
FileId(3u32),
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@@ -782,26 +904,26 @@ mod tests {
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
let crate2 = graph.add_crate_root(
FileId(2u32),
Edition2018,
None,
None,
- CfgOptions::default(),
- CfgOptions::default(),
+ Default::default(),
+ Default::default(),
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("".into()),
+ None,
);
assert!(graph
.add_dep(
diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
index 9720db9d8..af204e44e 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
@@ -6,18 +6,19 @@ mod input;
mod change;
pub mod fixture;
-use std::{panic, sync::Arc};
+use std::panic;
-use stdx::hash::NoHashHashSet;
+use rustc_hash::FxHashSet;
use syntax::{ast, Parse, SourceFile, TextRange, TextSize};
+use triomphe::Arc;
pub use crate::{
change::Change,
input::{
CrateData, CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency,
Edition, Env, LangCrateOrigin, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
- ProcMacroId, ProcMacroKind, ProcMacroLoadResult, SourceRoot, SourceRootId,
- TargetLayoutLoadResult,
+ ProcMacroId, ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths, ProcMacros,
+ ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult,
},
};
pub use salsa::{self, Cancelled};
@@ -53,13 +54,13 @@ pub struct FileRange {
pub range: TextRange,
}
-pub const DEFAULT_LRU_CAP: usize = 128;
+pub const DEFAULT_PARSE_LRU_CAP: usize = 128;
pub trait FileLoader {
/// Text of the file.
- fn file_text(&self, file_id: FileId) -> Arc<String>;
+ fn file_text(&self, file_id: FileId) -> Arc<str>;
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>;
- fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>>;
+ fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>;
}
/// Database which stores all significant input facts: source code and project
@@ -73,6 +74,10 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug {
/// The crate graph.
#[salsa::input]
fn crate_graph(&self) -> Arc<CrateGraph>;
+
+ /// The crate graph.
+ #[salsa::input]
+ fn proc_macros(&self) -> Arc<ProcMacros>;
}
fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFile> {
@@ -86,7 +91,7 @@ fn parse_query(db: &dyn SourceDatabase, file_id: FileId) -> Parse<ast::SourceFil
#[salsa::query_group(SourceDatabaseExtStorage)]
pub trait SourceDatabaseExt: SourceDatabase {
#[salsa::input]
- fn file_text(&self, file_id: FileId) -> Arc<String>;
+ fn file_text(&self, file_id: FileId) -> Arc<str>;
/// Path to a file, relative to the root of its source root.
/// Source root of the file.
#[salsa::input]
@@ -95,10 +100,10 @@ pub trait SourceDatabaseExt: SourceDatabase {
#[salsa::input]
fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
- fn source_root_crates(&self, id: SourceRootId) -> Arc<NoHashHashSet<CrateId>>;
+ fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>;
}
-fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<NoHashHashSet<CrateId>> {
+fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<FxHashSet<CrateId>> {
let graph = db.crate_graph();
let res = graph
.iter()
@@ -114,7 +119,7 @@ fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc<NoHas
pub struct FileLoaderDelegate<T>(pub T);
impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
- fn file_text(&self, file_id: FileId) -> Arc<String> {
+ fn file_text(&self, file_id: FileId) -> Arc<str> {
SourceDatabaseExt::file_text(self.0, file_id)
}
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
@@ -124,7 +129,7 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
source_root.resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
let _p = profile::span("relevant_crates");
let source_root = self.0.file_source_root(file_id);
self.0.source_root_crates(source_root)
diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
index 30709c968..495119d55 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
@@ -86,7 +86,7 @@ impl CfgOptions {
}
}
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CfgDiff {
// Invariants: No duplicates, no atom that's both in `enable` and `disable`.
enable: Vec<CfgAtom>,
diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml
index 609d18c4e..3f6671b1c 100644
--- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml
@@ -16,9 +16,8 @@ crossbeam-channel = "0.5.5"
tracing = "0.1.37"
cargo_metadata = "0.15.0"
rustc-hash = "1.1.0"
-serde = { version = "1.0.137", features = ["derive"] }
-serde_json = "1.0.86"
-jod-thread = "0.1.2"
+serde_json.workspace = true
+serde.workspace = true
command-group = "2.0.1"
# local deps
diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
index accb14a51..fbb943ccb 100644
--- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
@@ -77,7 +77,7 @@ impl fmt::Display for FlycheckConfig {
pub struct FlycheckHandle {
// XXX: drop order is significant
sender: Sender<StateChange>,
- _thread: jod_thread::JoinHandle,
+ _thread: stdx::thread::JoinHandle,
id: usize,
}
@@ -90,7 +90,7 @@ impl FlycheckHandle {
) -> FlycheckHandle {
let actor = FlycheckActor::new(id, sender, config, workspace_root);
let (sender, receiver) = unbounded::<StateChange>();
- let thread = jod_thread::Builder::new()
+ let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
.name("Flycheck".to_owned())
.spawn(move || actor.run(receiver))
.expect("failed to spawn thread");
@@ -395,7 +395,7 @@ struct CargoHandle {
/// The handle to the actual cargo process. As we cannot cancel directly from with
/// a read syscall dropping and therefore terminating the process is our best option.
child: JodGroupChild,
- thread: jod_thread::JoinHandle<io::Result<(bool, String)>>,
+ thread: stdx::thread::JoinHandle<io::Result<(bool, String)>>,
receiver: Receiver<CargoMessage>,
}
@@ -409,7 +409,7 @@ impl CargoHandle {
let (sender, receiver) = unbounded();
let actor = CargoActor::new(sender, stdout, stderr);
- let thread = jod_thread::Builder::new()
+ let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
.name("CargoHandle".to_owned())
.spawn(move || actor.run())
.expect("failed to spawn thread");
@@ -485,7 +485,7 @@ impl CargoActor {
error.push_str(line);
error.push('\n');
- return false;
+ false
};
let output = streaming_output(
self.stdout,
diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
index 31d4018d2..83c705164 100644
--- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
[dependencies]
anymap = "1.0.0-beta.2"
arrayvec = "0.7.2"
-bitflags = "1.3.2"
+bitflags = "2.1.0"
cov-mark = "2.0.0-pre.1"
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
dashmap = { version = "=5.4.0", features = ["raw-api"] }
@@ -29,6 +29,7 @@ once_cell = "1.17.0"
rustc-hash = "1.1.0"
smallvec.workspace = true
tracing = "0.1.35"
+triomphe.workspace = true
rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
index 200072c17..bab3bbc23 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
@@ -1,6 +1,11 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
-use std::{hash::Hash, ops, sync::Arc};
+pub mod builtin;
+
+#[cfg(test)]
+mod tests;
+
+use std::{hash::Hash, ops};
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
@@ -16,14 +21,16 @@ use syntax::{
ast::{self, HasAttrs, IsString},
AstPtr, AstToken, SmolStr, TextRange, TextSize,
};
+use triomphe::Arc;
use crate::{
db::DefDatabase,
item_tree::{AttrOwner, Fields, ItemTreeId, ItemTreeNode},
+ lang_item::LangItem,
nameres::{ModuleOrigin, ModuleSource},
src::{HasChildSource, HasSource},
- AdtId, AttrDefId, EnumId, GenericParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroId,
- VariantId,
+ AdtId, AssocItemLoc, AttrDefId, EnumId, GenericParamId, ItemLoc, LocalEnumVariantId,
+ LocalFieldId, Lookup, MacroId, VariantId,
};
/// Holds documentation
@@ -88,6 +95,7 @@ impl Attrs {
db: &dyn DefDatabase,
e: EnumId,
) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> {
+ let _p = profile::span("variants_attrs_query");
// FIXME: There should be some proper form of mapping between item tree enum variant ids and hir enum variant ids
let mut res = ArenaMap::default();
@@ -114,6 +122,7 @@ impl Attrs {
db: &dyn DefDatabase,
v: VariantId,
) -> Arc<ArenaMap<LocalFieldId, Attrs>> {
+ let _p = profile::span("fields_attrs_query");
// FIXME: There should be some proper form of mapping between item tree field ids and hir field ids
let mut res = ArenaMap::default();
@@ -175,13 +184,13 @@ impl Attrs {
Arc::new(res)
}
+}
+impl Attrs {
pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> {
AttrQuery { attrs: self, key }
}
-}
-impl Attrs {
pub fn cfg(&self) -> Option<CfgExpr> {
let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse);
let first = cfgs.next()?;
@@ -193,6 +202,7 @@ impl Attrs {
None => Some(first),
}
}
+
pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
match self.cfg() {
None => true,
@@ -204,6 +214,10 @@ impl Attrs {
self.by_key("lang").string_value()
}
+ pub fn lang_item(&self) -> Option<LangItem> {
+ self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
+ }
+
pub fn docs(&self) -> Option<Documentation> {
let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value());
let indent = doc_indent(self);
@@ -238,6 +252,14 @@ impl Attrs {
})
}
+ pub fn doc_exprs(&self) -> impl Iterator<Item = DocExpr> + '_ {
+ self.by_key("doc").tt_values().map(DocExpr::parse)
+ }
+
+ pub fn doc_aliases(&self) -> impl Iterator<Item = SmolStr> + '_ {
+ self.doc_exprs().flat_map(|doc_expr| doc_expr.aliases().to_vec())
+ }
+
pub fn is_proc_macro(&self) -> bool {
self.by_key("proc_macro").exists()
}
@@ -249,10 +271,120 @@ impl Attrs {
pub fn is_proc_macro_derive(&self) -> bool {
self.by_key("proc_macro_derive").exists()
}
+
+ pub fn is_unstable(&self) -> bool {
+ self.by_key("unstable").exists()
+ }
+}
+
+use std::slice::Iter as SliceIter;
+#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
+pub enum DocAtom {
+ /// eg. `#[doc(hidden)]`
+ Flag(SmolStr),
+ /// eg. `#[doc(alias = "x")]`
+ ///
+ /// Note that a key can have multiple values that are all considered "active" at the same time.
+ /// For example, `#[doc(alias = "x")]` and `#[doc(alias = "y")]`.
+ KeyValue { key: SmolStr, value: SmolStr },
+}
+
+// Adapted from `CfgExpr` parsing code
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+// #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))]
+pub enum DocExpr {
+ Invalid,
+ /// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
+ Atom(DocAtom),
+ /// eg. `#[doc(alias("x", "y"))]`
+ Alias(Vec<SmolStr>),
+}
+
+impl From<DocAtom> for DocExpr {
+ fn from(atom: DocAtom) -> Self {
+ DocExpr::Atom(atom)
+ }
+}
+
+impl DocExpr {
+ fn parse<S>(tt: &tt::Subtree<S>) -> DocExpr {
+ next_doc_expr(&mut tt.token_trees.iter()).unwrap_or(DocExpr::Invalid)
+ }
+
+ pub fn aliases(&self) -> &[SmolStr] {
+ match self {
+ DocExpr::Atom(DocAtom::KeyValue { key, value }) if key == "alias" => {
+ std::slice::from_ref(value)
+ }
+ DocExpr::Alias(aliases) => aliases,
+ _ => &[],
+ }
+ }
+}
+
+fn next_doc_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<DocExpr> {
+ let name = match it.next() {
+ None => return None,
+ Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.text.clone(),
+ Some(_) => return Some(DocExpr::Invalid),
+ };
+
+ // Peek
+ let ret = match it.as_slice().first() {
+ Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) if punct.char == '=' => {
+ match it.as_slice().get(1) {
+ Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
+ it.next();
+ it.next();
+ // FIXME: escape? raw string?
+ let value =
+ SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"'));
+ DocAtom::KeyValue { key: name, value }.into()
+ }
+ _ => return Some(DocExpr::Invalid),
+ }
+ }
+ Some(tt::TokenTree::Subtree(subtree)) => {
+ it.next();
+ let subs = parse_comma_sep(subtree);
+ match name.as_str() {
+ "alias" => DocExpr::Alias(subs),
+ _ => DocExpr::Invalid,
+ }
+ }
+ _ => DocAtom::Flag(name).into(),
+ };
+
+ // Eat comma separator
+ if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = it.as_slice().first() {
+ if punct.char == ',' {
+ it.next();
+ }
+ }
+ Some(ret)
+}
+
+fn parse_comma_sep<S>(subtree: &tt::Subtree<S>) -> Vec<SmolStr> {
+ subtree
+ .token_trees
+ .iter()
+ .filter_map(|tt| match tt {
+ tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
+ // FIXME: escape? raw string?
+ Some(SmolStr::new(lit.text.trim_start_matches('"').trim_end_matches('"')))
+ }
+ _ => None,
+ })
+ .collect()
}
impl AttrsWithOwner {
- pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Self {
+ pub(crate) fn attrs_with_owner(db: &dyn DefDatabase, owner: AttrDefId) -> Self {
+ Self { attrs: db.attrs(owner), owner }
+ }
+
+ pub(crate) fn attrs_query(db: &dyn DefDatabase, def: AttrDefId) -> Attrs {
+ let _p = profile::span("attrs_query");
// FIXME: this should use `Trace` to avoid duplication in `source_map` below
let raw_attrs = match def {
AttrDefId::ModuleId(module) => {
@@ -286,31 +418,29 @@ impl AttrsWithOwner {
}
}
AttrDefId::FieldId(it) => {
- return Self { attrs: db.fields_attrs(it.parent)[it.local_id].clone(), owner: def };
+ return db.fields_attrs(it.parent)[it.local_id].clone();
}
AttrDefId::EnumVariantId(it) => {
- return Self {
- attrs: db.variants_attrs(it.parent)[it.local_id].clone(),
- owner: def,
- };
+ return db.variants_attrs(it.parent)[it.local_id].clone();
}
+ // FIXME: DRY this up
AttrDefId::AdtId(it) => match it {
- AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AdtId::EnumId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AdtId::UnionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AdtId::StructId(it) => attrs_from_item_tree_loc(db, it),
+ AdtId::EnumId(it) => attrs_from_item_tree_loc(db, it),
+ AdtId::UnionId(it) => attrs_from_item_tree_loc(db, it),
},
- AttrDefId::TraitId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::TraitAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AttrDefId::TraitId(it) => attrs_from_item_tree_loc(db, it),
+ AttrDefId::TraitAliasId(it) => attrs_from_item_tree_loc(db, it),
AttrDefId::MacroId(it) => match it {
- MacroId::Macro2Id(it) => attrs_from_item_tree(it.lookup(db).id, db),
- MacroId::MacroRulesId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- MacroId::ProcMacroId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ MacroId::Macro2Id(it) => attrs_from_item_tree(db, it.lookup(db).id),
+ MacroId::MacroRulesId(it) => attrs_from_item_tree(db, it.lookup(db).id),
+ MacroId::ProcMacroId(it) => attrs_from_item_tree(db, it.lookup(db).id),
},
- AttrDefId::ImplId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::ConstId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
- AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AttrDefId::ImplId(it) => attrs_from_item_tree_loc(db, it),
+ AttrDefId::ConstId(it) => attrs_from_item_tree_assoc(db, it),
+ AttrDefId::StaticId(it) => attrs_from_item_tree_assoc(db, it),
+ AttrDefId::FunctionId(it) => attrs_from_item_tree_assoc(db, it),
+ AttrDefId::TypeAliasId(it) => attrs_from_item_tree_assoc(db, it),
AttrDefId::GenericParamId(it) => match it {
GenericParamId::ConstParamId(it) => {
let src = it.parent().child_source(db);
@@ -331,11 +461,11 @@ impl AttrsWithOwner {
RawAttrs::from_attrs_owner(db.upcast(), src.with_value(&src.value[it.local_id]))
}
},
- AttrDefId::ExternBlockId(it) => attrs_from_item_tree(it.lookup(db).id, db),
+ AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
};
let attrs = raw_attrs.filter(db.upcast(), def.krate(db));
- Self { attrs: Attrs(attrs), owner: def }
+ Attrs(attrs)
}
pub fn source_map(&self, db: &dyn DefDatabase) -> AttrSourceMap {
@@ -371,7 +501,7 @@ impl AttrsWithOwner {
AttrDefId::FieldId(id) => {
let map = db.fields_attrs_source_map(id.parent);
let file_id = id.parent.file_id(db);
- let root = db.parse_or_expand(file_id).unwrap();
+ let root = db.parse_or_expand(file_id);
let owner = match &map[id.local_id] {
Either::Left(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
Either::Right(it) => ast::AnyHasAttrs::new(it.to_node(&root)),
@@ -379,28 +509,28 @@ impl AttrsWithOwner {
InFile::new(file_id, owner)
}
AttrDefId::AdtId(adt) => match adt {
- AdtId::StructId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AdtId::UnionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AdtId::EnumId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AdtId::StructId(id) => any_has_attrs(db, id),
+ AdtId::UnionId(id) => any_has_attrs(db, id),
+ AdtId::EnumId(id) => any_has_attrs(db, id),
},
- AttrDefId::FunctionId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::FunctionId(id) => any_has_attrs(db, id),
AttrDefId::EnumVariantId(id) => {
let map = db.variants_attrs_source_map(id.parent);
let file_id = id.parent.lookup(db).id.file_id();
- let root = db.parse_or_expand(file_id).unwrap();
+ let root = db.parse_or_expand(file_id);
InFile::new(file_id, ast::AnyHasAttrs::new(map[id.local_id].to_node(&root)))
}
- AttrDefId::StaticId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::ConstId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::TraitId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::TraitAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- AttrDefId::TypeAliasId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::StaticId(id) => any_has_attrs(db, id),
+ AttrDefId::ConstId(id) => any_has_attrs(db, id),
+ AttrDefId::TraitId(id) => any_has_attrs(db, id),
+ AttrDefId::TraitAliasId(id) => any_has_attrs(db, id),
+ AttrDefId::TypeAliasId(id) => any_has_attrs(db, id),
AttrDefId::MacroId(id) => match id {
- MacroId::Macro2Id(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- MacroId::MacroRulesId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
- MacroId::ProcMacroId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ MacroId::Macro2Id(id) => any_has_attrs(db, id),
+ MacroId::MacroRulesId(id) => any_has_attrs(db, id),
+ MacroId::ProcMacroId(id) => any_has_attrs(db, id),
},
- AttrDefId::ImplId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::ImplId(id) => any_has_attrs(db, id),
AttrDefId::GenericParamId(id) => match id {
GenericParamId::ConstParamId(id) => id
.parent()
@@ -415,7 +545,7 @@ impl AttrsWithOwner {
.child_source(db)
.map(|source| ast::AnyHasAttrs::new(source[id.local_id].clone())),
},
- AttrDefId::ExternBlockId(id) => id.lookup(db).source(db).map(ast::AnyHasAttrs::new),
+ AttrDefId::ExternBlockId(id) => any_has_attrs(db, id),
};
AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs))
@@ -635,19 +765,42 @@ impl<'attr> AttrQuery<'attr> {
.nth(2);
match name {
- Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ref text, ..}))) => Some(text),
+ Some(tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal{ ref text, ..}))) => Some(text),
_ => None
}
})
}
}
-fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase) -> RawAttrs {
+fn any_has_attrs(
+ db: &dyn DefDatabase,
+ id: impl Lookup<Data = impl HasSource<Value = impl ast::HasAttrs>>,
+) -> InFile<ast::AnyHasAttrs> {
+ id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
+}
+
+fn attrs_from_item_tree<N: ItemTreeNode>(db: &dyn DefDatabase, id: ItemTreeId<N>) -> RawAttrs {
let tree = id.item_tree(db);
let mod_item = N::id_to_mod_item(id.value);
tree.raw_attrs(mod_item.into()).clone()
}
+fn attrs_from_item_tree_loc<N: ItemTreeNode>(
+ db: &dyn DefDatabase,
+ lookup: impl Lookup<Data = ItemLoc<N>>,
+) -> RawAttrs {
+ let id = lookup.lookup(db).id;
+ attrs_from_item_tree(db, id)
+}
+
+fn attrs_from_item_tree_assoc<N: ItemTreeNode>(
+ db: &dyn DefDatabase,
+ lookup: impl Lookup<Data = AssocItemLoc<N>>,
+) -> RawAttrs {
+ let id = lookup.lookup(db).id;
+ attrs_from_item_tree(db, id)
+}
+
pub(crate) fn variants_attrs_source_map(
db: &dyn DefDatabase,
def: EnumId,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
index f7c1e683d..cead64a33 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
@@ -2,7 +2,7 @@
//!
//! The actual definitions were copied from rustc's `compiler/rustc_feature/src/builtin_attrs.rs`.
//!
-//! It was last synchronized with upstream commit c1a2db3372a4d6896744919284f3287650a38ab7.
+//! It was last synchronized with upstream commit e29821ff85a2a3000d226f99f62f89464028d5d6.
//!
//! The macros were adjusted to only expand to the attribute name, since that is all we need to do
//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
@@ -108,7 +108,7 @@ macro_rules! experimental {
};
}
-/// "Inert" built-in attributes that have a special meaning to rustc or rustdoc.
+/// Attributes that have a special meaning to rustc or rustdoc.
#[rustfmt::skip]
pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// ==========================================================================
@@ -123,7 +123,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
ungated!(ignore, Normal, template!(Word, NameValueStr: "reason"), WarnFollowing),
ungated!(
should_panic, Normal,
- template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"), FutureWarnFollowing,
+ template!(Word, List: r#"expected = "reason""#, NameValueStr: "reason"), FutureWarnFollowing,
),
// FIXME(Centril): This can be used on stable but shouldn't.
ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name"), ErrorFollowing),
@@ -142,20 +142,24 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// Lints:
ungated!(
- warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+ warn, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+ DuplicatesOk, @only_local: true,
),
ungated!(
- allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+ allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+ DuplicatesOk, @only_local: true,
),
gated!(
expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk,
lint_reasons, experimental!(expect)
),
ungated!(
- forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+ forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+ DuplicatesOk, @only_local: true,
),
ungated!(
- deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk
+ deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#),
+ DuplicatesOk, @only_local: true,
),
ungated!(must_use, Normal, template!(Word, NameValueStr: "reason"), FutureWarnFollowing),
gated!(
@@ -181,30 +185,28 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// ABI, linking, symbols, and FFI
ungated!(
link, Normal,
- template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#),
+ template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated""#),
DuplicatesOk,
),
ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(no_link, Normal, template!(Word), WarnFollowing),
- ungated!(repr, Normal, template!(List: "C"), DuplicatesOk),
+ ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, @only_local: true),
ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true),
ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing, @only_local: true),
+ ungated!(link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding),
// Limits:
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
ungated!(type_length_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
gated!(
- const_eval_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
- const_eval_limit, experimental!(const_eval_limit)
- ),
- gated!(
move_size_limit, CrateLevel, template!(NameValueStr: "N"), ErrorFollowing,
large_assignments, experimental!(move_size_limit)
),
// Entry point:
+ gated!(unix_sigpipe, Normal, template!(Word, NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing, experimental!(unix_sigpipe)),
ungated!(start, Normal, template!(Word), WarnFollowing),
ungated!(no_start, CrateLevel, template!(Word), WarnFollowing),
ungated!(no_main, CrateLevel, template!(Word), WarnFollowing),
@@ -226,11 +228,15 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
ungated!(inline, Normal, template!(Word, List: "always|never"), FutureWarnFollowing, @only_local: true),
ungated!(cold, Normal, template!(Word), WarnFollowing, @only_local: true),
ungated!(no_builtins, CrateLevel, template!(Word), WarnFollowing),
- ungated!(target_feature, Normal, template!(List: r#"enable = "name""#), DuplicatesOk),
+ ungated!(
+ target_feature, Normal, template!(List: r#"enable = "name""#),
+ DuplicatesOk, @only_local: true,
+ ),
ungated!(track_caller, Normal, template!(Word), WarnFollowing),
+ ungated!(instruction_set, Normal, template!(List: "set"), ErrorPreceding),
gated!(
no_sanitize, Normal,
- template!(List: "address, memory, thread"), DuplicatesOk,
+ template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
experimental!(no_sanitize)
),
gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)),
@@ -239,25 +245,23 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk
),
+ // Debugging
+ ungated!(
+ debugger_visualizer, Normal,
+ template!(List: r#"natvis_file = "...", gdb_script_file = "...""#), DuplicatesOk
+ ),
+
// ==========================================================================
// Unstable attributes:
// ==========================================================================
- // RFC #3191: #[debugger_visualizer] support
- gated!(
- debugger_visualizer, Normal, template!(List: r#"natvis_file = "...", gdb_script_file = "...""#),
- DuplicatesOk, experimental!(debugger_visualizer)
- ),
-
// Linking:
- gated!(naked, Normal, template!(Word), WarnFollowing, @only_local: true, naked_functions, experimental!(naked)),
gated!(
- link_ordinal, Normal, template!(List: "ordinal"), ErrorPreceding, raw_dylib,
- experimental!(link_ordinal)
+ naked, Normal, template!(Word), WarnFollowing, @only_local: true,
+ naked_functions, experimental!(naked)
),
// Plugins:
- // XXX Modified for use in rust-analyzer
// BuiltinAttribute {
// name: sym::plugin,
// only_local: false,
@@ -274,10 +278,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// cfg_fn!(plugin)
// ),
// },
- BuiltinAttribute {
- name: "plugin",
- template: template!(List: "name"),
- },
// Testing:
gated!(
@@ -286,7 +286,8 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
),
// RFC #1268
gated!(
- marker, Normal, template!(Word), WarnFollowing, marker_trait_attr, experimental!(marker)
+ marker, Normal, template!(Word), WarnFollowing, @only_local: true,
+ marker_trait_attr, experimental!(marker)
),
gated!(
thread_local, Normal, template!(Word), WarnFollowing,
@@ -298,11 +299,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
optimize, Normal, template!(List: "size|speed"), ErrorPreceding, optimize_attribute,
experimental!(optimize),
),
- // RFC 2867
- gated!(
- instruction_set, Normal, template!(List: "set"), ErrorPreceding,
- isa_attribute, experimental!(instruction_set)
- ),
gated!(
ffi_returns_twice, Normal, template!(Word), WarnFollowing, experimental!(ffi_returns_twice)
@@ -310,10 +306,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
gated!(ffi_pure, Normal, template!(Word), WarnFollowing, experimental!(ffi_pure)),
gated!(ffi_const, Normal, template!(Word), WarnFollowing, experimental!(ffi_const)),
gated!(
- register_attr, CrateLevel, template!(List: "attr1, attr2, ..."), DuplicatesOk,
- experimental!(register_attr),
- ),
- gated!(
register_tool, CrateLevel, template!(List: "tool1, tool2, ..."), DuplicatesOk,
experimental!(register_tool),
),
@@ -325,7 +317,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// RFC 2632
gated!(
const_trait, Normal, template!(Word), WarnFollowing, const_trait_impl,
- "`const` is a temporary placeholder for marking a trait that is suitable for `const` \
+ "`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \
`impls` and all default bodies as `const`, which may be removed or renamed in the \
future."
),
@@ -335,22 +327,47 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
experimental!(deprecated_safe),
),
+ // `#[collapse_debuginfo]`
+ gated!(
+ collapse_debuginfo, Normal, template!(Word), WarnFollowing,
+ experimental!(collapse_debuginfo)
+ ),
+
+ // RFC 2397
+ gated!(do_not_recommend, Normal, template!(Word), WarnFollowing, experimental!(do_not_recommend)),
+
+ // `#[cfi_encoding = ""]`
+ gated!(
+ cfi_encoding, Normal, template!(NameValueStr: "encoding"), ErrorPreceding,
+ experimental!(cfi_encoding)
+ ),
+
// ==========================================================================
// Internal attributes: Stability, deprecation, and unsafe:
// ==========================================================================
- ungated!(feature, CrateLevel, template!(List: "name1, name2, ..."), DuplicatesOk),
+ ungated!(
+ feature, CrateLevel,
+ template!(List: "name1, name2, ..."), DuplicatesOk, @only_local: true,
+ ),
// DuplicatesOk since it has its own validation
ungated!(
- stable, Normal, template!(List: r#"feature = "name", since = "version""#), DuplicatesOk,
+ stable, Normal,
+ template!(List: r#"feature = "name", since = "version""#), DuplicatesOk, @only_local: true,
),
ungated!(
unstable, Normal,
template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk,
),
ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
- ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
- ungated!(rustc_safe_intrinsic, Normal, template!(List: r#"feature = "name""#), DuplicatesOk),
+ ungated!(
+ rustc_const_stable, Normal,
+ template!(List: r#"feature = "name""#), DuplicatesOk, @only_local: true,
+ ),
+ ungated!(
+ rustc_default_body_unstable, Normal,
+ template!(List: r#"feature = "name", reason = "...", issue = "N""#), DuplicatesOk
+ ),
gated!(
allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."), DuplicatesOk,
"allow_internal_unstable side-steps feature gating and stability checks",
@@ -364,6 +381,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
allow_internal_unsafe, Normal, template!(Word), WarnFollowing,
"allow_internal_unsafe side-steps the unsafe_code lint",
),
+ ungated!(rustc_safe_intrinsic, Normal, template!(Word), DuplicatesOk),
+ rustc_attr!(rustc_allowed_through_unstable_modules, Normal, template!(Word), WarnFollowing,
+ "rustc_allowed_through_unstable_modules special cases accidental stabilizations of stable items \
+ through unstable paths"),
// ==========================================================================
// Internal attributes: Type system related:
@@ -381,10 +402,9 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
- gated!(
- alloc_error_handler, Normal, template!(Word), WarnFollowing,
- experimental!(alloc_error_handler)
- ),
+ rustc_attr!(rustc_reallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
+ rustc_attr!(rustc_deallocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
+ rustc_attr!(rustc_allocator_zeroed, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
gated!(
default_lib_allocator, Normal, template!(Word), WarnFollowing, allocator_internals,
experimental!(default_lib_allocator),
@@ -465,6 +485,12 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints
// to assist in changes to diagnostic APIs.
rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+ // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions`
+ // types (as well as any others in future).
+ rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+ // Used by the `rustc::bad_opt_access` lint on fields
+ // types (as well as any others in future).
+ rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE),
// ==========================================================================
// Internal attributes, Const related:
@@ -508,8 +534,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
"language items are subject to change",
),
rustc_attr!(
- rustc_pass_by_value, Normal,
- template!(Word), ErrorFollowing,
+ rustc_pass_by_value, Normal, template!(Word), ErrorFollowing,
"#[rustc_pass_by_value] is used to mark types that must be passed by value instead of reference."
),
rustc_attr!(
@@ -517,10 +542,18 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
"#![rustc_coherence_is_core] allows inherent methods on builtin types, only intended to be used in `core`."
),
rustc_attr!(
+ rustc_coinductive, AttributeType::Normal, template!(Word), WarnFollowing, @only_local: true,
+ "#![rustc_coinductive] changes a trait to be coinductive, allowing cycles in the trait solver."
+ ),
+ rustc_attr!(
rustc_allow_incoherent_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: true,
"#[rustc_allow_incoherent_impl] has to be added to all impl items of an incoherent inherent impl."
),
rustc_attr!(
+ rustc_deny_explicit_impl, AttributeType::Normal, template!(Word), ErrorFollowing, @only_local: false,
+ "#[rustc_deny_explicit_impl] enforces that a trait can have no user-provided impls"
+ ),
+ rustc_attr!(
rustc_has_incoherent_inherent_impls, AttributeType::Normal, template!(Word), ErrorFollowing,
"#[rustc_has_incoherent_inherent_impls] allows the addition of incoherent inherent impls for \
the given type by annotating all impl items with #[rustc_allow_incoherent_impl]."
@@ -531,24 +564,20 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
and it is only intended to be used in `alloc`."
),
- // modified for r-a
- // BuiltinAttribute {
- // name: sym::rustc_diagnostic_item,
- // // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
- // only_local: false,
- // type_: Normal,
- // template: template!(NameValueStr: "name"),
- // duplicates: ErrorFollowing,
- // gate: Gated(
- // Stability::Unstable,
- // sym::rustc_attrs,
- // "diagnostic items compiler internal support for linting",
- // cfg_fn!(rustc_attrs),
- // ),
- // },
BuiltinAttribute {
+ // name: sym::rustc_diagnostic_item,
name: "rustc_diagnostic_item",
+ // FIXME: This can be `true` once we always use `tcx.is_diagnostic_item`.
+ // only_local: false,
+ // type_: Normal,
template: template!(NameValueStr: "name"),
+ // duplicates: ErrorFollowing,
+ // gate: Gated(
+ // Stability::Unstable,
+ // sym::rustc_attrs,
+ // "diagnostic items compiler internal support for linting",
+ // cfg_fn!(rustc_attrs),
+ // ),
},
gated!(
// Used in resolve:
@@ -572,7 +601,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
for reserving for `for<T> From<!> for T` impl"
),
rustc_attr!(
- rustc_test_marker, Normal, template!(Word), WarnFollowing,
+ rustc_test_marker, Normal, template!(NameValueStr: "name"), WarnFollowing,
"the `#[rustc_test_marker]` attribute is used internally to track tests",
),
rustc_attr!(
@@ -598,11 +627,16 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
definition of a trait, it's currently in experimental form and should be changed before \
being exposed outside of the std"
),
+ rustc_attr!(
+ rustc_doc_primitive, Normal, template!(NameValueStr: "primitive name"), ErrorFollowing,
+ r#"`rustc_doc_primitive` is a rustc internal attribute"#,
+ ),
// ==========================================================================
// Internal attributes, Testing:
// ==========================================================================
+ rustc_attr!(TEST, rustc_effective_visibility, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_outlives, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_capture_analysis, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing),
@@ -643,6 +677,10 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_def_path, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk),
+ gated!(
+ custom_mir, Normal, template!(List: r#"dialect = "...", phase = "...""#),
+ ErrorFollowing, "the `#[custom_mir]` attribute is just used for the Rust test suite",
+ ),
rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs
new file mode 100644
index 000000000..e4c8d446a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/tests.rs
@@ -0,0 +1,40 @@
+//! This module contains tests for doc-expression parsing.
+//! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
+
+use mbe::syntax_node_to_token_tree;
+use syntax::{ast, AstNode};
+
+use crate::attr::{DocAtom, DocExpr};
+
+fn assert_parse_result(input: &str, expected: DocExpr) {
+ let (tt, _) = {
+ let source_file = ast::SourceFile::parse(input).ok().unwrap();
+ let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+ syntax_node_to_token_tree(tt.syntax())
+ };
+ let cfg = DocExpr::parse(&tt);
+ assert_eq!(cfg, expected);
+}
+
+#[test]
+fn test_doc_expr_parser() {
+ assert_parse_result("#![doc(hidden)]", DocAtom::Flag("hidden".into()).into());
+
+ assert_parse_result(
+ r#"#![doc(alias = "foo")]"#,
+ DocAtom::KeyValue { key: "alias".into(), value: "foo".into() }.into(),
+ );
+
+ assert_parse_result(r#"#![doc(alias("foo"))]"#, DocExpr::Alias(["foo".into()].into()));
+ assert_parse_result(
+ r#"#![doc(alias("foo", "bar", "baz"))]"#,
+ DocExpr::Alias(["foo".into(), "bar".into(), "baz".into()].into()),
+ );
+
+ assert_parse_result(
+ r#"
+ #[doc(alias("Bar", "Qux"))]
+ struct Foo;"#,
+ DocExpr::Alias(["Bar".into(), "Qux".into()].into()),
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index b70e658ef..94dc39b11 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -6,267 +6,30 @@ mod tests;
pub mod scope;
mod pretty;
-use std::{ops::Index, sync::Arc};
+use std::ops::Index;
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
-use drop_bomb::DropBomb;
use either::Either;
-use hir_expand::{
- attrs::RawAttrs, hygiene::Hygiene, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId,
-};
+use hir_expand::{name::Name, HirFileId, InFile};
use la_arena::{Arena, ArenaMap};
-use limit::Limit;
use profile::Count;
use rustc_hash::FxHashMap;
-use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
+use syntax::{ast, AstPtr, SyntaxNodePtr};
+use triomphe::Arc;
use crate::{
- attr::Attrs,
db::DefDatabase,
- expr::{
+ expander::Expander,
+ hir::{
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
},
- item_scope::BuiltinShadowMode,
- macro_id_to_def_id,
nameres::DefMap,
path::{ModPath, Path},
src::{HasChildSource, HasSource},
- AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
- UnresolvedMacro,
+ BlockId, DefWithBodyId, HasModule, Lookup,
};
-pub use lower::LowerCtx;
-
-/// A subset of Expander that only deals with cfg attributes. We only need it to
-/// avoid cyclic queries in crate def map during enum processing.
-#[derive(Debug)]
-pub(crate) struct CfgExpander {
- cfg_options: CfgOptions,
- hygiene: Hygiene,
- krate: CrateId,
-}
-
-#[derive(Debug)]
-pub struct Expander {
- cfg_expander: CfgExpander,
- def_map: Arc<DefMap>,
- current_file_id: HirFileId,
- module: LocalModuleId,
- /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
- recursion_depth: usize,
-}
-
-impl CfgExpander {
- pub(crate) fn new(
- db: &dyn DefDatabase,
- current_file_id: HirFileId,
- krate: CrateId,
- ) -> CfgExpander {
- let hygiene = Hygiene::new(db.upcast(), current_file_id);
- let cfg_options = db.crate_graph()[krate].cfg_options.clone();
- CfgExpander { cfg_options, hygiene, krate }
- }
-
- pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
- Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene))
- }
-
- pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool {
- let attrs = self.parse_attrs(db, owner);
- attrs.is_cfg_enabled(&self.cfg_options)
- }
-}
-
-impl Expander {
- pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
- let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
- let def_map = module.def_map(db);
- Expander {
- cfg_expander,
- def_map,
- current_file_id,
- module: module.local_id,
- recursion_depth: 0,
- }
- }
-
- pub fn enter_expand<T: ast::AstNode>(
- &mut self,
- db: &dyn DefDatabase,
- macro_call: ast::MacroCall,
- ) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> {
- let mut unresolved_macro_err = None;
-
- let result = self.within_limit(db, |this| {
- let macro_call = InFile::new(this.current_file_id, &macro_call);
-
- let resolver =
- |path| this.resolve_path_as_macro(db, &path).map(|it| macro_id_to_def_id(db, it));
-
- let mut err = None;
- let call_id = match macro_call.as_call_id_with_errors(
- db,
- this.def_map.krate(),
- resolver,
- &mut |e| {
- err.get_or_insert(e);
- },
- ) {
- Ok(call_id) => call_id,
- Err(resolve_err) => {
- unresolved_macro_err = Some(resolve_err);
- return ExpandResult { value: None, err: None };
- }
- };
- ExpandResult { value: call_id.ok(), err }
- });
-
- if let Some(err) = unresolved_macro_err {
- Err(err)
- } else {
- Ok(result)
- }
- }
-
- pub fn enter_expand_id<T: ast::AstNode>(
- &mut self,
- db: &dyn DefDatabase,
- call_id: MacroCallId,
- ) -> ExpandResult<Option<(Mark, T)>> {
- self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
- }
-
- fn enter_expand_inner(
- db: &dyn DefDatabase,
- call_id: MacroCallId,
- mut err: Option<ExpandError>,
- ) -> ExpandResult<Option<(HirFileId, SyntaxNode)>> {
- if err.is_none() {
- err = db.macro_expand_error(call_id);
- }
-
- let file_id = call_id.as_file();
-
- let raw_node = match db.parse_or_expand(file_id) {
- Some(it) => it,
- None => {
- // Only `None` if the macro expansion produced no usable AST.
- if err.is_none() {
- tracing::warn!("no error despite `parse_or_expand` failing");
- }
-
- return ExpandResult::only_err(err.unwrap_or_else(|| {
- ExpandError::Other("failed to parse macro invocation".into())
- }));
- }
- };
-
- ExpandResult { value: Some((file_id, raw_node)), err }
- }
-
- pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
- self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
- self.current_file_id = mark.file_id;
- if self.recursion_depth == usize::MAX {
- // Recursion limit has been reached somewhere in the macro expansion tree. Reset the
- // depth only when we get out of the tree.
- if !self.current_file_id.is_macro() {
- self.recursion_depth = 0;
- }
- } else {
- self.recursion_depth -= 1;
- }
- mark.bomb.defuse();
- }
-
- pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
- InFile { file_id: self.current_file_id, value }
- }
-
- pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
- self.cfg_expander.parse_attrs(db, owner)
- }
-
- pub(crate) fn cfg_options(&self) -> &CfgOptions {
- &self.cfg_expander.cfg_options
- }
-
- pub fn current_file_id(&self) -> HirFileId {
- self.current_file_id
- }
-
- fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
- let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene);
- Path::from_src(path, &ctx)
- }
-
- fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroId> {
- self.def_map.resolve_path(db, self.module, path, BuiltinShadowMode::Other).0.take_macros()
- }
-
- fn recursion_limit(&self, db: &dyn DefDatabase) -> Limit {
- let limit = db.crate_limits(self.cfg_expander.krate).recursion_limit as _;
-
- #[cfg(not(test))]
- return Limit::new(limit);
-
- // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
- #[cfg(test)]
- return Limit::new(std::cmp::min(32, limit));
- }
-
- fn within_limit<F, T: ast::AstNode>(
- &mut self,
- db: &dyn DefDatabase,
- op: F,
- ) -> ExpandResult<Option<(Mark, T)>>
- where
- F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
- {
- if self.recursion_depth == usize::MAX {
- // Recursion limit has been reached somewhere in the macro expansion tree. We should
- // stop expanding other macro calls in this tree, or else this may result in
- // exponential number of macro expansions, leading to a hang.
- //
- // The overflow error should have been reported when it occurred (see the next branch),
- // so don't return overflow error here to avoid diagnostics duplication.
- cov_mark::hit!(overflow_but_not_me);
- return ExpandResult::only_err(ExpandError::RecursionOverflowPosioned);
- } else if self.recursion_limit(db).check(self.recursion_depth + 1).is_err() {
- self.recursion_depth = usize::MAX;
- cov_mark::hit!(your_stack_belongs_to_me);
- return ExpandResult::only_err(ExpandError::Other(
- "reached recursion limit during macro expansion".into(),
- ));
- }
-
- let ExpandResult { value, err } = op(self);
- let Some(call_id) = value else {
- return ExpandResult { value: None, err };
- };
-
- Self::enter_expand_inner(db, call_id, err).map(|value| {
- value.and_then(|(new_file_id, node)| {
- let node = T::cast(node)?;
-
- self.recursion_depth += 1;
- self.cfg_expander.hygiene = Hygiene::new(db.upcast(), new_file_id);
- let old_file_id = std::mem::replace(&mut self.current_file_id, new_file_id);
- let mark =
- Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") };
- Some((mark, node))
- })
- })
- }
-}
-
-#[derive(Debug)]
-pub struct Mark {
- file_id: HirFileId,
- bomb: DropBomb,
-}
-
/// The body of an item (function, const etc.).
#[derive(Debug, Eq, PartialEq)]
pub struct Body {
@@ -274,6 +37,9 @@ pub struct Body {
pub pats: Arena<Pat>,
pub bindings: Arena<Binding>,
pub labels: Arena<Label>,
+ /// Id of the closure/generator that owns the corresponding binding. If a binding is owned by the
+ /// top level expression, it will not be listed in here.
+ pub binding_owners: FxHashMap<BindingId, ExprId>,
/// The patterns for the function's parameters. While the parameter types are
/// part of the function signature, the patterns are not (they don't change
/// the external type of the function).
@@ -343,6 +109,8 @@ pub enum BodyDiagnostic {
MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String },
UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>>, krate: CrateId },
UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath },
+ UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
+ UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
}
impl Body {
@@ -353,45 +121,51 @@ impl Body {
let _p = profile::span("body_with_source_map_query");
let mut params = None;
- let (file_id, module, body) = match def {
- DefWithBodyId::FunctionId(f) => {
- let f = f.lookup(db);
- let src = f.source(db);
- params = src.value.param_list().map(|param_list| {
- let item_tree = f.id.item_tree(db);
- let func = &item_tree[f.id.value];
- let krate = f.container.module(db).krate;
- let crate_graph = db.crate_graph();
- (
- param_list,
- func.params.clone().map(move |param| {
- item_tree
- .attrs(db, krate, param.into())
- .is_cfg_enabled(&crate_graph[krate].cfg_options)
- }),
- )
- });
- (src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
- }
- DefWithBodyId::ConstId(c) => {
- let c = c.lookup(db);
- let src = c.source(db);
- (src.file_id, c.module(db), src.value.body())
- }
- DefWithBodyId::StaticId(s) => {
- let s = s.lookup(db);
- let src = s.source(db);
- (src.file_id, s.module(db), src.value.body())
- }
- DefWithBodyId::VariantId(v) => {
- let e = v.parent.lookup(db);
- let src = v.parent.child_source(db);
- let variant = &src.value[v.local_id];
- (src.file_id, e.container, variant.expr())
+ let mut is_async_fn = false;
+ let InFile { file_id, value: body } = {
+ match def {
+ DefWithBodyId::FunctionId(f) => {
+ let data = db.function_data(f);
+ let f = f.lookup(db);
+ let src = f.source(db);
+ params = src.value.param_list().map(|param_list| {
+ let item_tree = f.id.item_tree(db);
+ let func = &item_tree[f.id.value];
+ let krate = f.container.module(db).krate;
+ let crate_graph = db.crate_graph();
+ (
+ param_list,
+ func.params.clone().map(move |param| {
+ item_tree
+ .attrs(db, krate, param.into())
+ .is_cfg_enabled(&crate_graph[krate].cfg_options)
+ }),
+ )
+ });
+ is_async_fn = data.has_async_kw();
+ src.map(|it| it.body().map(ast::Expr::from))
+ }
+ DefWithBodyId::ConstId(c) => {
+ let c = c.lookup(db);
+ let src = c.source(db);
+ src.map(|it| it.body())
+ }
+ DefWithBodyId::StaticId(s) => {
+ let s = s.lookup(db);
+ let src = s.source(db);
+ src.map(|it| it.body())
+ }
+ DefWithBodyId::VariantId(v) => {
+ let src = v.parent.child_source(db);
+ src.map(|it| it[v.local_id].expr())
+ }
+ DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
}
};
+ let module = def.module(db);
let expander = Expander::new(db, file_id, module);
- let (mut body, source_map) = Body::new(db, expander, params, body);
+ let (mut body, source_map) =
+ Body::new(db, def, expander, params, body, module.krate, is_async_fn);
body.shrink_to_fit();
(Arc::new(body), Arc::new(source_map))
@@ -406,46 +180,65 @@ impl Body {
&'a self,
db: &'a dyn DefDatabase,
) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + '_ {
- self.block_scopes
- .iter()
- .map(move |&block| (block, db.block_def_map(block).expect("block ID without DefMap")))
+ self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block)))
}
pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String {
pretty::print_body_hir(db, self, owner)
}
+ pub fn pretty_print_expr(
+ &self,
+ db: &dyn DefDatabase,
+ owner: DefWithBodyId,
+ expr: ExprId,
+ ) -> String {
+ pretty::print_expr_hir(db, self, owner, expr)
+ }
+
fn new(
db: &dyn DefDatabase,
+ owner: DefWithBodyId,
expander: Expander,
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
+ krate: CrateId,
+ is_async_fn: bool,
) -> (Body, BodySourceMap) {
- lower::lower(db, expander, params, body)
+ lower::lower(db, owner, expander, params, body, krate, is_async_fn)
}
fn shrink_to_fit(&mut self) {
- let Self { _c: _, body_expr: _, block_scopes, exprs, labels, params, pats, bindings } =
- self;
+ let Self {
+ _c: _,
+ body_expr: _,
+ block_scopes,
+ exprs,
+ labels,
+ params,
+ pats,
+ bindings,
+ binding_owners,
+ } = self;
block_scopes.shrink_to_fit();
exprs.shrink_to_fit();
labels.shrink_to_fit();
params.shrink_to_fit();
pats.shrink_to_fit();
bindings.shrink_to_fit();
+ binding_owners.shrink_to_fit();
}
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
self.walk_pats(pat_id, &mut |pat| {
- if let Pat::Bind { id, .. } = pat {
+ if let Pat::Bind { id, .. } = &self[pat] {
f(*id);
}
});
}
- pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(&Pat)) {
+ pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) {
let pat = &self[pat_id];
- f(pat);
match pat {
Pat::Range { .. }
| Pat::Lit(..)
@@ -455,21 +248,37 @@ impl Body {
| Pat::Missing => {}
&Pat::Bind { subpat, .. } => {
if let Some(subpat) = subpat {
- self.walk_pats(subpat, f);
+ f(subpat);
}
}
Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
- args.iter().copied().for_each(|p| self.walk_pats(p, f));
+ args.iter().copied().for_each(|p| f(p));
}
- Pat::Ref { pat, .. } => self.walk_pats(*pat, f),
+ Pat::Ref { pat, .. } => f(*pat),
Pat::Slice { prefix, slice, suffix } => {
let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
- total_iter.copied().for_each(|p| self.walk_pats(p, f));
+ total_iter.copied().for_each(|p| f(p));
}
Pat::Record { args, .. } => {
- args.iter().for_each(|RecordFieldPat { pat, .. }| self.walk_pats(*pat, f));
+ args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat));
+ }
+ Pat::Box { inner } => f(*inner),
+ }
+ }
+
+ pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(PatId)) {
+ f(pat_id);
+ self.walk_pats_shallow(pat_id, |p| self.walk_pats(p, f));
+ }
+
+ pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool {
+ match self.binding_owners.get(&binding) {
+ Some(x) => {
+ // We assign expression ids in a way that outer closures will receive
+ // a lower id
+ x.into_raw() < relative_to.into_raw()
}
- Pat::Box { inner } => self.walk_pats(*inner, f),
+ None => true,
}
}
}
@@ -484,6 +293,7 @@ impl Default for Body {
labels: Default::default(),
params: Default::default(),
block_scopes: Default::default(),
+ binding_owners: Default::default(),
_c: Default::default(),
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
index fedaf3955..b375ec63a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
@@ -1,120 +1,148 @@
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
//! representation.
-use std::{mem, sync::Arc};
+use std::mem;
+use base_db::CrateId;
use either::Either;
use hir_expand::{
ast_id_map::AstIdMap,
- hygiene::Hygiene,
name::{name, AsName, Name},
- AstId, ExpandError, HirFileId, InFile,
+ AstId, ExpandError, InFile,
};
use intern::Interned;
-use la_arena::Arena;
-use once_cell::unsync::OnceCell;
use profile::Count;
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use syntax::{
ast::{
- self, ArrayExprKind, AstChildren, HasArgList, HasLoopBody, HasName, LiteralKind,
+ self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasLoopBody, HasName,
SlicePatComponents,
},
AstNode, AstPtr, SyntaxNodePtr,
};
+use triomphe::Arc;
use crate::{
- adt::StructKind,
- body::{Body, BodySourceMap, Expander, ExprPtr, LabelPtr, LabelSource, PatPtr},
- body::{BodyDiagnostic, ExprSource, PatSource},
- builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
+ body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
+ data::adt::StructKind,
db::DefDatabase,
- expr::{
- dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, ClosureKind, Expr, ExprId,
- FloatTypeWrapper, Label, LabelId, Literal, MatchArm, Movability, Pat, PatId,
- RecordFieldPat, RecordLitField, Statement,
+ expander::Expander,
+ hir::{
+ dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy,
+ ClosureKind, Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
+ Pat, PatId, RecordFieldPat, RecordLitField, Statement,
},
item_scope::BuiltinShadowMode,
+ lang_item::LangItem,
+ lower::LowerCtx,
+ nameres::{DefMap, MacroSubNs},
path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef},
- AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro,
+ AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
};
-pub struct LowerCtx<'a> {
- pub db: &'a dyn DefDatabase,
- hygiene: Hygiene,
- ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
-}
-
-impl<'a> LowerCtx<'a> {
- pub fn new(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
- LowerCtx {
- db,
- hygiene: Hygiene::new(db.upcast(), file_id),
- ast_id_map: Some((file_id, OnceCell::new())),
- }
- }
-
- pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
- LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
- }
-
- pub(crate) fn hygiene(&self) -> &Hygiene {
- &self.hygiene
- }
-
- pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
- Path::from_src(ast, self)
- }
-
- pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> Option<AstId<N>> {
- let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
- let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
- Some(InFile::new(file_id, ast_id_map.ast_id(item)))
- }
-}
-
pub(super) fn lower(
db: &dyn DefDatabase,
+ owner: DefWithBodyId,
expander: Expander,
params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
+ krate: CrateId,
+ is_async_fn: bool,
) -> (Body, BodySourceMap) {
ExprCollector {
db,
+ owner,
+ krate,
+ def_map: expander.module.def_map(db),
source_map: BodySourceMap::default(),
ast_id_map: db.ast_id_map(expander.current_file_id),
body: Body {
- exprs: Arena::default(),
- pats: Arena::default(),
- bindings: Arena::default(),
- labels: Arena::default(),
+ exprs: Default::default(),
+ pats: Default::default(),
+ bindings: Default::default(),
+ binding_owners: Default::default(),
+ labels: Default::default(),
params: Vec::new(),
body_expr: dummy_expr_id(),
block_scopes: Vec::new(),
_c: Count::new(),
},
expander,
+ current_try_block_label: None,
is_lowering_assignee_expr: false,
is_lowering_generator: false,
+ label_ribs: Vec::new(),
+ current_binding_owner: None,
}
- .collect(params, body)
+ .collect(params, body, is_async_fn)
}
struct ExprCollector<'a> {
db: &'a dyn DefDatabase,
expander: Expander,
+ owner: DefWithBodyId,
+ def_map: Arc<DefMap>,
ast_id_map: Arc<AstIdMap>,
+ krate: CrateId,
body: Body,
source_map: BodySourceMap,
+
is_lowering_assignee_expr: bool,
is_lowering_generator: bool,
+
+ current_try_block_label: Option<LabelId>,
+ // points to the expression that a try expression will target (replaces current_try_block_label)
+ // catch_scope: Option<ExprId>,
+ // points to the expression that an unlabeled control flow will target
+ // loop_scope: Option<ExprId>,
+ // needed to diagnose non label control flow in while conditions
+ // is_in_loop_condition: bool,
+
+ // resolution
+ label_ribs: Vec<LabelRib>,
+ current_binding_owner: Option<ExprId>,
+}
+
+#[derive(Clone, Debug)]
+struct LabelRib {
+ kind: RibKind,
+ // Once we handle macro hygiene this will need to be a map
+ label: Option<(Name, LabelId)>,
+}
+
+impl LabelRib {
+ fn new(kind: RibKind) -> Self {
+ LabelRib { kind, label: None }
+ }
+ fn new_normal(label: (Name, LabelId)) -> Self {
+ LabelRib { kind: RibKind::Normal, label: Some(label) }
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum RibKind {
+ Normal,
+ Closure,
+ Constant,
+}
+
+impl RibKind {
+ /// This rib forbids referring to labels defined in upwards ribs.
+ fn is_label_barrier(self) -> bool {
+ match self {
+ RibKind::Normal => false,
+ RibKind::Closure | RibKind::Constant => true,
+ }
+ }
}
#[derive(Debug, Default)]
struct BindingList {
map: FxHashMap<Name, BindingId>,
+ is_used: FxHashMap<BindingId, bool>,
+ reject_new: bool,
}
impl BindingList {
@@ -124,7 +152,27 @@ impl BindingList {
name: Name,
mode: BindingAnnotation,
) -> BindingId {
- *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode))
+ let id = *self.map.entry(name).or_insert_with_key(|n| ec.alloc_binding(n.clone(), mode));
+ if ec.body.bindings[id].mode != mode {
+ ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently);
+ }
+ self.check_is_used(ec, id);
+ id
+ }
+
+ fn check_is_used(&mut self, ec: &mut ExprCollector<'_>, id: BindingId) {
+ match self.is_used.get(&id) {
+ None => {
+ if self.reject_new {
+ ec.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll);
+ }
+ }
+ Some(true) => {
+ ec.body.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce);
+ }
+ Some(false) => {}
+ }
+ self.is_used.insert(id, true);
}
}
@@ -133,13 +181,14 @@ impl ExprCollector<'_> {
mut self,
param_list: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
+ is_async_fn: bool,
) -> (Body, BodySourceMap) {
if let Some((param_list, mut attr_enabled)) = param_list {
if let Some(self_param) =
param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
{
let ptr = AstPtr::new(&self_param);
- let binding_id = self.alloc_binding(
+ let binding_id: la_arena::Idx<Binding> = self.alloc_binding(
name![self],
BindingAnnotation::new(
self_param.mut_token().is_some() && self_param.amp_token().is_none(),
@@ -152,72 +201,35 @@ impl ExprCollector<'_> {
self.body.params.push(param_pat);
}
- for pat in param_list
- .params()
- .zip(attr_enabled)
- .filter_map(|(param, enabled)| param.pat().filter(|_| enabled))
+ for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled)
{
- let param_pat = self.collect_pat(pat);
+ let param_pat = self.collect_pat_top(param.pat());
self.body.params.push(param_pat);
}
};
+ self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| {
+ if is_async_fn {
+ match body {
+ Some(e) => {
+ let expr = this.collect_expr(e);
+ this.alloc_expr_desugared(Expr::Async {
+ id: None,
+ statements: Box::new([]),
+ tail: Some(expr),
+ })
+ }
+ None => this.missing_expr(),
+ }
+ } else {
+ this.collect_expr_opt(body)
+ }
+ });
- self.body.body_expr = self.collect_expr_opt(body);
(self.body, self.source_map)
}
fn ctx(&self) -> LowerCtx<'_> {
- LowerCtx::new(self.db, self.expander.current_file_id)
- }
-
- fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
- let src = self.expander.to_source(ptr);
- let id = self.make_expr(expr, src.clone());
- self.source_map.expr_map.insert(src, id);
- id
- }
- // desugared exprs don't have ptr, that's wrong and should be fixed
- // somehow.
- fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
- self.body.exprs.alloc(expr)
- }
- fn missing_expr(&mut self) -> ExprId {
- self.alloc_expr_desugared(Expr::Missing)
- }
- fn make_expr(&mut self, expr: Expr, src: ExprSource) -> ExprId {
- let id = self.body.exprs.alloc(expr);
- self.source_map.expr_map_back.insert(id, src);
- id
- }
-
- fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
- self.body.bindings.alloc(Binding { name, mode, definitions: SmallVec::new() })
- }
- fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
- let src = self.expander.to_source(ptr);
- let id = self.make_pat(pat, src.clone());
- self.source_map.pat_map.insert(src, id);
- id
- }
- fn missing_pat(&mut self) -> PatId {
- self.body.pats.alloc(Pat::Missing)
- }
- fn make_pat(&mut self, pat: Pat, src: PatSource) -> PatId {
- let id = self.body.pats.alloc(pat);
- self.source_map.pat_map_back.insert(id, src);
- id
- }
-
- fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
- let src = self.expander.to_source(ptr);
- let id = self.make_label(label, src.clone());
- self.source_map.label_map.insert(src, id);
- id
- }
- fn make_label(&mut self, label: Label, src: LabelSource) -> LabelId {
- let id = self.body.labels.alloc(label);
- self.source_map.label_map_back.insert(id, src);
- id
+ self.expander.ctx(self.db)
}
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@@ -229,6 +241,7 @@ impl ExprCollector<'_> {
let syntax_ptr = AstPtr::new(&expr);
self.check_cfg(&expr)?;
+ // FIXME: Move some of these arms out into separate methods for clarity
Some(match expr {
ast::Expr::IfExpr(e) => {
let then_branch = self.collect_block_opt(e.then_branch());
@@ -246,18 +259,12 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
}
ast::Expr::LetExpr(e) => {
- let pat = self.collect_pat_opt(e.pat());
+ let pat = self.collect_pat_top(e.pat());
let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
}
ast::Expr::BlockExpr(e) => match e.modifier() {
- Some(ast::BlockModifier::Try(_)) => {
- self.collect_block_(e, |id, statements, tail| Expr::TryBlock {
- id,
- statements,
- tail,
- })
- }
+ Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e),
Some(ast::BlockModifier::Unsafe(_)) => {
self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
id,
@@ -267,50 +274,77 @@ impl ExprCollector<'_> {
}
Some(ast::BlockModifier::Label(label)) => {
let label = self.collect_label(label);
- self.collect_block_(e, |id, statements, tail| Expr::Block {
- id,
- statements,
- tail,
- label: Some(label),
+ self.with_labeled_rib(label, |this| {
+ this.collect_block_(e, |id, statements, tail| Expr::Block {
+ id,
+ statements,
+ tail,
+ label: Some(label),
+ })
+ })
+ }
+ Some(ast::BlockModifier::Async(_)) => {
+ self.with_label_rib(RibKind::Closure, |this| {
+ this.collect_block_(e, |id, statements, tail| Expr::Async {
+ id,
+ statements,
+ tail,
+ })
+ })
+ }
+ Some(ast::BlockModifier::Const(_)) => {
+ self.with_label_rib(RibKind::Constant, |this| {
+ let (result_expr_id, prev_binding_owner) =
+ this.initialize_binding_owner(syntax_ptr);
+ let inner_expr = this.collect_block(e);
+ let x = this.db.intern_anonymous_const(ConstBlockLoc {
+ parent: this.owner,
+ root: inner_expr,
+ });
+ this.body.exprs[result_expr_id] = Expr::Const(x);
+ this.current_binding_owner = prev_binding_owner;
+ result_expr_id
})
}
- Some(ast::BlockModifier::Async(_)) => self
- .collect_block_(e, |id, statements, tail| Expr::Async { id, statements, tail }),
- Some(ast::BlockModifier::Const(_)) => self
- .collect_block_(e, |id, statements, tail| Expr::Const { id, statements, tail }),
None => self.collect_block(e),
},
ast::Expr::LoopExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
- let body = self.collect_block_opt(e.loop_body());
+ let body = self.collect_labelled_block_opt(label, e.loop_body());
self.alloc_expr(Expr::Loop { body, label }, syntax_ptr)
}
ast::Expr::WhileExpr(e) => {
let label = e.label().map(|label| self.collect_label(label));
- let body = self.collect_block_opt(e.loop_body());
-
+ let body = self.collect_labelled_block_opt(label, e.loop_body());
let condition = self.collect_expr_opt(e.condition());
self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
}
- ast::Expr::ForExpr(e) => {
- let label = e.label().map(|label| self.collect_label(label));
- let iterable = self.collect_expr_opt(e.iterable());
- let pat = self.collect_pat_opt(e.pat());
- let body = self.collect_block_opt(e.loop_body());
- self.alloc_expr(Expr::For { iterable, pat, body, label }, syntax_ptr)
- }
+ ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e),
ast::Expr::CallExpr(e) => {
- let callee = self.collect_expr_opt(e.expr());
- let args = if let Some(arg_list) = e.arg_list() {
- arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
- } else {
- Box::default()
+ let is_rustc_box = {
+ let attrs = e.attrs();
+ attrs.filter_map(|x| x.as_simple_atom()).any(|x| x == "rustc_box")
};
- self.alloc_expr(
- Expr::Call { callee, args, is_assignee_expr: self.is_lowering_assignee_expr },
- syntax_ptr,
- )
+ if is_rustc_box {
+ let expr = self.collect_expr_opt(e.arg_list().and_then(|x| x.args().next()));
+ self.alloc_expr(Expr::Box { expr }, syntax_ptr)
+ } else {
+ let callee = self.collect_expr_opt(e.expr());
+ let args = if let Some(arg_list) = e.arg_list() {
+ arg_list.args().filter_map(|e| self.maybe_collect_expr(e)).collect()
+ } else {
+ Box::default()
+ };
+ self.alloc_expr(
+ Expr::Call {
+ callee,
+ args,
+ is_assignee_expr: self.is_lowering_assignee_expr,
+ },
+ syntax_ptr,
+ )
+ }
}
ast::Expr::MethodCallExpr(e) => {
let receiver = self.collect_expr_opt(e.receiver());
@@ -336,7 +370,7 @@ impl ExprCollector<'_> {
.arms()
.filter_map(|arm| {
self.check_cfg(&arm).map(|()| MatchArm {
- pat: self.collect_pat_opt(arm.pat()),
+ pat: self.collect_pat_top(arm.pat()),
expr: self.collect_expr_opt(arm.expr()),
guard: arm
.guard()
@@ -357,16 +391,20 @@ impl ExprCollector<'_> {
.unwrap_or(Expr::Missing);
self.alloc_expr(path, syntax_ptr)
}
- ast::Expr::ContinueExpr(e) => self.alloc_expr(
- Expr::Continue { label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
- syntax_ptr,
- ),
+ ast::Expr::ContinueExpr(e) => {
+ let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
+ self.source_map.diagnostics.push(e);
+ None
+ });
+ self.alloc_expr(Expr::Continue { label }, syntax_ptr)
+ }
ast::Expr::BreakExpr(e) => {
+ let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
+ self.source_map.diagnostics.push(e);
+ None
+ });
let expr = e.expr().map(|e| self.collect_expr(e));
- self.alloc_expr(
- Expr::Break { expr, label: e.lifetime().map(|l| Name::new_lifetime(&l)) },
- syntax_ptr,
- )
+ self.alloc_expr(Expr::Break { expr, label }, syntax_ptr)
}
ast::Expr::ParenExpr(e) => {
let inner = self.collect_expr_opt(e.expr());
@@ -437,10 +475,7 @@ impl ExprCollector<'_> {
let expr = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::Await { expr }, syntax_ptr)
}
- ast::Expr::TryExpr(e) => {
- let expr = self.collect_expr_opt(e.expr());
- self.alloc_expr(Expr::Try { expr }, syntax_ptr)
- }
+ ast::Expr::TryExpr(e) => self.collect_try_operator(syntax_ptr, e),
ast::Expr::CastExpr(e) => {
let expr = self.collect_expr_opt(e.expr());
let type_ref = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
@@ -470,14 +505,16 @@ impl ExprCollector<'_> {
None => self.alloc_expr(Expr::Missing, syntax_ptr),
}
}
- ast::Expr::ClosureExpr(e) => {
+ ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
+ let (result_expr_id, prev_binding_owner) =
+ this.initialize_binding_owner(syntax_ptr);
let mut args = Vec::new();
let mut arg_types = Vec::new();
if let Some(pl) = e.param_list() {
for param in pl.params() {
- let pat = self.collect_pat_opt(param.pat());
+ let pat = this.collect_pat_top(param.pat());
let type_ref =
- param.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+ param.ty().map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
args.push(pat);
arg_types.push(type_ref);
}
@@ -485,14 +522,14 @@ impl ExprCollector<'_> {
let ret_type = e
.ret_type()
.and_then(|r| r.ty())
- .map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+ .map(|it| Interned::new(TypeRef::from_ast(&this.ctx(), it)));
- let prev_is_lowering_generator = self.is_lowering_generator;
- self.is_lowering_generator = false;
+ let prev_is_lowering_generator = mem::take(&mut this.is_lowering_generator);
+ let prev_try_block_label = this.current_try_block_label.take();
- let body = self.collect_expr_opt(e.body());
+ let body = this.collect_expr_opt(e.body());
- let closure_kind = if self.is_lowering_generator {
+ let closure_kind = if this.is_lowering_generator {
let movability = if e.static_token().is_some() {
Movability::Static
} else {
@@ -504,19 +541,21 @@ impl ExprCollector<'_> {
} else {
ClosureKind::Closure
};
- self.is_lowering_generator = prev_is_lowering_generator;
-
- self.alloc_expr(
- Expr::Closure {
- args: args.into(),
- arg_types: arg_types.into(),
- ret_type,
- body,
- closure_kind,
- },
- syntax_ptr,
- )
- }
+ let capture_by =
+ if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
+ this.is_lowering_generator = prev_is_lowering_generator;
+ this.current_binding_owner = prev_binding_owner;
+ this.current_try_block_label = prev_try_block_label;
+ this.body.exprs[result_expr_id] = Expr::Closure {
+ args: args.into(),
+ arg_types: arg_types.into(),
+ ret_type,
+ body,
+ closure_kind,
+ capture_by,
+ };
+ result_expr_id
+ }),
ast::Expr::BinExpr(e) => {
let op = e.op_kind();
if let Some(ast::BinaryOp::Assignment { op: None }) = op {
@@ -528,9 +567,18 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr)
}
ast::Expr::TupleExpr(e) => {
- let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect();
+ let mut exprs: Vec<_> = e.fields().map(|expr| self.collect_expr(expr)).collect();
+ // if there is a leading comma, the user is most likely to type out a leading expression
+ // so we insert a missing expression at the beginning for IDE features
+ if comma_follows_token(e.l_paren_token()) {
+ exprs.insert(0, self.missing_expr());
+ }
+
self.alloc_expr(
- Expr::Tuple { exprs, is_assignee_expr: self.is_lowering_assignee_expr },
+ Expr::Tuple {
+ exprs: exprs.into_boxed_slice(),
+ is_assignee_expr: self.is_lowering_assignee_expr,
+ },
syntax_ptr,
)
}
@@ -555,7 +603,17 @@ impl ExprCollector<'_> {
}
ArrayExprKind::Repeat { initializer, repeat } => {
let initializer = self.collect_expr_opt(initializer);
- let repeat = self.collect_expr_opt(repeat);
+ let repeat = self.with_label_rib(RibKind::Constant, |this| {
+ if let Some(repeat) = repeat {
+ let syntax_ptr = AstPtr::new(&repeat);
+ this.collect_as_a_binding_owner_bad(
+ |this| this.collect_expr(repeat),
+ syntax_ptr,
+ )
+ } else {
+ this.missing_expr()
+ }
+ });
self.alloc_expr(
Expr::Array(Array::Repeat { initializer, repeat }),
syntax_ptr,
@@ -601,6 +659,240 @@ impl ExprCollector<'_> {
})
}
+ fn initialize_binding_owner(
+ &mut self,
+ syntax_ptr: AstPtr<ast::Expr>,
+ ) -> (ExprId, Option<ExprId>) {
+ let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
+ let prev_binding_owner = self.current_binding_owner.take();
+ self.current_binding_owner = Some(result_expr_id);
+ (result_expr_id, prev_binding_owner)
+ }
+
+ /// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently
+ /// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have
+ /// their own body. Don't add more usage for this function so that we can remove this function after
+ /// separating those bodies.
+ fn collect_as_a_binding_owner_bad(
+ &mut self,
+ job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId,
+ syntax_ptr: AstPtr<ast::Expr>,
+ ) -> ExprId {
+ let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
+ let tmp = job(self);
+ self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing);
+ self.current_binding_owner = prev_owner;
+ id
+ }
+
+ /// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
+ /// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
+ /// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
+ fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
+ let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else {
+ return self.collect_block(e);
+ };
+ let label = self.alloc_label_desugared(Label { name: Name::generate_new_name() });
+ let old_label = self.current_try_block_label.replace(label);
+
+ let (btail, expr_id) = self.with_labeled_rib(label, |this| {
+ let mut btail = None;
+ let block = this.collect_block_(e, |id, statements, tail| {
+ btail = tail;
+ Expr::Block { id, statements, tail, label: Some(label) }
+ });
+ (btail, block)
+ });
+
+ let callee = self.alloc_expr_desugared(Expr::Path(try_from_output));
+ let next_tail = match btail {
+ Some(tail) => self.alloc_expr_desugared(Expr::Call {
+ callee,
+ args: Box::new([tail]),
+ is_assignee_expr: false,
+ }),
+ None => {
+ let unit = self.alloc_expr_desugared(Expr::Tuple {
+ exprs: Box::new([]),
+ is_assignee_expr: false,
+ });
+ self.alloc_expr_desugared(Expr::Call {
+ callee,
+ args: Box::new([unit]),
+ is_assignee_expr: false,
+ })
+ }
+ };
+ let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else {
+ unreachable!("block was lowered to non-block");
+ };
+ *tail = Some(next_tail);
+ self.current_try_block_label = old_label;
+ expr_id
+ }
+
+ /// Desugar `ast::ForExpr` from: `[opt_ident]: for <pat> in <head> <body>` into:
+ /// ```ignore (pseudo-rust)
+ /// match IntoIterator::into_iter(<head>) {
+ /// mut iter => {
+ /// [opt_ident]: loop {
+ /// match Iterator::next(&mut iter) {
+ /// None => break,
+ /// Some(<pat>) => <body>,
+ /// };
+ /// }
+ /// }
+ /// }
+ /// ```
+ fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId {
+ let Some((into_iter_fn, iter_next_fn, option_some, option_none)) = (|| {
+ Some((
+ LangItem::IntoIterIntoIter.path(self.db, self.krate)?,
+ LangItem::IteratorNext.path(self.db, self.krate)?,
+ LangItem::OptionSome.path(self.db, self.krate)?,
+ LangItem::OptionNone.path(self.db, self.krate)?,
+ ))
+ })() else {
+ // Some of the needed lang items are missing, so we can't desugar
+ return self.alloc_expr(Expr::Missing, syntax_ptr);
+ };
+ let head = self.collect_expr_opt(e.iterable());
+ let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr.clone());
+ let iterator = self.alloc_expr(
+ Expr::Call {
+ callee: into_iter_fn_expr,
+ args: Box::new([head]),
+ is_assignee_expr: false,
+ },
+ syntax_ptr.clone(),
+ );
+ let none_arm = MatchArm {
+ pat: self.alloc_pat_desugared(Pat::Path(Box::new(option_none))),
+ guard: None,
+ expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone()),
+ };
+ let some_pat = Pat::TupleStruct {
+ path: Some(Box::new(option_some)),
+ args: Box::new([self.collect_pat_top(e.pat())]),
+ ellipsis: None,
+ };
+ let label = e.label().map(|label| self.collect_label(label));
+ let some_arm = MatchArm {
+ pat: self.alloc_pat_desugared(some_pat),
+ guard: None,
+ expr: self.with_opt_labeled_rib(label, |this| {
+ this.collect_expr_opt(e.loop_body().map(|x| x.into()))
+ }),
+ };
+ let iter_name = Name::generate_new_name();
+ let iter_expr =
+ self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr.clone());
+ let iter_expr_mut = self.alloc_expr(
+ Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
+ syntax_ptr.clone(),
+ );
+ let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr.clone());
+ let iter_next_expr = self.alloc_expr(
+ Expr::Call {
+ callee: iter_next_fn_expr,
+ args: Box::new([iter_expr_mut]),
+ is_assignee_expr: false,
+ },
+ syntax_ptr.clone(),
+ );
+ let loop_inner = self.alloc_expr(
+ Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) },
+ syntax_ptr.clone(),
+ );
+ let loop_outer =
+ self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr.clone());
+ let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable);
+ let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None });
+ self.add_definition_to_binding(iter_binding, iter_pat);
+ self.alloc_expr(
+ Expr::Match {
+ expr: iterator,
+ arms: Box::new([MatchArm { pat: iter_pat, guard: None, expr: loop_outer }]),
+ },
+ syntax_ptr.clone(),
+ )
+ }
+
+ /// Desugar `ast::TryExpr` from: `<expr>?` into:
+ /// ```ignore (pseudo-rust)
+ /// match Try::branch(<expr>) {
+ /// ControlFlow::Continue(val) => val,
+ /// ControlFlow::Break(residual) =>
+ /// // If there is an enclosing `try {...}`:
+ /// break 'catch_target Try::from_residual(residual),
+ /// // Otherwise:
+ /// return Try::from_residual(residual),
+ /// }
+ /// ```
+ fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId {
+ let Some((try_branch, cf_continue, cf_break, try_from_residual)) = (|| {
+ Some((
+ LangItem::TryTraitBranch.path(self.db, self.krate)?,
+ LangItem::ControlFlowContinue.path(self.db, self.krate)?,
+ LangItem::ControlFlowBreak.path(self.db, self.krate)?,
+ LangItem::TryTraitFromResidual.path(self.db, self.krate)?,
+ ))
+ })() else {
+ // Some of the needed lang items are missing, so we can't desugar
+ return self.alloc_expr(Expr::Missing, syntax_ptr);
+ };
+ let operand = self.collect_expr_opt(e.expr());
+ let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr.clone());
+ let expr = self.alloc_expr(
+ Expr::Call { callee: try_branch, args: Box::new([operand]), is_assignee_expr: false },
+ syntax_ptr.clone(),
+ );
+ let continue_name = Name::generate_new_name();
+ let continue_binding =
+ self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated);
+ let continue_bpat =
+ self.alloc_pat_desugared(Pat::Bind { id: continue_binding, subpat: None });
+ self.add_definition_to_binding(continue_binding, continue_bpat);
+ let continue_arm = MatchArm {
+ pat: self.alloc_pat_desugared(Pat::TupleStruct {
+ path: Some(Box::new(cf_continue)),
+ args: Box::new([continue_bpat]),
+ ellipsis: None,
+ }),
+ guard: None,
+ expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr.clone()),
+ };
+ let break_name = Name::generate_new_name();
+ let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated);
+ let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
+ self.add_definition_to_binding(break_binding, break_bpat);
+ let break_arm = MatchArm {
+ pat: self.alloc_pat_desugared(Pat::TupleStruct {
+ path: Some(Box::new(cf_break)),
+ args: Box::new([break_bpat]),
+ ellipsis: None,
+ }),
+ guard: None,
+ expr: {
+ let x = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr.clone());
+ let callee = self.alloc_expr(Expr::Path(try_from_residual), syntax_ptr.clone());
+ let result = self.alloc_expr(
+ Expr::Call { callee, args: Box::new([x]), is_assignee_expr: false },
+ syntax_ptr.clone(),
+ );
+ self.alloc_expr(
+ match self.current_try_block_label {
+ Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
+ None => Expr::Return { expr: Some(result) },
+ },
+ syntax_ptr.clone(),
+ )
+ },
+ };
+ let arms = Box::new([continue_arm, break_arm]);
+ self.alloc_expr(Expr::Match { expr, arms }, syntax_ptr)
+ }
+
fn collect_macro_call<F, T, U>(
&mut self,
mcall: ast::MacroCall,
@@ -616,7 +908,19 @@ impl ExprCollector<'_> {
let outer_file = self.expander.current_file_id;
let macro_call_ptr = self.expander.to_source(AstPtr::new(&mcall));
- let res = self.expander.enter_expand(self.db, mcall);
+ let module = self.expander.module.local_id;
+ let res = self.expander.enter_expand(self.db, mcall, |path| {
+ self.def_map
+ .resolve_path(
+ self.db,
+ module,
+ &path,
+ crate::item_scope::BuiltinShadowMode::Other,
+ Some(MacroSubNs::Bang),
+ )
+ .0
+ .take_macros()
+ });
let res = match res {
Ok(res) => res,
@@ -639,7 +943,7 @@ impl ExprCollector<'_> {
krate: *krate,
});
}
- Some(ExpandError::RecursionOverflowPosioned) => {
+ Some(ExpandError::RecursionOverflowPoisoned) => {
// Recursion limit has been reached in the macro expansion tree, but not in
// this very macro call. Don't add diagnostics to avoid duplication.
}
@@ -663,7 +967,11 @@ impl ExprCollector<'_> {
self.db.ast_id_map(self.expander.current_file_id),
);
- let id = collector(self, Some(expansion));
+ if record_diagnostics {
+ // FIXME: Report parse errors here
+ }
+
+ let id = collector(self, Some(expansion.tree()));
self.ast_id_map = prev_ast_id_map;
self.expander.exit(self.db, mark);
id
@@ -720,7 +1028,7 @@ impl ExprCollector<'_> {
if self.check_cfg(&stmt).is_none() {
return;
}
- let pat = self.collect_pat_opt(stmt.pat());
+ let pat = self.collect_pat_top(stmt.pat());
let type_ref =
stmt.ty().map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
@@ -763,22 +1071,36 @@ impl ExprCollector<'_> {
fn collect_block_(
&mut self,
block: ast::BlockExpr,
- mk_block: impl FnOnce(BlockId, Box<[Statement]>, Option<ExprId>) -> Expr,
+ mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
) -> ExprId {
- let file_local_id = self.ast_id_map.ast_id(&block);
- let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
- let block_loc =
- BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) };
- let block_id = self.db.intern_block(block_loc);
-
- let (module, def_map) = match self.db.block_def_map(block_id) {
- Some(def_map) => {
- self.body.block_scopes.push(block_id);
- (def_map.root(), def_map)
- }
- None => (self.expander.module, self.expander.def_map.clone()),
+ let block_has_items = {
+ let statement_has_item = block.statements().any(|stmt| match stmt {
+ ast::Stmt::Item(_) => true,
+ // Macro calls can be both items and expressions. The syntax library always treats
+ // them as expressions here, so we undo that.
+ ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))),
+ _ => false,
+ });
+ statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
};
- let prev_def_map = mem::replace(&mut self.expander.def_map, def_map);
+
+ let block_id = if block_has_items {
+ let file_local_id = self.ast_id_map.ast_id(&block);
+ let ast_id = AstId::new(self.expander.current_file_id, file_local_id);
+ Some(self.db.intern_block(BlockLoc { ast_id, module: self.expander.module }))
+ } else {
+ None
+ };
+
+ let (module, def_map) =
+ match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) {
+ Some((def_map, block_id)) => {
+ self.body.block_scopes.push(block_id);
+ (def_map.module_id(DefMap::ROOT), def_map)
+ }
+ None => (self.expander.module, self.def_map.clone()),
+ };
+ let prev_def_map = mem::replace(&mut self.def_map, def_map);
let prev_local_module = mem::replace(&mut self.expander.module, module);
let mut statements = Vec::new();
@@ -800,7 +1122,7 @@ impl ExprCollector<'_> {
let expr_id = self
.alloc_expr(mk_block(block_id, statements.into_boxed_slice(), tail), syntax_node_ptr);
- self.expander.def_map = prev_def_map;
+ self.def_map = prev_def_map;
self.expander.module = prev_local_module;
expr_id
}
@@ -812,43 +1134,46 @@ impl ExprCollector<'_> {
}
}
- fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
- let label = Label {
- name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
- };
- self.alloc_label(label, AstPtr::new(&ast_label))
+ fn collect_labelled_block_opt(
+ &mut self,
+ label: Option<LabelId>,
+ expr: Option<ast::BlockExpr>,
+ ) -> ExprId {
+ match label {
+ Some(label) => self.with_labeled_rib(label, |this| this.collect_block_opt(expr)),
+ None => self.collect_block_opt(expr),
+ }
}
- fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
- self.collect_pat_(pat, &mut BindingList::default())
- }
+ // region: patterns
- fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
+ fn collect_pat_top(&mut self, pat: Option<ast::Pat>) -> PatId {
match pat {
- Some(pat) => self.collect_pat(pat),
+ Some(pat) => self.collect_pat(pat, &mut BindingList::default()),
None => self.missing_pat(),
}
}
- fn collect_pat_(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
+ fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatId {
let pattern = match &pat {
ast::Pat::IdentPat(bp) => {
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
let annotation =
BindingAnnotation::new(bp.mut_token().is_some(), bp.ref_token().is_some());
- let subpat = bp.pat().map(|subpat| self.collect_pat_(subpat, binding_list));
+ let subpat = bp.pat().map(|subpat| self.collect_pat(subpat, binding_list));
let is_simple_ident_pat =
annotation == BindingAnnotation::Unannotated && subpat.is_none();
let (binding, pattern) = if is_simple_ident_pat {
// This could also be a single-segment path pattern. To
// decide that, we need to try resolving the name.
- let (resolved, _) = self.expander.def_map.resolve_path(
+ let (resolved, _) = self.def_map.resolve_path(
self.db,
- self.expander.module,
+ self.expander.module.local_id,
&name.clone().into(),
BuiltinShadowMode::Other,
+ None,
);
match resolved.take_values() {
Some(ModuleDefId::ConstId(_)) => (None, Pat::Path(name.into())),
@@ -887,11 +1212,15 @@ impl ExprCollector<'_> {
ast::Pat::TupleStructPat(p) => {
let path =
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
- let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
+ let (args, ellipsis) = self.collect_tuple_pat(
+ p.fields(),
+ comma_follows_token(p.l_paren_token()),
+ binding_list,
+ );
Pat::TupleStruct { path, args, ellipsis }
}
ast::Pat::RefPat(p) => {
- let pat = self.collect_pat_opt_(p.pat(), binding_list);
+ let pat = self.collect_pat_opt(p.pat(), binding_list);
let mutability = Mutability::from_mutable(p.mut_token().is_some());
Pat::Ref { pat, mutability }
}
@@ -900,13 +1229,42 @@ impl ExprCollector<'_> {
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
path.map(Pat::Path).unwrap_or(Pat::Missing)
}
- ast::Pat::OrPat(p) => {
- let pats = p.pats().map(|p| self.collect_pat_(p, binding_list)).collect();
- Pat::Or(pats)
+ ast::Pat::OrPat(p) => 'b: {
+ let prev_is_used = mem::take(&mut binding_list.is_used);
+ let prev_reject_new = mem::take(&mut binding_list.reject_new);
+ let mut pats = Vec::with_capacity(p.pats().count());
+ let mut it = p.pats();
+ let Some(first) = it.next() else {
+ break 'b Pat::Or(Box::new([]));
+ };
+ pats.push(self.collect_pat(first, binding_list));
+ binding_list.reject_new = true;
+ for rest in it {
+ for (_, x) in binding_list.is_used.iter_mut() {
+ *x = false;
+ }
+ pats.push(self.collect_pat(rest, binding_list));
+ for (&id, &x) in binding_list.is_used.iter() {
+ if !x {
+ self.body.bindings[id].problems =
+ Some(BindingProblems::NotBoundAcrossAll);
+ }
+ }
+ }
+ binding_list.reject_new = prev_reject_new;
+ let current_is_used = mem::replace(&mut binding_list.is_used, prev_is_used);
+ for (id, _) in current_is_used.into_iter() {
+ binding_list.check_is_used(self, id);
+ }
+ Pat::Or(pats.into())
}
- ast::Pat::ParenPat(p) => return self.collect_pat_opt_(p.pat(), binding_list),
+ ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat(), binding_list),
ast::Pat::TuplePat(p) => {
- let (args, ellipsis) = self.collect_tuple_pat(p.fields(), binding_list);
+ let (args, ellipsis) = self.collect_tuple_pat(
+ p.fields(),
+ comma_follows_token(p.l_paren_token()),
+ binding_list,
+ );
Pat::Tuple { args, ellipsis }
}
ast::Pat::WildcardPat(_) => Pat::Wild,
@@ -919,7 +1277,7 @@ impl ExprCollector<'_> {
.fields()
.filter_map(|f| {
let ast_pat = f.pat()?;
- let pat = self.collect_pat_(ast_pat, binding_list);
+ let pat = self.collect_pat(ast_pat, binding_list);
let name = f.field_name()?.as_name();
Some(RecordFieldPat { name, pat })
})
@@ -938,26 +1296,18 @@ impl ExprCollector<'_> {
// FIXME properly handle `RestPat`
Pat::Slice {
- prefix: prefix
- .into_iter()
- .map(|p| self.collect_pat_(p, binding_list))
- .collect(),
- slice: slice.map(|p| self.collect_pat_(p, binding_list)),
- suffix: suffix
- .into_iter()
- .map(|p| self.collect_pat_(p, binding_list))
- .collect(),
+ prefix: prefix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
+ slice: slice.map(|p| self.collect_pat(p, binding_list)),
+ suffix: suffix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
}
}
- ast::Pat::LiteralPat(lit) => {
- if let Some(ast_lit) = lit.literal() {
- let expr = Expr::Literal(ast_lit.kind().into());
- let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
- let expr_id = self.alloc_expr(expr, expr_ptr);
- Pat::Lit(expr_id)
- } else {
- Pat::Missing
- }
+ #[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5676
+ ast::Pat::LiteralPat(lit) => 'b: {
+ let Some((hir_lit, ast_lit)) = pat_literal_to_hir(lit) else { break 'b Pat::Missing };
+ let expr = Expr::Literal(hir_lit);
+ let expr_ptr = AstPtr::new(&ast::Expr::Literal(ast_lit));
+ let expr_id = self.alloc_expr(expr, expr_ptr);
+ Pat::Lit(expr_id)
}
ast::Pat::RestPat(_) => {
// `RestPat` requires special handling and should not be mapped
@@ -969,12 +1319,18 @@ impl ExprCollector<'_> {
Pat::Missing
}
ast::Pat::BoxPat(boxpat) => {
- let inner = self.collect_pat_opt_(boxpat.pat(), binding_list);
+ let inner = self.collect_pat_opt(boxpat.pat(), binding_list);
Pat::Box { inner }
}
ast::Pat::ConstBlockPat(const_block_pat) => {
- if let Some(expr) = const_block_pat.block_expr() {
- let expr_id = self.collect_block(expr);
+ if let Some(block) = const_block_pat.block_expr() {
+ let expr_id = self.with_label_rib(RibKind::Constant, |this| {
+ let syntax_ptr = AstPtr::new(&block.clone().into());
+ this.collect_as_a_binding_owner_bad(
+ |this| this.collect_block(block),
+ syntax_ptr,
+ )
+ });
Pat::ConstBlock(expr_id)
} else {
Pat::Missing
@@ -986,23 +1342,45 @@ impl ExprCollector<'_> {
let src = self.expander.to_source(Either::Left(AstPtr::new(&pat)));
let pat =
self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
- this.collect_pat_opt_(expanded_pat, binding_list)
+ this.collect_pat_opt(expanded_pat, binding_list)
});
self.source_map.pat_map.insert(src, pat);
return pat;
}
None => Pat::Missing,
},
- // FIXME: implement
- ast::Pat::RangePat(_) => Pat::Missing,
+ // FIXME: implement in a way that also builds source map and calculates assoc resolutions in type inference.
+ ast::Pat::RangePat(p) => {
+ let mut range_part_lower = |p: Option<ast::Pat>| {
+ p.and_then(|x| match &x {
+ ast::Pat::LiteralPat(x) => {
+ Some(Box::new(LiteralOrConst::Literal(pat_literal_to_hir(x)?.0)))
+ }
+ ast::Pat::IdentPat(p) => {
+ let name =
+ p.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
+ Some(Box::new(LiteralOrConst::Const(name.into())))
+ }
+ ast::Pat::PathPat(p) => p
+ .path()
+ .and_then(|path| self.expander.parse_path(self.db, path))
+ .map(LiteralOrConst::Const)
+ .map(Box::new),
+ _ => None,
+ })
+ };
+ let start = range_part_lower(p.start());
+ let end = range_part_lower(p.end());
+ Pat::Range { start, end }
+ }
};
let ptr = AstPtr::new(&pat);
self.alloc_pat(pattern, Either::Left(ptr))
}
- fn collect_pat_opt_(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
+ fn collect_pat_opt(&mut self, pat: Option<ast::Pat>, binding_list: &mut BindingList) -> PatId {
match pat {
- Some(pat) => self.collect_pat_(pat, binding_list),
+ Some(pat) => self.collect_pat(pat, binding_list),
None => self.missing_pat(),
}
}
@@ -1010,20 +1388,28 @@ impl ExprCollector<'_> {
fn collect_tuple_pat(
&mut self,
args: AstChildren<ast::Pat>,
+ has_leading_comma: bool,
binding_list: &mut BindingList,
) -> (Box<[PatId]>, Option<usize>) {
// Find the location of the `..`, if there is one. Note that we do not
// consider the possibility of there being multiple `..` here.
let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::RestPat(_)));
// We want to skip the `..` pattern here, since we account for it above.
- let args = args
+ let mut args: Vec<_> = args
.filter(|p| !matches!(p, ast::Pat::RestPat(_)))
- .map(|p| self.collect_pat_(p, binding_list))
+ .map(|p| self.collect_pat(p, binding_list))
.collect();
+ // if there is a leading comma, the user is most likely to type out a leading pattern
+ // so we insert a missing pattern at the beginning for IDE features
+ if has_leading_comma {
+ args.insert(0, self.missing_pat());
+ }
- (args, ellipsis)
+ (args.into_boxed_slice(), ellipsis)
}
+ // endregion: patterns
+
/// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
/// not.
fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> Option<()> {
@@ -1051,42 +1437,150 @@ impl ExprCollector<'_> {
fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
self.body.bindings[binding_id].definitions.push(pat_id);
}
-}
-impl From<ast::LiteralKind> for Literal {
- fn from(ast_lit_kind: ast::LiteralKind) -> Self {
- match ast_lit_kind {
- // FIXME: these should have actual values filled in, but unsure on perf impact
- LiteralKind::IntNumber(lit) => {
- if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
- Literal::Float(
- FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
- builtin,
- )
- } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinInt::from_suffix) {
- Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
- } else {
- let builtin = lit.suffix().and_then(BuiltinUint::from_suffix);
- Literal::Uint(lit.value().unwrap_or(0), builtin)
+ // region: labels
+
+ fn collect_label(&mut self, ast_label: ast::Label) -> LabelId {
+ let label = Label {
+ name: ast_label.lifetime().as_ref().map_or_else(Name::missing, Name::new_lifetime),
+ };
+ self.alloc_label(label, AstPtr::new(&ast_label))
+ }
+
+ fn resolve_label(
+ &self,
+ lifetime: Option<ast::Lifetime>,
+ ) -> Result<Option<LabelId>, BodyDiagnostic> {
+ let Some(lifetime) = lifetime else {
+ return Ok(None)
+ };
+ let name = Name::new_lifetime(&lifetime);
+
+ for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
+ if let Some((label_name, id)) = &rib.label {
+ if *label_name == name {
+ return if self.is_label_valid_from_rib(rib_idx) {
+ Ok(Some(*id))
+ } else {
+ Err(BodyDiagnostic::UnreachableLabel {
+ name,
+ node: InFile::new(
+ self.expander.current_file_id,
+ AstPtr::new(&lifetime),
+ ),
+ })
+ };
}
}
- LiteralKind::FloatNumber(lit) => {
- let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
- Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
- }
- LiteralKind::ByteString(bs) => {
- let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
- Literal::ByteString(text)
- }
- LiteralKind::String(s) => {
- let text = s.value().map(Box::from).unwrap_or_else(Default::default);
- Literal::String(text)
- }
- LiteralKind::Byte(b) => {
- Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
- }
- LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
- LiteralKind::Bool(val) => Literal::Bool(val),
}
+
+ Err(BodyDiagnostic::UndeclaredLabel {
+ name,
+ node: InFile::new(self.expander.current_file_id, AstPtr::new(&lifetime)),
+ })
+ }
+
+ fn is_label_valid_from_rib(&self, rib_index: usize) -> bool {
+ !self.label_ribs[rib_index + 1..].iter().any(|rib| rib.kind.is_label_barrier())
+ }
+
+ fn with_label_rib<T>(&mut self, kind: RibKind, f: impl FnOnce(&mut Self) -> T) -> T {
+ self.label_ribs.push(LabelRib::new(kind));
+ let res = f(self);
+ self.label_ribs.pop();
+ res
+ }
+
+ fn with_labeled_rib<T>(&mut self, label: LabelId, f: impl FnOnce(&mut Self) -> T) -> T {
+ self.label_ribs.push(LabelRib::new_normal((self.body[label].name.clone(), label)));
+ let res = f(self);
+ self.label_ribs.pop();
+ res
+ }
+
+ fn with_opt_labeled_rib<T>(
+ &mut self,
+ label: Option<LabelId>,
+ f: impl FnOnce(&mut Self) -> T,
+ ) -> T {
+ match label {
+ None => f(self),
+ Some(label) => self.with_labeled_rib(label, f),
+ }
+ }
+ // endregion: labels
+}
+
+fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> {
+ let ast_lit = lit.literal()?;
+ let mut hir_lit: Literal = ast_lit.kind().into();
+ if lit.minus_token().is_some() {
+ let Some(h) = hir_lit.negate() else {
+ return None;
+ };
+ hir_lit = h;
+ }
+ Some((hir_lit, ast_lit))
+}
+
+impl ExprCollector<'_> {
+ fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
+ let src = self.expander.to_source(ptr);
+ let id = self.body.exprs.alloc(expr);
+ self.source_map.expr_map_back.insert(id, src.clone());
+ self.source_map.expr_map.insert(src, id);
+ id
+ }
+ // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
+ fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
+ self.body.exprs.alloc(expr)
+ }
+ fn missing_expr(&mut self) -> ExprId {
+ self.alloc_expr_desugared(Expr::Missing)
+ }
+
+ fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId {
+ let binding = self.body.bindings.alloc(Binding {
+ name,
+ mode,
+ definitions: SmallVec::new(),
+ problems: None,
+ });
+ if let Some(owner) = self.current_binding_owner {
+ self.body.binding_owners.insert(binding, owner);
+ }
+ binding
+ }
+
+ fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
+ let src = self.expander.to_source(ptr);
+ let id = self.body.pats.alloc(pat);
+ self.source_map.pat_map_back.insert(id, src.clone());
+ self.source_map.pat_map.insert(src, id);
+ id
+ }
+ // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
+ fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId {
+ self.body.pats.alloc(pat)
+ }
+ fn missing_pat(&mut self) -> PatId {
+ self.body.pats.alloc(Pat::Missing)
+ }
+
+ fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
+ let src = self.expander.to_source(ptr);
+ let id = self.body.labels.alloc(label);
+ self.source_map.label_map_back.insert(id, src.clone());
+ self.source_map.label_map.insert(src, id);
+ id
+ }
+ // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
+ fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
+ self.body.labels.alloc(label)
}
}
+
+fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
+ (|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
+ .map_or(false, |it| it.kind() == syntax::T![,])
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
index 5a9b825a2..cd6df0e63 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
@@ -2,10 +2,14 @@
use std::fmt::{self, Write};
+use hir_expand::db::ExpandDatabase;
use syntax::ast::HasName;
use crate::{
- expr::{Array, BindingAnnotation, BindingId, ClosureKind, Literal, Movability, Statement},
+ hir::{
+ Array, BindingAnnotation, BindingId, CaptureBy, ClosureKind, Literal, LiteralOrConst,
+ Movability, Statement,
+ },
pretty::{print_generic_args, print_path, print_type_ref},
type_ref::TypeRef,
};
@@ -13,47 +17,71 @@ use crate::{
use super::*;
pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String {
- let needs_semi;
let header = match owner {
DefWithBodyId::FunctionId(it) => {
- needs_semi = false;
let item_tree_id = it.lookup(db).id;
- format!("fn {}(…) ", item_tree_id.item_tree(db)[item_tree_id.value].name)
+ format!(
+ "fn {}",
+ item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast())
+ )
}
DefWithBodyId::StaticId(it) => {
- needs_semi = true;
let item_tree_id = it.lookup(db).id;
- format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name)
+ format!(
+ "static {} = ",
+ item_tree_id.item_tree(db)[item_tree_id.value].name.display(db.upcast())
+ )
}
DefWithBodyId::ConstId(it) => {
- needs_semi = true;
let item_tree_id = it.lookup(db).id;
let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name {
- Some(name) => name.to_string(),
+ Some(name) => name.display(db.upcast()).to_string(),
None => "_".to_string(),
};
format!("const {name} = ")
}
+ DefWithBodyId::InTypeConstId(_) => format!("In type const = "),
DefWithBodyId::VariantId(it) => {
- needs_semi = false;
let src = it.parent.child_source(db);
let variant = &src.value[it.local_id];
- let name = match &variant.name() {
+ match &variant.name() {
Some(name) => name.to_string(),
None => "_".to_string(),
- };
- format!("{name}")
+ }
}
};
- let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };
+ let mut p =
+ Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
+ if let DefWithBodyId::FunctionId(it) = owner {
+ p.buf.push('(');
+ body.params.iter().zip(&db.function_data(it).params).for_each(|(&param, ty)| {
+ p.print_pat(param);
+ p.buf.push(':');
+ p.print_type_ref(ty);
+ });
+ p.buf.push(')');
+ p.buf.push(' ');
+ }
p.print_expr(body.body_expr);
- if needs_semi {
+ if matches!(owner, DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_)) {
p.buf.push(';');
}
p.buf
}
+pub(super) fn print_expr_hir(
+ db: &dyn DefDatabase,
+ body: &Body,
+ _owner: DefWithBodyId,
+ expr: ExprId,
+) -> String {
+ let mut p =
+ Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
+ p.print_expr(expr);
+ p.buf
+}
+
macro_rules! w {
($dst:expr, $($arg:tt)*) => {
{ let _ = write!($dst, $($arg)*); }
@@ -70,6 +98,7 @@ macro_rules! wln {
}
struct Printer<'a> {
+ db: &'a dyn ExpandDatabase,
body: &'a Body,
buf: String,
indent_level: usize,
@@ -144,29 +173,19 @@ impl<'a> Printer<'a> {
}
Expr::Loop { body, label } => {
if let Some(lbl) = label {
- w!(self, "{}: ", self.body[*lbl].name);
+ w!(self, "{}: ", self.body[*lbl].name.display(self.db));
}
w!(self, "loop ");
self.print_expr(*body);
}
Expr::While { condition, body, label } => {
if let Some(lbl) = label {
- w!(self, "{}: ", self.body[*lbl].name);
+ w!(self, "{}: ", self.body[*lbl].name.display(self.db));
}
w!(self, "while ");
self.print_expr(*condition);
self.print_expr(*body);
}
- Expr::For { iterable, pat, body, label } => {
- if let Some(lbl) = label {
- w!(self, "{}: ", self.body[*lbl].name);
- }
- w!(self, "for ");
- self.print_pat(*pat);
- w!(self, " in ");
- self.print_expr(*iterable);
- self.print_expr(*body);
- }
Expr::Call { callee, args, is_assignee_expr: _ } => {
self.print_expr(*callee);
w!(self, "(");
@@ -182,10 +201,10 @@ impl<'a> Printer<'a> {
}
Expr::MethodCall { receiver, method_name, args, generic_args } => {
self.print_expr(*receiver);
- w!(self, ".{}", method_name);
+ w!(self, ".{}", method_name.display(self.db));
if let Some(args) = generic_args {
w!(self, "::<");
- print_generic_args(args, self).unwrap();
+ print_generic_args(self.db, args, self).unwrap();
w!(self, ">");
}
w!(self, "(");
@@ -219,14 +238,14 @@ impl<'a> Printer<'a> {
}
Expr::Continue { label } => {
w!(self, "continue");
- if let Some(label) = label {
- w!(self, " {}", label);
+ if let Some(lbl) = label {
+ w!(self, " {}", self.body[*lbl].name.display(self.db));
}
}
Expr::Break { expr, label } => {
w!(self, "break");
- if let Some(label) = label {
- w!(self, " {}", label);
+ if let Some(lbl) = label {
+ w!(self, " {}", self.body[*lbl].name.display(self.db));
}
if let Some(expr) = expr {
self.whitespace();
@@ -265,7 +284,7 @@ impl<'a> Printer<'a> {
w!(self, "{{");
self.indented(|p| {
for field in &**fields {
- w!(p, "{}: ", field.name);
+ w!(p, "{}: ", field.name.display(self.db));
p.print_expr(field.expr);
wln!(p, ",");
}
@@ -282,16 +301,12 @@ impl<'a> Printer<'a> {
}
Expr::Field { expr, name } => {
self.print_expr(*expr);
- w!(self, ".{}", name);
+ w!(self, ".{}", name.display(self.db));
}
Expr::Await { expr } => {
self.print_expr(*expr);
w!(self, ".await");
}
- Expr::Try { expr } => {
- self.print_expr(*expr);
- w!(self, "?");
- }
Expr::Cast { expr, type_ref } => {
self.print_expr(*expr);
w!(self, " as ");
@@ -359,7 +374,7 @@ impl<'a> Printer<'a> {
self.print_expr(*index);
w!(self, "]");
}
- Expr::Closure { args, arg_types, ret_type, body, closure_kind } => {
+ Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => {
match closure_kind {
ClosureKind::Generator(Movability::Static) => {
w!(self, "static ");
@@ -369,6 +384,12 @@ impl<'a> Printer<'a> {
}
_ => (),
}
+ match capture_by {
+ CaptureBy::Value => {
+ w!(self, "move ");
+ }
+ CaptureBy::Ref => (),
+ }
w!(self, "|");
for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
if i != 0 {
@@ -418,20 +439,17 @@ impl<'a> Printer<'a> {
}
Expr::Literal(lit) => self.print_literal(lit),
Expr::Block { id: _, statements, tail, label } => {
- let label = label.map(|lbl| format!("{}: ", self.body[lbl].name));
+ let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db)));
self.print_block(label.as_deref(), statements, tail);
}
Expr::Unsafe { id: _, statements, tail } => {
self.print_block(Some("unsafe "), statements, tail);
}
- Expr::TryBlock { id: _, statements, tail } => {
- self.print_block(Some("try "), statements, tail);
- }
Expr::Async { id: _, statements, tail } => {
self.print_block(Some("async "), statements, tail);
}
- Expr::Const { id: _, statements, tail } => {
- self.print_block(Some("const "), statements, tail);
+ Expr::Const(id) => {
+ w!(self, "const {{ /* {id:?} */ }}");
}
}
}
@@ -439,7 +457,7 @@ impl<'a> Printer<'a> {
fn print_block(
&mut self,
label: Option<&str>,
- statements: &Box<[Statement]>,
+ statements: &[Statement],
tail: &Option<la_arena::Idx<Expr>>,
) {
self.whitespace();
@@ -449,7 +467,7 @@ impl<'a> Printer<'a> {
w!(self, "{{");
if !statements.is_empty() || tail.is_some() {
self.indented(|p| {
- for stmt in &**statements {
+ for stmt in statements {
p.print_stmt(stmt);
}
if let Some(tail) = tail {
@@ -497,7 +515,7 @@ impl<'a> Printer<'a> {
w!(self, " {{");
self.indented(|p| {
for arg in args.iter() {
- w!(p, "{}: ", arg.name);
+ w!(p, "{}: ", arg.name.display(self.db));
p.print_pat(arg.pat);
wln!(p, ",");
}
@@ -508,9 +526,13 @@ impl<'a> Printer<'a> {
w!(self, "}}");
}
Pat::Range { start, end } => {
- self.print_expr(*start);
- w!(self, "...");
- self.print_expr(*end);
+ if let Some(start) = start {
+ self.print_literal_or_const(start);
+ }
+ w!(self, "..=");
+ if let Some(end) = end {
+ self.print_literal_or_const(end);
+ }
}
Pat::Slice { prefix, slice, suffix } => {
w!(self, "[");
@@ -601,10 +623,18 @@ impl<'a> Printer<'a> {
}
}
+ fn print_literal_or_const(&mut self, literal_or_const: &LiteralOrConst) {
+ match literal_or_const {
+ LiteralOrConst::Literal(l) => self.print_literal(l),
+ LiteralOrConst::Const(c) => self.print_path(c),
+ }
+ }
+
fn print_literal(&mut self, literal: &Literal) {
match literal {
Literal::String(it) => w!(self, "{:?}", it),
Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()),
+ Literal::CString(it) => w!(self, "\"{}\\0\"", it),
Literal::Char(it) => w!(self, "'{}'", it.escape_debug()),
Literal::Bool(it) => w!(self, "{}", it),
Literal::Int(i, suffix) => {
@@ -629,11 +659,11 @@ impl<'a> Printer<'a> {
}
fn print_type_ref(&mut self, ty: &TypeRef) {
- print_type_ref(ty, self).unwrap();
+ print_type_ref(self.db, ty, self).unwrap();
}
fn print_path(&mut self, path: &Path) {
- print_path(path, self).unwrap();
+ print_path(self.db, path, self).unwrap();
}
fn print_binding(&mut self, id: BindingId) {
@@ -644,6 +674,6 @@ impl<'a> Printer<'a> {
BindingAnnotation::Ref => "ref ",
BindingAnnotation::RefMut => "ref mut ",
};
- w!(self, "{}{}", mode, name);
+ w!(self, "{}{}", mode, name.display(self.db));
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
index 12fc1f116..69741c445 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs
@@ -1,14 +1,13 @@
//! Name resolution for expressions.
-use std::sync::Arc;
-
use hir_expand::name::Name;
-use la_arena::{Arena, Idx};
+use la_arena::{Arena, Idx, IdxRange, RawIdx};
use rustc_hash::FxHashMap;
+use triomphe::Arc;
use crate::{
body::Body,
db::DefDatabase,
- expr::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
+ hir::{Binding, BindingId, Expr, ExprId, LabelId, Pat, PatId, Statement},
BlockId, DefWithBodyId,
};
@@ -17,6 +16,7 @@ pub type ScopeId = Idx<ScopeData>;
#[derive(Debug, PartialEq, Eq)]
pub struct ExprScopes {
scopes: Arena<ScopeData>,
+ scope_entries: Arena<ScopeEntry>,
scope_by_expr: FxHashMap<ExprId, ScopeId>,
}
@@ -41,7 +41,7 @@ pub struct ScopeData {
parent: Option<ScopeId>,
block: Option<BlockId>,
label: Option<(LabelId, Name)>,
- entries: Vec<ScopeEntry>,
+ entries: IdxRange<ScopeEntry>,
}
impl ExprScopes {
@@ -53,7 +53,7 @@ impl ExprScopes {
}
pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
- &self.scopes[scope].entries
+ &self.scope_entries[self.scopes[scope].entries.clone()]
}
/// If `scope` refers to a block expression scope, returns the corresponding `BlockId`.
@@ -85,10 +85,17 @@ impl ExprScopes {
}
}
+fn empty_entries(idx: usize) -> IdxRange<ScopeEntry> {
+ IdxRange::new(Idx::from_raw(RawIdx::from(idx as u32))..Idx::from_raw(RawIdx::from(idx as u32)))
+}
+
impl ExprScopes {
fn new(body: &Body) -> ExprScopes {
- let mut scopes =
- ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
+ let mut scopes = ExprScopes {
+ scopes: Arena::default(),
+ scope_entries: Arena::default(),
+ scope_by_expr: FxHashMap::default(),
+ };
let mut root = scopes.root_scope();
scopes.add_params_bindings(body, root, &body.params);
compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
@@ -96,7 +103,12 @@ impl ExprScopes {
}
fn root_scope(&mut self) -> ScopeId {
- self.scopes.alloc(ScopeData { parent: None, block: None, label: None, entries: vec![] })
+ self.scopes.alloc(ScopeData {
+ parent: None,
+ block: None,
+ label: None,
+ entries: empty_entries(self.scope_entries.len()),
+ })
}
fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
@@ -104,32 +116,38 @@ impl ExprScopes {
parent: Some(parent),
block: None,
label: None,
- entries: vec![],
+ entries: empty_entries(self.scope_entries.len()),
})
}
fn new_labeled_scope(&mut self, parent: ScopeId, label: Option<(LabelId, Name)>) -> ScopeId {
- self.scopes.alloc(ScopeData { parent: Some(parent), block: None, label, entries: vec![] })
+ self.scopes.alloc(ScopeData {
+ parent: Some(parent),
+ block: None,
+ label,
+ entries: empty_entries(self.scope_entries.len()),
+ })
}
fn new_block_scope(
&mut self,
parent: ScopeId,
- block: BlockId,
+ block: Option<BlockId>,
label: Option<(LabelId, Name)>,
) -> ScopeId {
self.scopes.alloc(ScopeData {
parent: Some(parent),
- block: Some(block),
+ block,
label,
- entries: vec![],
+ entries: empty_entries(self.scope_entries.len()),
})
}
fn add_bindings(&mut self, body: &Body, scope: ScopeId, binding: BindingId) {
let Binding { name, .. } = &body.bindings[binding];
- let entry = ScopeEntry { name: name.clone(), binding };
- self.scopes[scope].entries.push(entry);
+ let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding });
+ self.scopes[scope].entries =
+ IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
}
fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
@@ -150,9 +168,9 @@ impl ExprScopes {
}
fn shrink_to_fit(&mut self) {
- let ExprScopes { scopes, scope_by_expr } = self;
+ let ExprScopes { scopes, scope_entries, scope_by_expr } = self;
scopes.shrink_to_fit();
- scopes.values_mut().for_each(|it| it.entries.shrink_to_fit());
+ scope_entries.shrink_to_fit();
scope_by_expr.shrink_to_fit();
}
}
@@ -200,22 +218,16 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope
scopes.set_scope(expr, scope);
compute_block_scopes(statements, *tail, body, scopes, &mut scope);
}
- Expr::Unsafe { id, statements, tail }
- | Expr::Async { id, statements, tail }
- | Expr::Const { id, statements, tail }
- | Expr::TryBlock { id, statements, tail } => {
+ Expr::Const(_) => {
+ // FIXME: This is broken.
+ }
+ Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => {
let mut scope = scopes.new_block_scope(*scope, *id, None);
// Overwrite the old scope for the block expr, so that every block scope can be found
// via the block itself (important for blocks that only contain items, no expressions).
scopes.set_scope(expr, scope);
compute_block_scopes(statements, *tail, body, scopes, &mut scope);
}
- Expr::For { iterable, pat, body: body_expr, label } => {
- compute_expr_scopes(*iterable, body, scopes, scope);
- let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
- scopes.add_pat_bindings(body, scope, *pat);
- compute_expr_scopes(*body_expr, body, scopes, &mut scope);
- }
Expr::While { condition, body: body_expr, label } => {
let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
compute_expr_scopes(*condition, body, scopes, &mut scope);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
index 77ac221e5..6e77744f2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
@@ -148,8 +148,8 @@ fn f() {
}
"#,
expect![[r#"
- BlockId(1) in ModuleId { krate: CrateId(0), block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) }
- BlockId(0) in ModuleId { krate: CrateId(0), block: None, local_id: Idx::<ModuleData>(0) }
+ BlockId(1) in BlockRelativeModuleId { block: Some(BlockId(0)), local_id: Idx::<ModuleData>(1) }
+ BlockId(0) in BlockRelativeModuleId { block: None, local_id: Idx::<ModuleData>(0) }
crate scope
"#]],
);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs
index dd69c3ab4..61b248197 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_type.rs
@@ -106,8 +106,14 @@ impl AsName for BuiltinType {
impl fmt::Display for BuiltinType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let type_name = self.as_name();
- type_name.fmt(f)
+ match self {
+ BuiltinType::Char => f.write_str("char"),
+ BuiltinType::Bool => f.write_str("bool"),
+ BuiltinType::Str => f.write_str("str"),
+ BuiltinType::Int(it) => it.fmt(f),
+ BuiltinType::Uint(it) => it.fmt(f),
+ BuiltinType::Float(it) => it.fmt(f),
+ }
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
index 68b57acca..bb79e28f2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
@@ -10,9 +10,9 @@ use syntax::ast::HasDocComments;
use crate::{
db::DefDatabase,
- dyn_map::DynMap,
+ dyn_map::{keys, DynMap},
item_scope::ItemScope,
- keys,
+ nameres::DefMap,
src::{HasChildSource, HasSource},
AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, FieldId, ImplId, Lookup, MacroId,
ModuleDefId, ModuleId, TraitId, VariantId,
@@ -206,7 +206,7 @@ impl ChildBySource for DefWithBodyId {
for (_, def_map) in body.blocks(db) {
// All block expressions are merged into the same map, because they logically all add
// inner items to the containing `DefWithBodyId`.
- def_map[def_map.root()].scope.child_by_source_to(db, res, file_id);
+ def_map[DefMap::ROOT].scope.child_by_source_to(db, res, file_id);
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index 1633a33be..40e6a4308 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -1,22 +1,28 @@
//! Contains basic data about various HIR declarations.
-use std::sync::Arc;
+pub mod adt;
-use hir_expand::{name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroDefKind};
+use hir_expand::{
+ name::Name, AstId, ExpandResult, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefKind,
+};
use intern::Interned;
use smallvec::SmallVec;
-use syntax::ast;
+use syntax::{ast, Parse};
+use triomphe::Arc;
use crate::{
attr::Attrs,
- body::{Expander, Mark},
db::DefDatabase,
- item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId},
+ expander::{Expander, Mark},
+ item_tree::{
+ self, AssocItem, FnFlags, ItemTree, ItemTreeId, MacroCall, ModItem, Param, TreeId,
+ },
+ macro_call_as_call_id, macro_id_to_def_id,
nameres::{
attr_resolution::ResolvedAttr,
diagnostics::DefDiagnostic,
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroKind},
- DefMap,
+ DefMap, MacroSubNs,
},
type_ref::{TraitRef, TypeBound, TypeRef},
visibility::RawVisibility,
@@ -28,9 +34,8 @@ use crate::{
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FunctionData {
pub name: Name,
- pub params: Vec<(Option<Name>, Interned<TypeRef>)>,
+ pub params: Vec<Interned<TypeRef>>,
pub ret_type: Interned<TypeRef>,
- pub async_ret_type: Option<Interned<TypeRef>>,
pub attrs: Attrs,
pub visibility: RawVisibility,
pub abi: Option<Interned<str>>,
@@ -43,16 +48,16 @@ impl FunctionData {
pub(crate) fn fn_data_query(db: &dyn DefDatabase, func: FunctionId) -> Arc<FunctionData> {
let loc = func.lookup(db);
let krate = loc.container.module(db).krate;
- let crate_graph = db.crate_graph();
- let cfg_options = &crate_graph[krate].cfg_options;
let item_tree = loc.id.item_tree(db);
let func = &item_tree[loc.id.value];
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
- db.trait_data(trait_id).visibility.clone()
+ trait_vis(db, trait_id)
} else {
item_tree[func.visibility].clone()
};
+ let crate_graph = db.crate_graph();
+ let cfg_options = &crate_graph[krate].cfg_options;
let enabled_params = func
.params
.clone()
@@ -99,12 +104,11 @@ impl FunctionData {
params: enabled_params
.clone()
.filter_map(|id| match &item_tree[id] {
- Param::Normal(name, ty) => Some((name.clone(), ty.clone())),
+ Param::Normal(ty) => Some(ty.clone()),
Param::Varargs => None,
})
.collect(),
ret_type: func.ret_type.clone(),
- async_ret_type: func.async_ret_type.clone(),
attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
visibility,
abi: func.abi.clone(),
@@ -188,7 +192,7 @@ impl TypeAliasData {
let item_tree = loc.id.item_tree(db);
let typ = &item_tree[loc.id.value];
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
- db.trait_data(trait_id).visibility.clone()
+ trait_vis(db, trait_id)
} else {
item_tree[typ.visibility].clone()
};
@@ -471,7 +475,7 @@ impl ConstData {
let item_tree = loc.id.item_tree(db);
let konst = &item_tree[loc.id.value];
let visibility = if let ItemContainerId::TraitId(trait_id) = loc.container {
- db.trait_data(trait_id).visibility.clone()
+ trait_vis(db, trait_id)
} else {
item_tree[konst.visibility].clone()
};
@@ -519,7 +523,7 @@ struct AssocItemCollector<'a> {
db: &'a dyn DefDatabase,
module_id: ModuleId,
def_map: Arc<DefMap>,
- inactive_diagnostics: Vec<DefDiagnostic>,
+ diagnostics: Vec<DefDiagnostic>,
container: ItemContainerId,
expander: Expander,
@@ -542,7 +546,7 @@ impl<'a> AssocItemCollector<'a> {
expander: Expander::new(db, file_id, module_id),
items: Vec::new(),
attr_calls: Vec::new(),
- inactive_diagnostics: Vec::new(),
+ diagnostics: Vec::new(),
}
}
@@ -556,11 +560,10 @@ impl<'a> AssocItemCollector<'a> {
(
self.items,
if self.attr_calls.is_empty() { None } else { Some(Box::new(self.attr_calls)) },
- self.inactive_diagnostics,
+ self.diagnostics,
)
}
- // FIXME: proc-macro diagnostics
fn collect(&mut self, item_tree: &ItemTree, tree_id: TreeId, assoc_items: &[AssocItem]) {
let container = self.container;
self.items.reserve(assoc_items.len());
@@ -568,7 +571,7 @@ impl<'a> AssocItemCollector<'a> {
'items: for &item in assoc_items {
let attrs = item_tree.attrs(self.db, self.module_id.krate, ModItem::from(item).into());
if !attrs.is_cfg_enabled(self.expander.cfg_options()) {
- self.inactive_diagnostics.push(DefDiagnostic::unconfigured_code(
+ self.diagnostics.push(DefDiagnostic::unconfigured_code(
self.module_id.local_id,
InFile::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast()),
attrs.cfg().unwrap(),
@@ -582,84 +585,164 @@ impl<'a> AssocItemCollector<'a> {
AstId::new(self.expander.current_file_id(), item.ast_id(item_tree).upcast());
let ast_id_with_path = AstIdWithPath { path: (*attr.path).clone(), ast_id };
- if let Ok(ResolvedAttr::Macro(call_id)) = self.def_map.resolve_attr_macro(
+ match self.def_map.resolve_attr_macro(
self.db,
self.module_id.local_id,
ast_id_with_path,
attr,
) {
- self.attr_calls.push((ast_id, call_id));
- // If proc attribute macro expansion is disabled, skip expanding it here
- if !self.db.enable_proc_attr_macros() {
- continue 'attrs;
- }
- let loc = self.db.lookup_intern_macro_call(call_id);
- if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
- // If there's no expander for the proc macro (e.g. the
- // proc macro is ignored, or building the proc macro
- // crate failed), skip expansion like we would if it was
- // disabled. This is analogous to the handling in
- // `DefCollector::collect_macros`.
- if exp.is_dummy() {
+ Ok(ResolvedAttr::Macro(call_id)) => {
+ self.attr_calls.push((ast_id, call_id));
+ // If proc attribute macro expansion is disabled, skip expanding it here
+ if !self.db.expand_proc_attr_macros() {
continue 'attrs;
}
- }
- match self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id) {
- ExpandResult { value: Some((mark, _)), .. } => {
- self.collect_macro_items(mark);
- continue 'items;
+ let loc = self.db.lookup_intern_macro_call(call_id);
+ if let MacroDefKind::ProcMacro(exp, ..) = loc.def.kind {
+ // If there's no expander for the proc macro (e.g. the
+ // proc macro is ignored, or building the proc macro
+ // crate failed), skip expansion like we would if it was
+ // disabled. This is analogous to the handling in
+ // `DefCollector::collect_macros`.
+ if exp.is_dummy() {
+ continue 'attrs;
+ }
}
- ExpandResult { .. } => {}
+
+ let res =
+ self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
+ self.collect_macro_items(res, &|| loc.kind.clone());
+ continue 'items;
+ }
+ Ok(_) => (),
+ Err(_) => {
+ self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
+ self.module_id.local_id,
+ MacroCallKind::Attr {
+ ast_id,
+ attr_args: Arc::new((tt::Subtree::empty(), Default::default())),
+ invoc_attr_index: attr.id,
+ },
+ attr.path().clone(),
+ ));
}
}
}
- match item {
- AssocItem::Function(id) => {
- let item = &item_tree[id];
+ self.collect_item(item_tree, tree_id, container, item);
+ }
+ }
- let def =
- FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
- self.items.push((item.name.clone(), def.into()));
- }
- AssocItem::Const(id) => {
- let item = &item_tree[id];
-
- let name = match item.name.clone() {
- Some(name) => name,
- None => continue,
- };
- let def =
- ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
- self.items.push((name, def.into()));
- }
- AssocItem::TypeAlias(id) => {
- let item = &item_tree[id];
+ fn collect_item(
+ &mut self,
+ item_tree: &ItemTree,
+ tree_id: TreeId,
+ container: ItemContainerId,
+ item: AssocItem,
+ ) {
+ match item {
+ AssocItem::Function(id) => {
+ let item = &item_tree[id];
- let def = TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }
- .intern(self.db);
- self.items.push((item.name.clone(), def.into()));
- }
- AssocItem::MacroCall(call) => {
- if let Some(root) = self.db.parse_or_expand(self.expander.current_file_id()) {
- let call = &item_tree[call];
-
- let ast_id_map = self.db.ast_id_map(self.expander.current_file_id());
- let call = ast_id_map.get(call.ast_id).to_node(&root);
- let _cx =
- stdx::panic_context::enter(format!("collect_items MacroCall: {call}"));
- let res = self.expander.enter_expand::<ast::MacroItems>(self.db, call);
-
- if let Ok(ExpandResult { value: Some((mark, _)), .. }) = res {
- self.collect_macro_items(mark);
- }
+ let def =
+ FunctionLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
+ self.items.push((item.name.clone(), def.into()));
+ }
+ AssocItem::Const(id) => {
+ let item = &item_tree[id];
+ let Some(name) = item.name.clone() else { return };
+ let def = ConstLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
+ self.items.push((name, def.into()));
+ }
+ AssocItem::TypeAlias(id) => {
+ let item = &item_tree[id];
+
+ let def =
+ TypeAliasLoc { container, id: ItemTreeId::new(tree_id, id) }.intern(self.db);
+ self.items.push((item.name.clone(), def.into()));
+ }
+ AssocItem::MacroCall(call) => {
+ let file_id = self.expander.current_file_id();
+ let MacroCall { ast_id, expand_to, ref path } = item_tree[call];
+ let module = self.expander.module.local_id;
+
+ let resolver = |path| {
+ self.def_map
+ .resolve_path(
+ self.db,
+ module,
+ &path,
+ crate::item_scope::BuiltinShadowMode::Other,
+ Some(MacroSubNs::Bang),
+ )
+ .0
+ .take_macros()
+ .map(|it| macro_id_to_def_id(self.db, it))
+ };
+ match macro_call_as_call_id(
+ self.db.upcast(),
+ &AstIdWithPath::new(file_id, ast_id, Clone::clone(path)),
+ expand_to,
+ self.expander.module.krate(),
+ resolver,
+ ) {
+ Ok(Some(call_id)) => {
+ let res =
+ self.expander.enter_expand_id::<ast::MacroItems>(self.db, call_id);
+ self.collect_macro_items(res, &|| hir_expand::MacroCallKind::FnLike {
+ ast_id: InFile::new(file_id, ast_id),
+ expand_to: hir_expand::ExpandTo::Items,
+ });
+ }
+ Ok(None) => (),
+ Err(_) => {
+ self.diagnostics.push(DefDiagnostic::unresolved_macro_call(
+ self.module_id.local_id,
+ MacroCallKind::FnLike {
+ ast_id: InFile::new(file_id, ast_id),
+ expand_to,
+ },
+ Clone::clone(path),
+ ));
}
}
}
}
}
- fn collect_macro_items(&mut self, mark: Mark) {
+ fn collect_macro_items(
+ &mut self,
+ ExpandResult { value, err }: ExpandResult<Option<(Mark, Parse<ast::MacroItems>)>>,
+ error_call_kind: &dyn Fn() -> hir_expand::MacroCallKind,
+ ) {
+ let Some((mark, parse)) = value else { return };
+
+ if let Some(err) = err {
+ let diag = match err {
+ // why is this reported here?
+ hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
+ DefDiagnostic::unresolved_proc_macro(
+ self.module_id.local_id,
+ error_call_kind(),
+ krate,
+ )
+ }
+ _ => DefDiagnostic::macro_error(
+ self.module_id.local_id,
+ error_call_kind(),
+ err.to_string(),
+ ),
+ };
+ self.diagnostics.push(diag);
+ }
+ if let errors @ [_, ..] = parse.errors() {
+ self.diagnostics.push(DefDiagnostic::macro_expansion_parse_error(
+ self.module_id.local_id,
+ error_call_kind(),
+ errors.into(),
+ ));
+ }
+
let tree_id = item_tree::TreeId::new(self.expander.current_file_id(), None);
let item_tree = tree_id.item_tree(self.db);
let iter: SmallVec<[_; 2]> =
@@ -670,3 +753,10 @@ impl<'a> AssocItemCollector<'a> {
self.expander.exit(self.db, mark);
}
}
+
+fn trait_vis(db: &dyn DefDatabase, trait_id: TraitId) -> RawVisibility {
+ let ItemLoc { id: tree_id, .. } = trait_id.lookup(db);
+ let item_tree = tree_id.item_tree(db);
+ let tr_def = &item_tree[tree_id.value];
+ item_tree[tr_def.visibility].clone()
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
index b336f59ff..6db5abccc 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
@@ -1,8 +1,7 @@
//! Defines hir-level representation of structs, enums and unions
-use std::sync::Arc;
-
use base_db::CrateId;
+use bitflags::bitflags;
use cfg::CfgOptions;
use either::Either;
@@ -12,15 +11,17 @@ use hir_expand::{
};
use intern::Interned;
use la_arena::{Arena, ArenaMap};
-use rustc_abi::{Integer, IntegerType};
+use rustc_abi::{Align, Integer, IntegerType, ReprFlags, ReprOptions};
use syntax::ast::{self, HasName, HasVisibility};
+use triomphe::Arc;
use crate::{
- body::{CfgExpander, LowerCtx},
builtin_type::{BuiltinInt, BuiltinUint},
db::DefDatabase,
+ expander::CfgExpander,
item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
- layout::{Align, ReprFlags, ReprOptions},
+ lang_item::LangItem,
+ lower::LowerCtx,
nameres::diagnostics::DefDiagnostic,
src::HasChildSource,
src::HasSource,
@@ -39,8 +40,27 @@ pub struct StructData {
pub variant_data: Arc<VariantData>,
pub repr: Option<ReprOptions>,
pub visibility: RawVisibility,
- pub rustc_has_incoherent_inherent_impls: bool,
- pub fundamental: bool,
+ pub flags: StructFlags,
+}
+
+bitflags! {
+#[derive(Debug, Clone, PartialEq, Eq)]
+ pub struct StructFlags: u8 {
+ const NO_FLAGS = 0;
+ /// Indicates whether the struct is `PhantomData`.
+ const IS_PHANTOM_DATA = 1 << 2;
+ /// Indicates whether the struct has a `#[fundamental]` attribute.
+ const IS_FUNDAMENTAL = 1 << 3;
+ // FIXME: should this be a flag?
+ /// Indicates whether the struct has a `#[rustc_has_incoherent_inherent_impls]` attribute.
+ const IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL = 1 << 4;
+ /// Indicates whether this struct is `Box`.
+ const IS_BOX = 1 << 5;
+ /// Indicates whether this struct is `ManuallyDrop`.
+ const IS_MANUALLY_DROP = 1 << 6;
+ /// Indicates whether this struct is `UnsafeCell`.
+ const IS_UNSAFE_CELL = 1 << 7;
+ }
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -174,10 +194,25 @@ impl StructData {
let item_tree = loc.id.item_tree(db);
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
+
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
- let rustc_has_incoherent_inherent_impls =
- attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
- let fundamental = attrs.by_key("fundamental").exists();
+
+ let mut flags = StructFlags::NO_FLAGS;
+ if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
+ flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
+ }
+ if attrs.by_key("fundamental").exists() {
+ flags |= StructFlags::IS_FUNDAMENTAL;
+ }
+ if let Some(lang) = attrs.lang_item() {
+ match lang {
+ LangItem::PhantomData => flags |= StructFlags::IS_PHANTOM_DATA,
+ LangItem::OwnedBox => flags |= StructFlags::IS_BOX,
+ LangItem::ManuallyDrop => flags |= StructFlags::IS_MANUALLY_DROP,
+ LangItem::UnsafeCell => flags |= StructFlags::IS_UNSAFE_CELL,
+ _ => (),
+ }
+ }
let strukt = &item_tree[loc.id.value];
let (variant_data, diagnostics) = lower_fields(
@@ -196,8 +231,7 @@ impl StructData {
variant_data: Arc::new(variant_data),
repr,
visibility: item_tree[strukt.visibility].clone(),
- rustc_has_incoherent_inherent_impls,
- fundamental,
+ flags,
}),
diagnostics.into(),
)
@@ -218,9 +252,13 @@ impl StructData {
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
- let rustc_has_incoherent_inherent_impls =
- attrs.by_key("rustc_has_incoherent_inherent_impls").exists();
- let fundamental = attrs.by_key("fundamental").exists();
+ let mut flags = StructFlags::NO_FLAGS;
+ if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
+ flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
+ }
+ if attrs.by_key("fundamental").exists() {
+ flags |= StructFlags::IS_FUNDAMENTAL;
+ }
let union = &item_tree[loc.id.value];
let (variant_data, diagnostics) = lower_fields(
@@ -239,8 +277,7 @@ impl StructData {
variant_data: Arc::new(variant_data),
repr,
visibility: item_tree[union.visibility].clone(),
- rustc_has_incoherent_inherent_impls,
- fundamental,
+ flags,
}),
diagnostics.into(),
)
@@ -436,7 +473,7 @@ fn lower_struct(
trace: &mut Trace<FieldData, Either<ast::TupleField, ast::RecordField>>,
ast: &InFile<ast::StructKind>,
) -> StructKind {
- let ctx = LowerCtx::new(db, ast.file_id);
+ let ctx = LowerCtx::new(db, &expander.hygiene(), ast.file_id);
match &ast.value {
ast::StructKind::Tuple(fl) => {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
index 9371fc14d..04ec47f84 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -1,18 +1,17 @@
//! Defines database & queries for name resolution.
-use std::sync::Arc;
-
use base_db::{salsa, CrateId, SourceDatabase, Upcast};
use either::Either;
use hir_expand::{db::ExpandDatabase, HirFileId};
use intern::Interned;
use la_arena::ArenaMap;
use syntax::{ast, AstPtr};
+use triomphe::Arc;
use crate::{
- adt::{EnumData, StructData},
attr::{Attrs, AttrsWithOwner},
body::{scope::ExprScopes, Body, BodySourceMap},
data::{
+ adt::{EnumData, StructData},
ConstData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData,
TraitAliasData, TraitData, TypeAliasData,
},
@@ -22,15 +21,17 @@ use crate::{
lang_item::{LangItem, LangItemTarget, LangItems},
nameres::{diagnostics::DefDiagnostic, DefMap},
visibility::{self, Visibility},
- AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId,
- ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId,
- LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc,
- StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc,
- TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId,
+ AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId,
+ EnumId, EnumLoc, ExternBlockId, ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId,
+ ImplLoc, InTypeConstId, InTypeConstLoc, LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc,
+ MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId,
+ StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId,
+ UnionLoc, VariantId,
};
#[salsa::query_group(InternDatabaseStorage)]
pub trait InternDatabase: SourceDatabase {
+ // region: items
#[salsa::interned]
fn intern_function(&self, loc: FunctionLoc) -> FunctionId;
#[salsa::interned]
@@ -54,19 +55,25 @@ pub trait InternDatabase: SourceDatabase {
#[salsa::interned]
fn intern_extern_block(&self, loc: ExternBlockLoc) -> ExternBlockId;
#[salsa::interned]
- fn intern_block(&self, loc: BlockLoc) -> BlockId;
- #[salsa::interned]
fn intern_macro2(&self, loc: Macro2Loc) -> Macro2Id;
#[salsa::interned]
fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId;
#[salsa::interned]
fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId;
+ // endregion: items
+
+ #[salsa::interned]
+ fn intern_block(&self, loc: BlockLoc) -> BlockId;
+ #[salsa::interned]
+ fn intern_anonymous_const(&self, id: ConstBlockLoc) -> ConstBlockId;
+ #[salsa::interned]
+ fn intern_in_type_const(&self, id: InTypeConstLoc) -> InTypeConstId;
}
#[salsa::query_group(DefDatabaseStorage)]
pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDatabase> {
#[salsa::input]
- fn enable_proc_attr_macros(&self) -> bool;
+ fn expand_proc_attr_macros(&self) -> bool;
#[salsa::invoke(ItemTree::file_item_tree_query)]
fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
@@ -94,7 +101,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
/// The `block_def_map` for block 0 would return `None`, while `block_def_map` of block 1 would
/// return a `DefMap` containing `inner`.
#[salsa::invoke(DefMap::block_def_map_query)]
- fn block_def_map(&self, block: BlockId) -> Option<Arc<DefMap>>;
+ fn block_def_map(&self, block: BlockId) -> Arc<DefMap>;
+
+ // region:data
#[salsa::invoke(StructData::struct_data_query)]
fn struct_data(&self, id: StructId) -> Arc<StructData>;
@@ -151,6 +160,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(ProcMacroData::proc_macro_data_query)]
fn proc_macro_data(&self, makro: ProcMacroId) -> Arc<ProcMacroData>;
+ // endregion:data
+
#[salsa::invoke(Body::body_with_source_map_query)]
fn body_with_source_map(&self, def: DefWithBodyId) -> (Arc<Body>, Arc<BodySourceMap>);
@@ -163,6 +174,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(GenericParams::generic_params_query)]
fn generic_params(&self, def: GenericDefId) -> Interned<GenericParams>;
+ // region:attrs
+
#[salsa::invoke(Attrs::variants_attrs_query)]
fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>;
@@ -182,10 +195,13 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
) -> Arc<ArenaMap<LocalFieldId, Either<AstPtr<ast::TupleField>, AstPtr<ast::RecordField>>>>;
#[salsa::invoke(AttrsWithOwner::attrs_query)]
- fn attrs(&self, def: AttrDefId) -> AttrsWithOwner;
+ fn attrs(&self, def: AttrDefId) -> Attrs;
- #[salsa::invoke(LangItems::crate_lang_items_query)]
- fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>;
+ #[salsa::transparent]
+ #[salsa::invoke(AttrsWithOwner::attrs_with_owner)]
+ fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner;
+
+ // endregion:attrs
#[salsa::invoke(LangItems::lang_item_query)]
fn lang_item(&self, start_crate: CrateId, item: LangItem) -> Option<LangItemTarget>;
@@ -193,6 +209,8 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(ImportMap::import_map_query)]
fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
+ // region:visibilities
+
#[salsa::invoke(visibility::field_visibilities_query)]
fn field_visibilities(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Visibility>>;
@@ -203,9 +221,17 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(visibility::const_visibility_query)]
fn const_visibility(&self, def: ConstId) -> Visibility;
+ // endregion:visibilities
+
+ #[salsa::invoke(LangItems::crate_lang_items_query)]
+ fn crate_lang_items(&self, krate: CrateId) -> Arc<LangItems>;
+
#[salsa::transparent]
fn crate_limits(&self, crate_id: CrateId) -> CrateLimits;
+ #[salsa::transparent]
+ fn recursion_limit(&self, crate_id: CrateId) -> u32;
+
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
}
@@ -228,6 +254,10 @@ fn crate_limits(db: &dyn DefDatabase, crate_id: CrateId) -> CrateLimits {
}
}
+fn recursion_limit(db: &dyn DefDatabase, crate_id: CrateId) -> u32 {
+ db.crate_limits(crate_id).recursion_limit
+}
+
fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
let file = db.crate_graph()[crate_id].root_file_id;
let item_tree = db.file_item_tree(file.into());
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
index 166aa04da..63138aa6a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs
@@ -21,6 +21,8 @@
//!
//! This is a work of fiction. Any similarities to Kotlin's `BindingContext` are
//! a coincidence.
+pub mod keys;
+
use std::{
hash::Hash,
marker::PhantomData,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/keys.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs
index f30be6b64..f30be6b64 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/keys.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map/keys.rs
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expander.rs b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
new file mode 100644
index 000000000..a588827c8
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expander.rs
@@ -0,0 +1,211 @@
+//! Macro expansion utilities.
+
+use base_db::CrateId;
+use cfg::CfgOptions;
+use drop_bomb::DropBomb;
+use hir_expand::{
+ attrs::RawAttrs, hygiene::Hygiene, mod_path::ModPath, ExpandError, ExpandResult, HirFileId,
+ InFile, MacroCallId, UnresolvedMacro,
+};
+use limit::Limit;
+use syntax::{ast, Parse, SyntaxNode};
+
+use crate::{
+ attr::Attrs, db::DefDatabase, lower::LowerCtx, macro_id_to_def_id, path::Path, AsMacroCall,
+ MacroId, ModuleId,
+};
+
+/// A subset of Expander that only deals with cfg attributes. We only need it to
+/// avoid cyclic queries in crate def map during enum processing.
+#[derive(Debug)]
+pub(crate) struct CfgExpander {
+ cfg_options: CfgOptions,
+ hygiene: Hygiene,
+ krate: CrateId,
+}
+
+#[derive(Debug)]
+pub struct Expander {
+ cfg_expander: CfgExpander,
+ pub(crate) current_file_id: HirFileId,
+ pub(crate) module: ModuleId,
+ /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
+ recursion_depth: u32,
+ recursion_limit: Limit,
+}
+
+impl CfgExpander {
+ pub(crate) fn new(
+ db: &dyn DefDatabase,
+ current_file_id: HirFileId,
+ krate: CrateId,
+ ) -> CfgExpander {
+ let hygiene = Hygiene::new(db.upcast(), current_file_id);
+ let cfg_options = db.crate_graph()[krate].cfg_options.clone();
+ CfgExpander { cfg_options, hygiene, krate }
+ }
+
+ pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
+ Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, &self.hygiene))
+ }
+
+ pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool {
+ let attrs = self.parse_attrs(db, owner);
+ attrs.is_cfg_enabled(&self.cfg_options)
+ }
+
+ pub(crate) fn hygiene(&self) -> &Hygiene {
+ &self.hygiene
+ }
+}
+
+impl Expander {
+ pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander {
+ let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
+ let recursion_limit = db.recursion_limit(module.krate);
+ #[cfg(not(test))]
+ let recursion_limit = Limit::new(recursion_limit as usize);
+ // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug
+ #[cfg(test)]
+ let recursion_limit = Limit::new(std::cmp::min(32, recursion_limit as usize));
+ Expander { cfg_expander, current_file_id, module, recursion_depth: 0, recursion_limit }
+ }
+
+ pub fn enter_expand<T: ast::AstNode>(
+ &mut self,
+ db: &dyn DefDatabase,
+ macro_call: ast::MacroCall,
+ resolver: impl Fn(ModPath) -> Option<MacroId>,
+ ) -> Result<ExpandResult<Option<(Mark, Parse<T>)>>, UnresolvedMacro> {
+ // FIXME: within_limit should support this, instead of us having to extract the error
+ let mut unresolved_macro_err = None;
+
+ let result = self.within_limit(db, |this| {
+ let macro_call = InFile::new(this.current_file_id, &macro_call);
+ match macro_call.as_call_id_with_errors(db.upcast(), this.module.krate(), |path| {
+ resolver(path).map(|it| macro_id_to_def_id(db, it))
+ }) {
+ Ok(call_id) => call_id,
+ Err(resolve_err) => {
+ unresolved_macro_err = Some(resolve_err);
+ ExpandResult { value: None, err: None }
+ }
+ }
+ });
+
+ if let Some(err) = unresolved_macro_err {
+ Err(err)
+ } else {
+ Ok(result)
+ }
+ }
+
+ pub fn enter_expand_id<T: ast::AstNode>(
+ &mut self,
+ db: &dyn DefDatabase,
+ call_id: MacroCallId,
+ ) -> ExpandResult<Option<(Mark, Parse<T>)>> {
+ self.within_limit(db, |_this| ExpandResult::ok(Some(call_id)))
+ }
+
+ fn enter_expand_inner(
+ db: &dyn DefDatabase,
+ call_id: MacroCallId,
+ error: Option<ExpandError>,
+ ) -> ExpandResult<Option<InFile<Parse<SyntaxNode>>>> {
+ let macro_file = call_id.as_macro_file();
+ let ExpandResult { value, err } = db.parse_macro_expansion(macro_file);
+
+ ExpandResult { value: Some(InFile::new(macro_file.into(), value.0)), err: error.or(err) }
+ }
+
+ pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
+ self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
+ self.current_file_id = mark.file_id;
+ if self.recursion_depth == u32::MAX {
+ // Recursion limit has been reached somewhere in the macro expansion tree. Reset the
+ // depth only when we get out of the tree.
+ if !self.current_file_id.is_macro() {
+ self.recursion_depth = 0;
+ }
+ } else {
+ self.recursion_depth -= 1;
+ }
+ mark.bomb.defuse();
+ }
+
+ pub fn ctx<'a>(&self, db: &'a dyn DefDatabase) -> LowerCtx<'a> {
+ LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id)
+ }
+
+ pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> {
+ InFile { file_id: self.current_file_id, value }
+ }
+
+ pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
+ self.cfg_expander.parse_attrs(db, owner)
+ }
+
+ pub(crate) fn cfg_options(&self) -> &CfgOptions {
+ &self.cfg_expander.cfg_options
+ }
+
+ pub fn current_file_id(&self) -> HirFileId {
+ self.current_file_id
+ }
+
+ pub(crate) fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> {
+ let ctx = LowerCtx::new(db, &self.cfg_expander.hygiene, self.current_file_id);
+ Path::from_src(path, &ctx)
+ }
+
+ fn within_limit<F, T: ast::AstNode>(
+ &mut self,
+ db: &dyn DefDatabase,
+ op: F,
+ ) -> ExpandResult<Option<(Mark, Parse<T>)>>
+ where
+ F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>,
+ {
+ if self.recursion_depth == u32::MAX {
+ // Recursion limit has been reached somewhere in the macro expansion tree. We should
+ // stop expanding other macro calls in this tree, or else this may result in
+ // exponential number of macro expansions, leading to a hang.
+ //
+ // The overflow error should have been reported when it occurred (see the next branch),
+ // so don't return overflow error here to avoid diagnostics duplication.
+ cov_mark::hit!(overflow_but_not_me);
+ return ExpandResult::only_err(ExpandError::RecursionOverflowPoisoned);
+ } else if self.recursion_limit.check(self.recursion_depth as usize + 1).is_err() {
+ self.recursion_depth = u32::MAX;
+ cov_mark::hit!(your_stack_belongs_to_me);
+ return ExpandResult::only_err(ExpandError::other(
+ "reached recursion limit during macro expansion",
+ ));
+ }
+
+ let ExpandResult { value, err } = op(self);
+ let Some(call_id) = value else {
+ return ExpandResult { value: None, err };
+ };
+
+ Self::enter_expand_inner(db, call_id, err).map(|value| {
+ value.and_then(|InFile { file_id, value }| {
+ let parse = value.cast::<T>()?;
+
+ self.recursion_depth += 1;
+ self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
+ let old_file_id = std::mem::replace(&mut self.current_file_id, file_id);
+ let mark =
+ Mark { file_id: old_file_id, bomb: DropBomb::new("expansion mark dropped") };
+ Some((mark, parse))
+ })
+ })
+ }
+}
+
+#[derive(Debug)]
+pub struct Mark {
+ file_id: HirFileId,
+ bomb: DropBomb,
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index 3f4392320..8c49ae1c4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -42,7 +42,7 @@ const MAX_PATH_LEN: usize = 15;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PrefixKind {
/// Causes paths to always start with either `self`, `super`, `crate` or a crate-name.
- /// This is the same as plain, just that paths will start with `self` iprepended f the path
+ /// This is the same as plain, just that paths will start with `self` prepended if the path
/// starts with an identifier that is not a crate.
BySelf,
/// Causes paths to ignore imports in the local module.
@@ -81,7 +81,7 @@ fn find_path_inner(
}
let def_map = from.def_map(db);
- let crate_root = def_map.crate_root(db);
+ let crate_root = def_map.crate_root().into();
// - if the item is a module, jump straight to module search
if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
let mut visited_modules = FxHashSet::default();
@@ -183,7 +183,7 @@ fn find_path_for_module(
// - if the item is the crate root of a dependency crate, return the name from the extern prelude
let root_def_map = crate_root.def_map(db);
- for (name, &def_id) in root_def_map.extern_prelude() {
+ for (name, def_id) in root_def_map.extern_prelude() {
if module_id == def_id {
let name = scope_name.unwrap_or_else(|| name.clone());
@@ -374,7 +374,7 @@ fn calculate_best_path(
}
}
if let Some(module) = item.module(db) {
- if module.def_map(db).block_id().is_some() && prefixed.is_some() {
+ if module.containing_block().is_some() && prefixed.is_some() {
cov_mark::hit!(prefixed_in_block_expression);
prefixed = Some(PrefixKind::Plain);
}
@@ -454,7 +454,7 @@ fn find_local_import_locations(
worklist.push(ancestor);
}
- let def_map = def_map.crate_root(db).def_map(db);
+ let def_map = def_map.crate_root().def_map(db);
let mut seen: FxHashSet<_> = FxHashSet::default();
@@ -543,6 +543,7 @@ mod tests {
module.local_id,
&mod_path,
crate::item_scope::BuiltinShadowMode::Module,
+ None,
)
.0
.take_types()
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
index e4912fa8a..f19c3f028 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
@@ -12,16 +12,17 @@ use hir_expand::{
use intern::Interned;
use la_arena::{Arena, ArenaMap, Idx};
use once_cell::unsync::Lazy;
-use std::ops::DerefMut;
use stdx::impl_from;
use syntax::ast::{self, HasGenericParams, HasName, HasTypeBounds};
+use triomphe::Arc;
use crate::{
- body::{Expander, LowerCtx},
child_by_source::ChildBySource,
db::DefDatabase,
- dyn_map::DynMap,
- keys,
+ dyn_map::{keys, DynMap},
+ expander::Expander,
+ lower::LowerCtx,
+ nameres::{DefMap, MacroSubNs},
src::{HasChildSource, HasSource},
type_ref::{LifetimeRef, TypeBound, TypeRef},
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
@@ -153,7 +154,6 @@ impl GenericParams {
def: GenericDefId,
) -> Interned<GenericParams> {
let _p = profile::span("generic_params_query");
-
macro_rules! id_to_generics {
($id:ident) => {{
let id = $id.lookup(db).id;
@@ -176,8 +176,10 @@ impl GenericParams {
// Don't create an `Expander` nor call `loc.source(db)` if not needed since this
// causes a reparse after the `ItemTree` has been created.
- let mut expander = Lazy::new(|| Expander::new(db, loc.source(db).file_id, module));
- for (_, param) in &func_data.params {
+ let mut expander = Lazy::new(|| {
+ (module.def_map(db), Expander::new(db, loc.source(db).file_id, module))
+ });
+ for param in &func_data.params {
generic_params.fill_implicit_impl_trait_args(db, &mut expander, param);
}
@@ -329,7 +331,7 @@ impl GenericParams {
pub(crate) fn fill_implicit_impl_trait_args(
&mut self,
db: &dyn DefDatabase,
- expander: &mut impl DerefMut<Target = Expander>,
+ exp: &mut Lazy<(Arc<DefMap>, Expander), impl FnOnce() -> (Arc<DefMap>, Expander)>,
type_ref: &TypeRef,
) {
type_ref.walk(&mut |type_ref| {
@@ -349,14 +351,28 @@ impl GenericParams {
}
if let TypeRef::Macro(mc) = type_ref {
let macro_call = mc.to_node(db.upcast());
- match expander.enter_expand::<ast::Type>(db, macro_call) {
- Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
- let ctx = LowerCtx::new(db, expander.current_file_id());
- let type_ref = TypeRef::from_ast(&ctx, expanded);
- self.fill_implicit_impl_trait_args(db, expander, &type_ref);
- expander.exit(db, mark);
- }
- _ => {}
+ let (def_map, expander) = &mut **exp;
+
+ let module = expander.module.local_id;
+ let resolver = |path| {
+ def_map
+ .resolve_path(
+ db,
+ module,
+ &path,
+ crate::item_scope::BuiltinShadowMode::Other,
+ Some(MacroSubNs::Bang),
+ )
+ .0
+ .take_macros()
+ };
+ if let Ok(ExpandResult { value: Some((mark, expanded)), .. }) =
+ expander.enter_expand(db, macro_call, resolver)
+ {
+ let ctx = expander.ctx(db);
+ let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
+ self.fill_implicit_impl_trait_args(db, &mut *exp, &type_ref);
+ exp.1.exit(db, mark);
}
}
});
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index 19fa6b254..500e88006 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -12,26 +12,29 @@
//!
//! See also a neighboring `body` module.
+pub mod type_ref;
+
use std::fmt;
use hir_expand::name::Name;
use intern::Interned;
use la_arena::{Idx, RawIdx};
use smallvec::SmallVec;
+use syntax::ast;
use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
path::{GenericArgs, Path},
type_ref::{Mutability, Rawness, TypeRef},
- BlockId,
+ BlockId, ConstBlockId,
};
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
-pub type ExprId = Idx<Expr>;
-
pub type BindingId = Idx<Binding>;
+pub type ExprId = Idx<Expr>;
+
/// FIXME: this is a hacky function which should be removed
pub(crate) fn dummy_expr_id() -> ExprId {
ExprId::from_raw(RawIdx::from(u32::MAX))
@@ -82,6 +85,7 @@ impl fmt::Display for FloatTypeWrapper {
pub enum Literal {
String(Box<str>),
ByteString(Box<[u8]>),
+ CString(Box<str>),
Char(char),
Bool(bool),
Int(i128, Option<BuiltinInt>),
@@ -93,6 +97,66 @@ pub enum Literal {
}
#[derive(Debug, Clone, Eq, PartialEq)]
+/// Used in range patterns.
+pub enum LiteralOrConst {
+ Literal(Literal),
+ Const(Path),
+}
+
+impl Literal {
+ pub fn negate(self) -> Option<Self> {
+ if let Literal::Int(i, k) = self {
+ Some(Literal::Int(-i, k))
+ } else {
+ None
+ }
+ }
+}
+
+impl From<ast::LiteralKind> for Literal {
+ fn from(ast_lit_kind: ast::LiteralKind) -> Self {
+ use ast::LiteralKind;
+ match ast_lit_kind {
+ // FIXME: these should have actual values filled in, but unsure on perf impact
+ LiteralKind::IntNumber(lit) => {
+ if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
+ Literal::Float(
+ FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
+ builtin,
+ )
+ } else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
+ Literal::Uint(lit.value().unwrap_or(0), builtin)
+ } else {
+ let builtin = lit.suffix().and_then(BuiltinInt::from_suffix);
+ Literal::Int(lit.value().unwrap_or(0) as i128, builtin)
+ }
+ }
+ LiteralKind::FloatNumber(lit) => {
+ let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
+ Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
+ }
+ LiteralKind::ByteString(bs) => {
+ let text = bs.value().map(Box::from).unwrap_or_else(Default::default);
+ Literal::ByteString(text)
+ }
+ LiteralKind::String(s) => {
+ let text = s.value().map(Box::from).unwrap_or_else(Default::default);
+ Literal::String(text)
+ }
+ LiteralKind::CString(s) => {
+ let text = s.value().map(Box::from).unwrap_or_else(Default::default);
+ Literal::CString(text)
+ }
+ LiteralKind::Byte(b) => {
+ Literal::Uint(b.value().unwrap_or_default() as u128, Some(BuiltinUint::U8))
+ }
+ LiteralKind::Char(c) => Literal::Char(c.value().unwrap_or_default()),
+ LiteralKind::Bool(val) => Literal::Bool(val),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Expr {
/// This is produced if the syntax tree does not have a required expression piece.
Missing,
@@ -107,28 +171,19 @@ pub enum Expr {
expr: ExprId,
},
Block {
- id: BlockId,
+ id: Option<BlockId>,
statements: Box<[Statement]>,
tail: Option<ExprId>,
label: Option<LabelId>,
},
- TryBlock {
- id: BlockId,
- statements: Box<[Statement]>,
- tail: Option<ExprId>,
- },
Async {
- id: BlockId,
- statements: Box<[Statement]>,
- tail: Option<ExprId>,
- },
- Const {
- id: BlockId,
+ id: Option<BlockId>,
statements: Box<[Statement]>,
tail: Option<ExprId>,
},
+ Const(ConstBlockId),
Unsafe {
- id: BlockId,
+ id: Option<BlockId>,
statements: Box<[Statement]>,
tail: Option<ExprId>,
},
@@ -141,12 +196,6 @@ pub enum Expr {
body: ExprId,
label: Option<LabelId>,
},
- For {
- iterable: ExprId,
- pat: PatId,
- body: ExprId,
- label: Option<LabelId>,
- },
Call {
callee: ExprId,
args: Box<[ExprId]>,
@@ -163,11 +212,11 @@ pub enum Expr {
arms: Box<[MatchArm]>,
},
Continue {
- label: Option<Name>,
+ label: Option<LabelId>,
},
Break {
expr: Option<ExprId>,
- label: Option<Name>,
+ label: Option<LabelId>,
},
Return {
expr: Option<ExprId>,
@@ -192,9 +241,6 @@ pub enum Expr {
Await {
expr: ExprId,
},
- Try {
- expr: ExprId,
- },
Cast {
expr: ExprId,
type_ref: Interned<TypeRef>,
@@ -231,6 +277,7 @@ pub enum Expr {
ret_type: Option<Interned<TypeRef>>,
body: ExprId,
closure_kind: ClosureKind,
+ capture_by: CaptureBy,
},
Tuple {
exprs: Box<[ExprId]>,
@@ -249,6 +296,14 @@ pub enum ClosureKind {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CaptureBy {
+ /// `move |x| y + x`.
+ Value,
+ /// `move` keyword was not specified.
+ Ref,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Movability {
Static,
Movable,
@@ -302,11 +357,10 @@ impl Expr {
Expr::Let { expr, .. } => {
f(*expr);
}
+ Expr::Const(_) => (),
Expr::Block { statements, tail, .. }
- | Expr::TryBlock { statements, tail, .. }
| Expr::Unsafe { statements, tail, .. }
- | Expr::Async { statements, tail, .. }
- | Expr::Const { statements, tail, .. } => {
+ | Expr::Async { statements, tail, .. } => {
for stmt in statements.iter() {
match stmt {
Statement::Let { initializer, else_branch, .. } => {
@@ -329,10 +383,6 @@ impl Expr {
f(*condition);
f(*body);
}
- Expr::For { iterable, body, .. } => {
- f(*iterable);
- f(*body);
- }
Expr::Call { callee, args, .. } => {
f(*callee);
args.iter().copied().for_each(f);
@@ -383,7 +433,6 @@ impl Expr {
}
Expr::Field { expr, .. }
| Expr::Await { expr }
- | Expr::Try { expr }
| Expr::Cast { expr, .. }
| Expr::Ref { expr, .. }
| Expr::UnaryOp { expr, .. }
@@ -438,10 +487,21 @@ impl BindingAnnotation {
}
#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum BindingProblems {
+ /// https://doc.rust-lang.org/stable/error_codes/E0416.html
+ BoundMoreThanOnce,
+ /// https://doc.rust-lang.org/stable/error_codes/E0409.html
+ BoundInconsistently,
+ /// https://doc.rust-lang.org/stable/error_codes/E0408.html
+ NotBoundAcrossAll,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Binding {
pub name: Name,
pub mode: BindingAnnotation,
pub definitions: SmallVec<[PatId; 1]>,
+ pub problems: Option<BindingProblems>,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -458,7 +518,7 @@ pub enum Pat {
Tuple { args: Box<[PatId]>, ellipsis: Option<usize> },
Or(Box<[PatId]>),
Record { path: Option<Box<Path>>, args: Box<[RecordFieldPat]>, ellipsis: bool },
- Range { start: ExprId, end: ExprId },
+ Range { start: Option<Box<LiteralOrConst>>, end: Option<Box<LiteralOrConst>> },
Slice { prefix: Box<[PatId]>, slice: Option<PatId>, suffix: Box<[PatId]> },
Path(Box<Path>),
Lit(ExprId),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
index 8e30f429a..fa1f4933a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/type_ref.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
@@ -1,9 +1,11 @@
//! HIR for references to types. Paths in these are not yet resolved. They can
//! be directly created from an ast::TypeRef, without further queries.
+use core::fmt;
use std::fmt::Write;
use hir_expand::{
+ db::ExpandDatabase,
name::{AsName, Name},
AstId,
};
@@ -11,9 +13,9 @@ use intern::Interned;
use syntax::ast::{self, HasName};
use crate::{
- body::LowerCtx,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
- expr::Literal,
+ hir::Literal,
+ lower::LowerCtx,
path::Path,
};
@@ -116,7 +118,7 @@ pub enum TypeRef {
Reference(Box<TypeRef>, Option<LifetimeRef>, Mutability),
// FIXME: for full const generics, the latter element (length) here is going to have to be an
// expression that is further lowered later in hir_ty.
- Array(Box<TypeRef>, ConstRefOrPath),
+ Array(Box<TypeRef>, ConstRef),
Slice(Box<TypeRef>),
/// A fn pointer. Last element of the vector is the return type.
Fn(Vec<(Option<Name>, TypeRef)>, bool /*varargs*/, bool /*is_unsafe*/),
@@ -184,11 +186,7 @@ impl TypeRef {
TypeRef::RawPtr(Box::new(inner_ty), mutability)
}
ast::Type::ArrayType(inner) => {
- // FIXME: This is a hack. We should probably reuse the machinery of
- // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
- // `hir_ty` level, which would allow knowing the type of:
- // let v: [u8; 2 + 2] = [0u8; 4];
- let len = ConstRefOrPath::from_expr_opt(inner.expr());
+ let len = ConstRef::from_const_arg(ctx, inner.const_arg());
TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
}
ast::Type::SliceType(inner) => {
@@ -378,69 +376,84 @@ impl TypeBound {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ConstRefOrPath {
- Scalar(ConstRef),
+pub enum ConstRef {
+ Scalar(LiteralConstRef),
Path(Name),
+ Complex(AstId<ast::ConstArg>),
}
-impl std::fmt::Display for ConstRefOrPath {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- ConstRefOrPath::Scalar(s) => s.fmt(f),
- ConstRefOrPath::Path(n) => n.fmt(f),
+impl ConstRef {
+ pub(crate) fn from_const_arg(lower_ctx: &LowerCtx<'_>, arg: Option<ast::ConstArg>) -> Self {
+ if let Some(arg) = arg {
+ let ast_id = lower_ctx.ast_id(&arg);
+ if let Some(expr) = arg.expr() {
+ return Self::from_expr(expr, ast_id);
+ }
}
+ Self::Scalar(LiteralConstRef::Unknown)
}
-}
-impl ConstRefOrPath {
- pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
- match expr {
- Some(x) => Self::from_expr(x),
- None => Self::Scalar(ConstRef::Unknown),
+ pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
+ struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef);
+ impl fmt::Display for Display<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.1 {
+ ConstRef::Scalar(s) => s.fmt(f),
+ ConstRef::Path(n) => n.display(self.0).fmt(f),
+ ConstRef::Complex(_) => f.write_str("{const}"),
+ }
+ }
}
+ Display(db, self)
}
- // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
- // parse stage.
- fn from_expr(expr: ast::Expr) -> Self {
+ // We special case literals and single identifiers, to speed up things.
+ fn from_expr(expr: ast::Expr, ast_id: Option<AstId<ast::ConstArg>>) -> Self {
+ fn is_path_ident(p: &ast::PathExpr) -> bool {
+ let Some(path) = p.path() else {
+ return false;
+ };
+ if path.coloncolon_token().is_some() {
+ return false;
+ }
+ if let Some(s) = path.segment() {
+ if s.coloncolon_token().is_some() || s.generic_arg_list().is_some() {
+ return false;
+ }
+ }
+ true
+ }
match expr {
- ast::Expr::PathExpr(p) => {
+ ast::Expr::PathExpr(p) if is_path_ident(&p) => {
match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) {
Some(x) => Self::Path(x.as_name()),
- None => Self::Scalar(ConstRef::Unknown),
+ None => Self::Scalar(LiteralConstRef::Unknown),
}
}
- ast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() {
- Some(ast::UnaryOp::Neg) => {
- let unsigned = Self::from_expr_opt(prefix_expr.expr());
- // Add sign
- match unsigned {
- Self::Scalar(ConstRef::UInt(num)) => {
- Self::Scalar(ConstRef::Int(-(num as i128)))
- }
- other => other,
- }
- }
- _ => Self::from_expr_opt(prefix_expr.expr()),
- },
ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
ast::LiteralKind::IntNumber(num) => {
- num.value().map(ConstRef::UInt).unwrap_or(ConstRef::Unknown)
+ num.value().map(LiteralConstRef::UInt).unwrap_or(LiteralConstRef::Unknown)
}
ast::LiteralKind::Char(c) => {
- c.value().map(ConstRef::Char).unwrap_or(ConstRef::Unknown)
+ c.value().map(LiteralConstRef::Char).unwrap_or(LiteralConstRef::Unknown)
}
- ast::LiteralKind::Bool(f) => ConstRef::Bool(f),
- _ => ConstRef::Unknown,
+ ast::LiteralKind::Bool(f) => LiteralConstRef::Bool(f),
+ _ => LiteralConstRef::Unknown,
}),
- _ => Self::Scalar(ConstRef::Unknown),
+ _ => {
+ if let Some(ast_id) = ast_id {
+ Self::Complex(ast_id)
+ } else {
+ Self::Scalar(LiteralConstRef::Unknown)
+ }
+ }
}
}
}
-/// A concrete constant value
+/// A literal constant value
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ConstRef {
+pub enum LiteralConstRef {
Int(i128),
UInt(u128),
Bool(bool),
@@ -454,18 +467,20 @@ pub enum ConstRef {
Unknown,
}
-impl ConstRef {
+impl LiteralConstRef {
pub fn builtin_type(&self) -> BuiltinType {
match self {
- ConstRef::UInt(_) | ConstRef::Unknown => BuiltinType::Uint(BuiltinUint::U128),
- ConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128),
- ConstRef::Char(_) => BuiltinType::Char,
- ConstRef::Bool(_) => BuiltinType::Bool,
+ LiteralConstRef::UInt(_) | LiteralConstRef::Unknown => {
+ BuiltinType::Uint(BuiltinUint::U128)
+ }
+ LiteralConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128),
+ LiteralConstRef::Char(_) => BuiltinType::Char,
+ LiteralConstRef::Bool(_) => BuiltinType::Bool,
}
}
}
-impl From<Literal> for ConstRef {
+impl From<Literal> for LiteralConstRef {
fn from(literal: Literal) -> Self {
match literal {
Literal::Char(c) => Self::Char(c),
@@ -477,14 +492,14 @@ impl From<Literal> for ConstRef {
}
}
-impl std::fmt::Display for ConstRef {
+impl std::fmt::Display for LiteralConstRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
- ConstRef::Int(num) => num.fmt(f),
- ConstRef::UInt(num) => num.fmt(f),
- ConstRef::Bool(flag) => flag.fmt(f),
- ConstRef::Char(c) => write!(f, "'{c}'"),
- ConstRef::Unknown => f.write_char('_'),
+ LiteralConstRef::Int(num) => num.fmt(f),
+ LiteralConstRef::UInt(num) => num.fmt(f),
+ LiteralConstRef::Bool(flag) => flag.fmt(f),
+ LiteralConstRef::Char(c) => write!(f, "'{c}'"),
+ LiteralConstRef::Unknown => f.write_char('_'),
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index 4f1f6000d..48532655e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -1,6 +1,6 @@
//! A map of all publicly exported items in a crate.
-use std::{fmt, hash::BuildHasherDefault, sync::Arc};
+use std::{fmt, hash::BuildHasherDefault};
use base_db::CrateId;
use fst::{self, Streamer};
@@ -8,10 +8,11 @@ use hir_expand::name::Name;
use indexmap::{map::Entry, IndexMap};
use itertools::Itertools;
use rustc_hash::{FxHashSet, FxHasher};
+use triomphe::Arc;
use crate::{
- db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId,
- ModuleId, TraitId,
+ db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId,
+ ModuleDefId, ModuleId, TraitId,
};
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
@@ -32,13 +33,23 @@ pub struct ImportPath {
pub segments: Vec<Name>,
}
-impl fmt::Display for ImportPath {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(&self.segments.iter().format("::"), f)
+impl ImportPath {
+ pub fn display<'a>(&'a self, db: &'a dyn DefDatabase) -> impl fmt::Display + 'a {
+ struct Display<'a> {
+ db: &'a dyn DefDatabase,
+ path: &'a ImportPath,
+ }
+ impl fmt::Display for Display<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(
+ &self.path.segments.iter().map(|it| it.display(self.db.upcast())).format("::"),
+ f,
+ )
+ }
+ }
+ Display { db, path: self }
}
-}
-impl ImportPath {
fn len(&self) -> usize {
self.segments.len()
}
@@ -75,7 +86,7 @@ impl ImportMap {
let mut importables = import_map
.map
.iter()
- .map(|(item, info)| (item, fst_path(&info.path)))
+ .map(|(item, info)| (item, fst_path(db, &info.path)))
.collect::<Vec<_>>();
importables.sort_by(|(_, fst_path), (_, fst_path2)| fst_path.cmp(fst_path2));
@@ -112,6 +123,25 @@ impl ImportMap {
self.map.get(&item)
}
+ #[cfg(test)]
+ fn fmt_for_test(&self, db: &dyn DefDatabase) -> String {
+ let mut importable_paths: Vec<_> = self
+ .map
+ .iter()
+ .map(|(item, info)| {
+ let ns = match item {
+ ItemInNs::Types(_) => "t",
+ ItemInNs::Values(_) => "v",
+ ItemInNs::Macros(_) => "m",
+ };
+ format!("- {} ({ns})", info.path.display(db))
+ })
+ .collect();
+
+ importable_paths.sort();
+ importable_paths.join("\n")
+ }
+
fn collect_trait_assoc_items(
&mut self,
db: &dyn DefDatabase,
@@ -153,7 +183,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> ImportMap {
// We look only into modules that are public(ly reexported), starting with the crate root.
let empty = ImportPath { segments: vec![] };
- let root = def_map.module_id(def_map.root());
+ let root = def_map.module_id(DefMap::ROOT);
let mut worklist = vec![(root, empty)];
while let Some((module, mod_path)) = worklist.pop() {
let ext_def_map;
@@ -233,13 +263,10 @@ impl fmt::Debug for ImportMap {
let mut importable_paths: Vec<_> = self
.map
.iter()
- .map(|(item, info)| {
- let ns = match item {
- ItemInNs::Types(_) => "t",
- ItemInNs::Values(_) => "v",
- ItemInNs::Macros(_) => "m",
- };
- format!("- {} ({ns})", info.path)
+ .map(|(item, _)| match item {
+ ItemInNs::Types(it) => format!("- {it:?} (t)",),
+ ItemInNs::Values(it) => format!("- {it:?} (v)",),
+ ItemInNs::Macros(it) => format!("- {it:?} (m)",),
})
.collect();
@@ -248,9 +275,9 @@ impl fmt::Debug for ImportMap {
}
}
-fn fst_path(path: &ImportPath) -> String {
+fn fst_path(db: &dyn DefDatabase, path: &ImportPath) -> String {
let _p = profile::span("fst_path");
- let mut s = path.to_string();
+ let mut s = path.display(db).to_string();
s.make_ascii_lowercase();
s
}
@@ -343,7 +370,12 @@ impl Query {
self
}
- fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool {
+ fn import_matches(
+ &self,
+ db: &dyn DefDatabase,
+ import: &ImportInfo,
+ enforce_lowercase: bool,
+ ) -> bool {
let _p = profile::span("import_map::Query::import_matches");
if import.is_trait_assoc_item {
if self.exclude_import_kinds.contains(&ImportKind::AssociatedItem) {
@@ -354,9 +386,9 @@ impl Query {
}
let mut input = if import.is_trait_assoc_item || self.name_only {
- import.path.segments.last().unwrap().to_string()
+ import.path.segments.last().unwrap().display(db.upcast()).to_string()
} else {
- import.path.to_string()
+ import.path.display(db).to_string()
};
if enforce_lowercase || !self.case_sensitive {
input.make_ascii_lowercase();
@@ -421,25 +453,27 @@ pub fn search_dependencies(
let importables = &import_map.importables[indexed_value.value as usize..];
let common_importable_data = &import_map.map[&importables[0]];
- if !query.import_matches(common_importable_data, true) {
+ if !query.import_matches(db, common_importable_data, true) {
continue;
}
// Path shared by the importable items in this group.
- let common_importables_path_fst = fst_path(&common_importable_data.path);
+ let common_importables_path_fst = fst_path(db, &common_importable_data.path);
// Add the items from this `ModPath` group. Those are all subsequent items in
// `importables` whose paths match `path`.
let iter = importables
.iter()
.copied()
- .take_while(|item| common_importables_path_fst == fst_path(&import_map.map[item].path))
+ .take_while(|item| {
+ common_importables_path_fst == fst_path(db, &import_map.map[item].path)
+ })
.filter(|&item| match item_import_kind(item) {
Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
None => true,
})
.filter(|item| {
!query.case_sensitive // we've already checked the common importables path case-insensitively
- || query.import_matches(&import_map.map[item], false)
+ || query.import_matches(db, &import_map.map[item], false)
});
res.extend(iter);
@@ -472,7 +506,7 @@ mod tests {
use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
use expect_test::{expect, Expect};
- use crate::{test_db::TestDB, ItemContainerId, Lookup};
+ use crate::{db::DefDatabase, test_db::TestDB, ItemContainerId, Lookup};
use super::*;
@@ -496,7 +530,7 @@ mod tests {
let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) {
Some(assoc_item_path) => (assoc_item_path, "a"),
None => (
- dependency_imports.path_of(dependency)?.to_string(),
+ dependency_imports.path_of(dependency)?.display(&db).to_string(),
match dependency {
ItemInNs::Types(ModuleDefId::FunctionId(_))
| ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
@@ -547,7 +581,11 @@ mod tests {
None
}
})?;
- return Some(format!("{}::{assoc_item_name}", dependency_imports.path_of(trait_)?));
+ return Some(format!(
+ "{}::{}",
+ dependency_imports.path_of(trait_)?.display(db),
+ assoc_item_name.display(db.upcast())
+ ));
}
None
}
@@ -587,7 +625,7 @@ mod tests {
let map = db.import_map(krate);
- Some(format!("{name}:\n{map:?}\n"))
+ Some(format!("{name}:\n{}\n", map.fmt_for_test(db.upcast())))
})
.sorted()
.collect::<String>();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index 991e44703..2001fb29a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -4,7 +4,7 @@
use std::collections::hash_map::Entry;
use base_db::CrateId;
-use hir_expand::{attrs::AttrId, name::Name, AstId, MacroCallId};
+use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId};
use itertools::Itertools;
use once_cell::sync::Lazy;
use profile::Count;
@@ -334,10 +334,6 @@ impl ItemScope {
)
}
- pub(crate) fn collect_legacy_macros(&self) -> FxHashMap<Name, SmallVec<[MacroId; 1]>> {
- self.legacy_macros.clone()
- }
-
/// Marks everything that is not a procedural macro as private to `this_module`.
pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) {
self.types
@@ -358,12 +354,16 @@ impl ItemScope {
}
}
- pub(crate) fn dump(&self, buf: &mut String) {
+ pub(crate) fn dump(&self, db: &dyn ExpandDatabase, buf: &mut String) {
let mut entries: Vec<_> = self.resolutions().collect();
entries.sort_by_key(|(name, _)| name.clone());
for (name, def) in entries {
- format_to!(buf, "{}:", name.map_or("_".to_string(), |name| name.to_string()));
+ format_to!(
+ buf,
+ "{}:",
+ name.map_or("_".to_string(), |name| name.display(db).to_string())
+ );
if def.types.is_some() {
buf.push_str(" t");
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index 9da5b2d47..e74b71888 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -40,7 +40,6 @@ use std::{
hash::{Hash, Hasher},
marker::PhantomData,
ops::Index,
- sync::Arc,
};
use ast::{AstNode, HasName, StructKind};
@@ -60,6 +59,7 @@ use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use stdx::never;
use syntax::{ast, match_ast, SyntaxKind};
+use triomphe::Arc;
use crate::{
attr::Attrs,
@@ -107,10 +107,7 @@ pub struct ItemTree {
impl ItemTree {
pub(crate) fn file_item_tree_query(db: &dyn DefDatabase, file_id: HirFileId) -> Arc<ItemTree> {
let _p = profile::span("file_item_tree_query").detail(|| format!("{file_id:?}"));
- let syntax = match db.parse_or_expand(file_id) {
- Some(node) => node,
- None => return Default::default(),
- };
+ let syntax = db.parse_or_expand(file_id);
if never!(syntax.kind() == SyntaxKind::ERROR, "{:?} from {:?} {}", file_id, syntax, syntax)
{
// FIXME: not 100% sure why these crop up, but return an empty tree to avoid a panic
@@ -169,8 +166,8 @@ impl ItemTree {
Attrs::filter(db, krate, self.raw_attrs(of).clone())
}
- pub fn pretty_print(&self) -> String {
- pretty::print_item_tree(self)
+ pub fn pretty_print(&self, db: &dyn DefDatabase) -> String {
+ pretty::print_item_tree(db.upcast(), self)
}
fn data(&self) -> &ItemTreeData {
@@ -600,19 +597,18 @@ pub struct Function {
pub abi: Option<Interned<str>>,
pub params: IdxRange<Param>,
pub ret_type: Interned<TypeRef>,
- pub async_ret_type: Option<Interned<TypeRef>>,
pub ast_id: FileAstId<ast::Fn>,
pub(crate) flags: FnFlags,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Param {
- Normal(Option<Name>, Interned<TypeRef>),
+ Normal(Interned<TypeRef>),
Varargs,
}
bitflags::bitflags! {
- #[derive(Default)]
+ #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
pub(crate) struct FnFlags: u8 {
const HAS_SELF_PARAM = 1 << 0;
const HAS_BODY = 1 << 1;
@@ -721,7 +717,6 @@ pub struct Mod {
pub enum ModKind {
/// `mod m { ... }`
Inline { items: Box<[ModItem]> },
-
/// `mod m;`
Outline,
}
@@ -895,10 +890,6 @@ impl ModItem {
}
}
- pub fn downcast<N: ItemTreeNode>(self) -> Option<FileItemTreeId<N>> {
- N::id_from_mod_item(self)
- }
-
pub fn ast_id(&self, tree: &ItemTree) -> FileAstId<ast::Item> {
match self {
ModItem::Import(it) => tree[it.index].ast_id().upcast(),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
index 77b186f8e..46633667e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
@@ -1,6 +1,6 @@
//! AST -> `ItemTree` lowering code.
-use std::{collections::hash_map::Entry, sync::Arc};
+use std::collections::hash_map::Entry;
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, HirFileId};
use syntax::ast::{self, HasModuleItem, HasTypeBounds};
@@ -20,7 +20,7 @@ pub(super) struct Ctx<'a> {
db: &'a dyn DefDatabase,
tree: ItemTree,
source_ast_id_map: Arc<AstIdMap>,
- body_ctx: crate::body::LowerCtx<'a>,
+ body_ctx: crate::lower::LowerCtx<'a>,
}
impl<'a> Ctx<'a> {
@@ -29,7 +29,7 @@ impl<'a> Ctx<'a> {
db,
tree: ItemTree::default(),
source_ast_id_map: db.ast_id_map(file),
- body_ctx: crate::body::LowerCtx::new(db, file),
+ body_ctx: crate::lower::LowerCtx::with_file_id(db, file),
}
}
@@ -293,7 +293,7 @@ impl<'a> Ctx<'a> {
}
};
let ty = Interned::new(self_type);
- let idx = self.data().params.alloc(Param::Normal(None, ty));
+ let idx = self.data().params.alloc(Param::Normal(ty));
self.add_attrs(
idx.into(),
RawAttrs::new(self.db.upcast(), &self_param, self.hygiene()),
@@ -306,19 +306,7 @@ impl<'a> Ctx<'a> {
None => {
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
let ty = Interned::new(type_ref);
- let mut pat = param.pat();
- // FIXME: This really shouldn't be here, in fact FunctionData/ItemTree's function shouldn't know about
- // pattern names at all
- let name = 'name: loop {
- match pat {
- Some(ast::Pat::RefPat(ref_pat)) => pat = ref_pat.pat(),
- Some(ast::Pat::IdentPat(ident)) => {
- break 'name ident.name().map(|it| it.as_name())
- }
- _ => break 'name None,
- }
- };
- self.data().params.alloc(Param::Normal(name, ty))
+ self.data().params.alloc(Param::Normal(ty))
}
};
self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &param, self.hygiene()));
@@ -336,13 +324,12 @@ impl<'a> Ctx<'a> {
None => TypeRef::unit(),
};
- let (ret_type, async_ret_type) = if func.async_token().is_some() {
- let async_ret_type = ret_type.clone();
+ let ret_type = if func.async_token().is_some() {
let future_impl = desugar_future_path(ret_type);
let ty_bound = Interned::new(TypeBound::Path(future_impl, TraitBoundModifier::None));
- (TypeRef::ImplTrait(vec![ty_bound]), Some(async_ret_type))
+ TypeRef::ImplTrait(vec![ty_bound])
} else {
- (ret_type, None)
+ ret_type
};
let abi = func.abi().map(lower_abi);
@@ -376,7 +363,6 @@ impl<'a> Ctx<'a> {
abi,
params,
ret_type: Interned::new(ret_type),
- async_ret_type: async_ret_type.map(Interned::new),
ast_id,
flags,
};
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
index 5f2999796..e873316a5 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
@@ -2,6 +2,8 @@
use std::fmt::{self, Write};
+use hir_expand::db::ExpandDatabase;
+
use crate::{
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
pretty::{print_path, print_type_bounds, print_type_ref},
@@ -10,8 +12,8 @@ use crate::{
use super::*;
-pub(super) fn print_item_tree(tree: &ItemTree) -> String {
- let mut p = Printer { tree, buf: String::new(), indent_level: 0, needs_indent: true };
+pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String {
+ let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
p.print_attrs(attrs, true);
@@ -43,6 +45,7 @@ macro_rules! wln {
}
struct Printer<'a> {
+ db: &'a dyn ExpandDatabase,
tree: &'a ItemTree,
buf: String,
indent_level: usize,
@@ -88,7 +91,7 @@ impl<'a> Printer<'a> {
self,
"#{}[{}{}]",
inner,
- attr.path,
+ attr.path.display(self.db),
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
);
}
@@ -102,7 +105,7 @@ impl<'a> Printer<'a> {
fn print_visibility(&mut self, vis: RawVisibilityId) {
match &self.tree[vis] {
- RawVisibility::Module(path) => w!(self, "pub({}) ", path),
+ RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)),
RawVisibility::Public => w!(self, "pub "),
};
}
@@ -117,7 +120,7 @@ impl<'a> Printer<'a> {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field);
this.print_visibility(*visibility);
- w!(this, "{}: ", name);
+ w!(this, "{}: ", name.display(self.db));
this.print_type_ref(type_ref);
wln!(this, ",");
}
@@ -131,7 +134,7 @@ impl<'a> Printer<'a> {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field);
this.print_visibility(*visibility);
- w!(this, "{}: ", name);
+ w!(this, "{}: ", name.display(self.db));
this.print_type_ref(type_ref);
wln!(this, ",");
}
@@ -164,20 +167,20 @@ impl<'a> Printer<'a> {
fn print_use_tree(&mut self, use_tree: &UseTree) {
match &use_tree.kind {
UseTreeKind::Single { path, alias } => {
- w!(self, "{}", path);
+ w!(self, "{}", path.display(self.db));
if let Some(alias) = alias {
w!(self, " as {}", alias);
}
}
UseTreeKind::Glob { path } => {
if let Some(path) = path {
- w!(self, "{}::", path);
+ w!(self, "{}::", path.display(self.db));
}
w!(self, "*");
}
UseTreeKind::Prefixed { prefix, list } => {
if let Some(prefix) = prefix {
- w!(self, "{}::", prefix);
+ w!(self, "{}::", prefix.display(self.db));
}
w!(self, "{{");
for (i, tree) in list.iter().enumerate() {
@@ -205,7 +208,7 @@ impl<'a> Printer<'a> {
ModItem::ExternCrate(it) => {
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "extern crate {}", name);
+ w!(self, "extern crate {}", name.display(self.db));
if let Some(alias) = alias {
w!(self, " as {}", alias);
}
@@ -233,7 +236,6 @@ impl<'a> Printer<'a> {
abi,
params,
ret_type,
- async_ret_type: _,
ast_id: _,
flags,
} = &self.tree[it];
@@ -253,26 +255,20 @@ impl<'a> Printer<'a> {
if let Some(abi) = abi {
w!(self, "extern \"{}\" ", abi);
}
- w!(self, "fn {}", name);
+ w!(self, "fn {}", name.display(self.db));
self.print_generic_params(explicit_generic_params);
w!(self, "(");
if !params.is_empty() {
self.indented(|this| {
- for (i, param) in params.clone().enumerate() {
+ for param in params.clone() {
this.print_attrs_of(param);
match &this.tree[param] {
- Param::Normal(name, ty) => {
- match name {
- Some(name) => w!(this, "{}: ", name),
- None => w!(this, "_: "),
+ Param::Normal(ty) => {
+ if flags.contains(FnFlags::HAS_SELF_PARAM) {
+ w!(this, "self: ");
}
this.print_type_ref(ty);
- w!(this, ",");
- if flags.contains(FnFlags::HAS_SELF_PARAM) && i == 0 {
- wln!(this, " // self");
- } else {
- wln!(this);
- }
+ wln!(this, ",");
}
Param::Varargs => {
wln!(this, "...");
@@ -293,7 +289,7 @@ impl<'a> Printer<'a> {
ModItem::Struct(it) => {
let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "struct {}", name);
+ w!(self, "struct {}", name.display(self.db));
self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) {
@@ -305,7 +301,7 @@ impl<'a> Printer<'a> {
ModItem::Union(it) => {
let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "union {}", name);
+ w!(self, "union {}", name.display(self.db));
self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) {
@@ -317,14 +313,14 @@ impl<'a> Printer<'a> {
ModItem::Enum(it) => {
let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "enum {}", name);
+ w!(self, "enum {}", name.display(self.db));
self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
for variant in variants.clone() {
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
this.print_attrs_of(variant);
- w!(this, "{}", name);
+ w!(this, "{}", name.display(self.db));
this.print_fields(fields);
wln!(this, ",");
}
@@ -336,7 +332,7 @@ impl<'a> Printer<'a> {
self.print_visibility(*visibility);
w!(self, "const ");
match name {
- Some(name) => w!(self, "{}", name),
+ Some(name) => w!(self, "{}", name.display(self.db)),
None => w!(self, "_"),
}
w!(self, ": ");
@@ -350,7 +346,7 @@ impl<'a> Printer<'a> {
if *mutable {
w!(self, "mut ");
}
- w!(self, "{}: ", name);
+ w!(self, "{}: ", name.display(self.db));
self.print_type_ref(type_ref);
w!(self, " = _;");
wln!(self);
@@ -372,7 +368,7 @@ impl<'a> Printer<'a> {
if *is_auto {
w!(self, "auto ");
}
- w!(self, "trait {}", name);
+ w!(self, "trait {}", name.display(self.db));
self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
@@ -385,7 +381,7 @@ impl<'a> Printer<'a> {
ModItem::TraitAlias(it) => {
let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "trait {}", name);
+ w!(self, "trait {}", name.display(self.db));
self.print_generic_params(generic_params);
w!(self, " = ");
self.print_where_clause(generic_params);
@@ -418,7 +414,7 @@ impl<'a> Printer<'a> {
let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
&self.tree[it];
self.print_visibility(*visibility);
- w!(self, "type {}", name);
+ w!(self, "type {}", name.display(self.db));
self.print_generic_params(generic_params);
if !bounds.is_empty() {
w!(self, ": ");
@@ -435,7 +431,7 @@ impl<'a> Printer<'a> {
ModItem::Mod(it) => {
let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "mod {}", name);
+ w!(self, "mod {}", name.display(self.db));
match kind {
ModKind::Inline { items } => {
w!(self, " {{");
@@ -453,16 +449,16 @@ impl<'a> Printer<'a> {
}
ModItem::MacroCall(it) => {
let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it];
- wln!(self, "{}!(...);", path);
+ wln!(self, "{}!(...);", path.display(self.db));
}
ModItem::MacroRules(it) => {
let MacroRules { name, ast_id: _ } = &self.tree[it];
- wln!(self, "macro_rules! {} {{ ... }}", name);
+ wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db));
}
ModItem::MacroDef(it) => {
let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- wln!(self, "macro {} {{ ... }}", name);
+ wln!(self, "macro {} {{ ... }}", name.display(self.db));
}
}
@@ -470,15 +466,15 @@ impl<'a> Printer<'a> {
}
fn print_type_ref(&mut self, type_ref: &TypeRef) {
- print_type_ref(type_ref, self).unwrap();
+ print_type_ref(self.db, type_ref, self).unwrap();
}
fn print_type_bounds(&mut self, bounds: &[Interned<TypeBound>]) {
- print_type_bounds(bounds, self).unwrap();
+ print_type_bounds(self.db, bounds, self).unwrap();
}
fn print_path(&mut self, path: &Path) {
- print_path(path, self).unwrap();
+ print_path(self.db, path, self).unwrap();
}
fn print_generic_params(&mut self, params: &GenericParams) {
@@ -493,7 +489,7 @@ impl<'a> Printer<'a> {
w!(self, ", ");
}
first = false;
- w!(self, "{}", lt.name);
+ w!(self, "{}", lt.name.display(self.db));
}
for (idx, x) in params.type_or_consts.iter() {
if !first {
@@ -502,11 +498,11 @@ impl<'a> Printer<'a> {
first = false;
match x {
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
- Some(name) => w!(self, "{}", name),
+ Some(name) => w!(self, "{}", name.display(self.db)),
None => w!(self, "_anon_{}", idx.into_raw()),
},
TypeOrConstParamData::ConstParamData(konst) => {
- w!(self, "const {}: ", konst.name);
+ w!(self, "const {}: ", konst.name.display(self.db));
self.print_type_ref(&konst.ty);
}
}
@@ -538,7 +534,12 @@ impl<'a> Printer<'a> {
let (target, bound) = match pred {
WherePredicate::TypeBound { target, bound } => (target, bound),
WherePredicate::Lifetime { target, bound } => {
- wln!(this, "{}: {},", target.name, bound.name);
+ wln!(
+ this,
+ "{}: {},",
+ target.name.display(self.db),
+ bound.name.display(self.db)
+ );
continue;
}
WherePredicate::ForLifetime { lifetimes, target, bound } => {
@@ -547,7 +548,7 @@ impl<'a> Printer<'a> {
if i != 0 {
w!(this, ", ");
}
- w!(this, "{}", lt);
+ w!(this, "{}", lt.display(self.db));
}
w!(this, "> ");
(target, bound)
@@ -558,7 +559,7 @@ impl<'a> Printer<'a> {
WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
WherePredicateTypeTarget::TypeOrConstParam(id) => {
match &params.type_or_consts[*id].name() {
- Some(name) => w!(this, "{}", name),
+ Some(name) => w!(this, "{}", name.display(self.db)),
None => w!(this, "_anon_{}", id.into_raw()),
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
index e30d9652b..5ded4b6b2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
@@ -6,7 +6,7 @@ use crate::{db::DefDatabase, test_db::TestDB};
fn check(ra_fixture: &str, expect: Expect) {
let (db, file_id) = TestDB::with_single_file(ra_fixture);
let item_tree = db.file_item_tree(file_id.into());
- let pretty = item_tree.pretty_print();
+ let pretty = item_tree.pretty_print(&db);
expect.assert_eq(&pretty);
}
@@ -165,7 +165,7 @@ trait Tr: SuperTrait + 'lifetime {
fn method(&self);
}
"#,
- expect![[r##"
+ expect![[r#"
pub static mut ST: () = _;
pub(self) const _: Anon = _;
@@ -174,8 +174,8 @@ trait Tr: SuperTrait + 'lifetime {
#[inner_attr_in_fn]
pub(self) fn f(
#[attr]
- arg: u8,
- _: (),
+ u8,
+ (),
) -> () { ... }
pub(self) trait Tr<Self>
@@ -186,10 +186,10 @@ trait Tr: SuperTrait + 'lifetime {
pub(self) type Assoc: AssocBound = Default;
pub(self) fn method(
- _: &Self, // self
+ self: &Self,
) -> ();
}
- "##]],
+ "#]],
);
}
@@ -336,7 +336,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
T: 'b
{
pub(self) fn f<G>(
- arg: impl Copy,
+ impl Copy,
) -> impl Copy
where
G: 'a { ... }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index d338bb412..0e9ac58fb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -2,14 +2,13 @@
//!
//! This attribute to tell the compiler about semi built-in std library
//! features, such as Fn family of traits.
-use std::sync::Arc;
-
use rustc_hash::FxHashMap;
use syntax::SmolStr;
+use triomphe::Arc;
use crate::{
- db::DefDatabase, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId, FunctionId,
- ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
+ db::DefDatabase, path::Path, AdtId, AssocItemId, AttrDefId, CrateId, EnumId, EnumVariantId,
+ FunctionId, ImplId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -200,7 +199,7 @@ pub enum GenericRequirement {
macro_rules! language_item_table {
(
- $( $(#[$attr:meta])* $variant:ident, $name:ident, $method:ident, $target:expr, $generics:expr; )*
+ $( $(#[$attr:meta])* $variant:ident, $module:ident :: $name:ident, $method:ident, $target:expr, $generics:expr; )*
) => {
/// A representation of all the valid language items in Rust.
@@ -221,11 +220,6 @@ macro_rules! language_item_table {
}
/// Opposite of [`LangItem::name`]
- pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
- Self::from_str(name.as_str()?)
- }
-
- /// Opposite of [`LangItem::name`]
pub fn from_str(name: &str) -> Option<Self> {
match name {
$( stringify!($name) => Some(LangItem::$variant), )*
@@ -236,84 +230,100 @@ macro_rules! language_item_table {
}
}
+impl LangItem {
+ /// Opposite of [`LangItem::name`]
+ pub fn from_name(name: &hir_expand::name::Name) -> Option<Self> {
+ Self::from_str(name.as_str()?)
+ }
+
+ pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
+ let t = db.lang_item(start_crate, *self)?;
+ Some(Path::LangItem(t))
+ }
+}
+
language_item_table! {
// Variant name, Name, Getter method name, Target Generic requirements;
- Sized, sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
- Unsize, unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
+ Sized, sym::sized, sized_trait, Target::Trait, GenericRequirement::Exact(0);
+ Unsize, sym::unsize, unsize_trait, Target::Trait, GenericRequirement::Minimum(1);
/// Trait injected by `#[derive(PartialEq)]`, (i.e. "Partial EQ").
- StructuralPeq, structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;
+ StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None;
/// Trait injected by `#[derive(Eq)]`, (i.e. "Total EQ"; no, I will not apologize).
- StructuralTeq, structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None;
- Copy, copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);
- Clone, clone, clone_trait, Target::Trait, GenericRequirement::None;
- Sync, sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
- DiscriminantKind, discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
+ StructuralTeq, sym::structural_teq, structural_teq_trait, Target::Trait, GenericRequirement::None;
+ Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0);
+ Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None;
+ Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0);
+ DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None;
/// The associated item of the [`DiscriminantKind`] trait.
- Discriminant, discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None;
+ Discriminant, sym::discriminant_type, discriminant_type, Target::AssocTy, GenericRequirement::None;
+
+ PointeeTrait, sym::pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
+ Metadata, sym::metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
+ DynMetadata, sym::dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
- PointeeTrait, pointee_trait, pointee_trait, Target::Trait, GenericRequirement::None;
- Metadata, metadata_type, metadata_type, Target::AssocTy, GenericRequirement::None;
- DynMetadata, dyn_metadata, dyn_metadata, Target::Struct, GenericRequirement::None;
+ Freeze, sym::freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
- Freeze, freeze, freeze_trait, Target::Trait, GenericRequirement::Exact(0);
+ FnPtrTrait, sym::fn_ptr_trait, fn_ptr_trait, Target::Trait, GenericRequirement::Exact(0);
+ FnPtrAddr, sym::fn_ptr_addr, fn_ptr_addr, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- Drop, drop, drop_trait, Target::Trait, GenericRequirement::None;
- Destruct, destruct, destruct_trait, Target::Trait, GenericRequirement::None;
+ Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None;
+ Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None;
- CoerceUnsized, coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
- DispatchFromDyn, dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
+ CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1);
+ DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1);
// language items relating to transmutability
- TransmuteOpts, transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
- TransmuteTrait, transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3);
-
- Add, add, add_trait, Target::Trait, GenericRequirement::Exact(1);
- Sub, sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
- Mul, mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);
- Div, div, div_trait, Target::Trait, GenericRequirement::Exact(1);
- Rem, rem, rem_trait, Target::Trait, GenericRequirement::Exact(1);
- Neg, neg, neg_trait, Target::Trait, GenericRequirement::Exact(0);
- Not, not, not_trait, Target::Trait, GenericRequirement::Exact(0);
- BitXor, bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1);
- BitAnd, bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1);
- BitOr, bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1);
- Shl, shl, shl_trait, Target::Trait, GenericRequirement::Exact(1);
- Shr, shr, shr_trait, Target::Trait, GenericRequirement::Exact(1);
- AddAssign, add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- SubAssign, sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- MulAssign, mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- DivAssign, div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- RemAssign, rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- BitXorAssign, bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- BitAndAssign, bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- BitOrAssign, bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- ShlAssign, shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- ShrAssign, shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1);
- Index, index, index_trait, Target::Trait, GenericRequirement::Exact(1);
- IndexMut, index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1);
-
- UnsafeCell, unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
- VaList, va_list, va_list, Target::Struct, GenericRequirement::None;
-
- Deref, deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
- DerefMut, deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
- DerefTarget, deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
- Receiver, receiver, receiver_trait, Target::Trait, GenericRequirement::None;
-
- Fn, fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
- FnMut, fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
- FnOnce, fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
-
- FnOnceOutput, fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
-
- Future, future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
- GeneratorState, generator_state, gen_state, Target::Enum, GenericRequirement::None;
- Generator, generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1);
- Unpin, unpin, unpin_trait, Target::Trait, GenericRequirement::None;
- Pin, pin, pin_type, Target::Struct, GenericRequirement::None;
-
- PartialEq, eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
- PartialOrd, partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
+ TransmuteOpts, sym::transmute_opts, transmute_opts, Target::Struct, GenericRequirement::Exact(0);
+ TransmuteTrait, sym::transmute_trait, transmute_trait, Target::Trait, GenericRequirement::Exact(3);
+
+ Add, sym::add, add_trait, Target::Trait, GenericRequirement::Exact(1);
+ Sub, sym::sub, sub_trait, Target::Trait, GenericRequirement::Exact(1);
+ Mul, sym::mul, mul_trait, Target::Trait, GenericRequirement::Exact(1);
+ Div, sym::div, div_trait, Target::Trait, GenericRequirement::Exact(1);
+ Rem, sym::rem, rem_trait, Target::Trait, GenericRequirement::Exact(1);
+ Neg, sym::neg, neg_trait, Target::Trait, GenericRequirement::Exact(0);
+ Not, sym::not, not_trait, Target::Trait, GenericRequirement::Exact(0);
+ BitXor, sym::bitxor, bitxor_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitAnd, sym::bitand, bitand_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitOr, sym::bitor, bitor_trait, Target::Trait, GenericRequirement::Exact(1);
+ Shl, sym::shl, shl_trait, Target::Trait, GenericRequirement::Exact(1);
+ Shr, sym::shr, shr_trait, Target::Trait, GenericRequirement::Exact(1);
+ AddAssign, sym::add_assign, add_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ SubAssign, sym::sub_assign, sub_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ MulAssign, sym::mul_assign, mul_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ DivAssign, sym::div_assign, div_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ RemAssign, sym::rem_assign, rem_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitXorAssign, sym::bitxor_assign, bitxor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitAndAssign, sym::bitand_assign, bitand_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ BitOrAssign, sym::bitor_assign, bitor_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ ShlAssign, sym::shl_assign, shl_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ ShrAssign, sym::shr_assign, shr_assign_trait, Target::Trait, GenericRequirement::Exact(1);
+ Index, sym::index, index_trait, Target::Trait, GenericRequirement::Exact(1);
+ IndexMut, sym::index_mut, index_mut_trait, Target::Trait, GenericRequirement::Exact(1);
+
+ UnsafeCell, sym::unsafe_cell, unsafe_cell_type, Target::Struct, GenericRequirement::None;
+ VaList, sym::va_list, va_list, Target::Struct, GenericRequirement::None;
+
+ Deref, sym::deref, deref_trait, Target::Trait, GenericRequirement::Exact(0);
+ DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
+ DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
+ Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None;
+
+ Fn, kw::fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
+ FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
+ FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
+
+ FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
+
+ Future, sym::future_trait, future_trait, Target::Trait, GenericRequirement::Exact(0);
+ GeneratorState, sym::generator_state, gen_state, Target::Enum, GenericRequirement::None;
+ Generator, sym::generator, gen_trait, Target::Trait, GenericRequirement::Minimum(1);
+ Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None;
+ Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None;
+
+ PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
+ PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
+ CVoid, sym::c_void, c_void, Target::Enum, GenericRequirement::None;
// A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and
// various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays.
@@ -322,92 +332,103 @@ language_item_table! {
// in the sense that a crate is not required to have it defined to use it, but a final product
// is required to define it somewhere. Additionally, there are restrictions on crates that use
// a weak lang item, but do not have it defined.
- Panic, panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
- PanicNounwind, panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0);
- PanicFmt, panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
- PanicDisplay, panic_display, panic_display, Target::Fn, GenericRequirement::None;
- ConstPanicFmt, const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
- PanicBoundsCheck, panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
- PanicInfo, panic_info, panic_info, Target::Struct, GenericRequirement::None;
- PanicLocation, panic_location, panic_location, Target::Struct, GenericRequirement::None;
- PanicImpl, panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
- PanicCannotUnwind, panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
+ Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
+ PanicNounwind, sym::panic_nounwind, panic_nounwind, Target::Fn, GenericRequirement::Exact(0);
+ PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
+ PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None;
+ ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
+ PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
+ PanicMisalignedPointerDereference, sym::panic_misaligned_pointer_dereference, panic_misaligned_pointer_dereference_fn, Target::Fn, GenericRequirement::Exact(0);
+ PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None;
+ PanicLocation, sym::panic_location, panic_location, Target::Struct, GenericRequirement::None;
+ PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
+ PanicCannotUnwind, sym::panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
/// libstd panic entry point. Necessary for const eval to be able to catch it
- BeginPanic, begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;
+ BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;
- ExchangeMalloc, exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
- BoxFree, box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
- DropInPlace, drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
- AllocLayout, alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
+ // Lang items needed for `format_args!()`.
+ FormatAlignment, sym::format_alignment, format_alignment, Target::Enum, GenericRequirement::None;
+ FormatArgument, sym::format_argument, format_argument, Target::Struct, GenericRequirement::None;
+ FormatArguments, sym::format_arguments, format_arguments, Target::Struct, GenericRequirement::None;
+ FormatCount, sym::format_count, format_count, Target::Enum, GenericRequirement::None;
+ FormatPlaceholder, sym::format_placeholder, format_placeholder, Target::Struct, GenericRequirement::None;
+ FormatUnsafeArg, sym::format_unsafe_arg, format_unsafe_arg, Target::Struct, GenericRequirement::None;
- Start, start, start_fn, Target::Fn, GenericRequirement::Exact(1);
+ ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
+ BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
+ DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
+ AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
- EhPersonality, eh_personality, eh_personality, Target::Fn, GenericRequirement::None;
- EhCatchTypeinfo, eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None;
+ Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
- OwnedBox, owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1);
+ EhPersonality, sym::eh_personality, eh_personality, Target::Fn, GenericRequirement::None;
+ EhCatchTypeinfo, sym::eh_catch_typeinfo, eh_catch_typeinfo, Target::Static, GenericRequirement::None;
- PhantomData, phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
+ OwnedBox, sym::owned_box, owned_box, Target::Struct, GenericRequirement::Minimum(1);
- ManuallyDrop, manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
+ PhantomData, sym::phantom_data, phantom_data, Target::Struct, GenericRequirement::Exact(1);
- MaybeUninit, maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
+ ManuallyDrop, sym::manually_drop, manually_drop, Target::Struct, GenericRequirement::None;
+
+ MaybeUninit, sym::maybe_uninit, maybe_uninit, Target::Union, GenericRequirement::None;
/// Align offset for stride != 1; must not panic.
- AlignOffset, align_offset, align_offset_fn, Target::Fn, GenericRequirement::None;
+ AlignOffset, sym::align_offset, align_offset_fn, Target::Fn, GenericRequirement::None;
- Termination, termination, termination, Target::Trait, GenericRequirement::None;
+ Termination, sym::termination, termination, Target::Trait, GenericRequirement::None;
- Try, Try, try_trait, Target::Trait, GenericRequirement::None;
+ Try, sym::Try, try_trait, Target::Trait, GenericRequirement::None;
- Tuple, tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0);
+ Tuple, sym::tuple_trait, tuple_trait, Target::Trait, GenericRequirement::Exact(0);
- SliceLen, slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
+ SliceLen, sym::slice_len_fn, slice_len_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
// Language items from AST lowering
- TryTraitFromResidual, from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- TryTraitFromOutput, from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- TryTraitBranch, branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- TryTraitFromYeet, from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
+ TryTraitFromResidual, sym::from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
+
+ PointerLike, sym::pointer_like, pointer_like, Target::Trait, GenericRequirement::Exact(0);
- PointerSized, pointer_sized, pointer_sized, Target::Trait, GenericRequirement::Exact(0);
+ ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0);
- Poll, Poll, poll, Target::Enum, GenericRequirement::None;
- PollReady, Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
- PollPending, Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
+ Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None;
+ PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
+ PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
// FIXME(swatinem): the following lang items are used for async lowering and
// should become obsolete eventually.
- ResumeTy, ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
- GetContext, get_context, get_context_fn, Target::Fn, GenericRequirement::None;
-
- Context, Context, context, Target::Struct, GenericRequirement::None;
- FuturePoll, poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
+ GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None;
- FromFrom, from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ Context, sym::Context, context, Target::Struct, GenericRequirement::None;
+ FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- OptionSome, Some, option_some_variant, Target::Variant, GenericRequirement::None;
- OptionNone, None, option_none_variant, Target::Variant, GenericRequirement::None;
+ Option, sym::Option, option_type, Target::Enum, GenericRequirement::None;
+ OptionSome, sym::Some, option_some_variant, Target::Variant, GenericRequirement::None;
+ OptionNone, sym::None, option_none_variant, Target::Variant, GenericRequirement::None;
- ResultOk, Ok, result_ok_variant, Target::Variant, GenericRequirement::None;
- ResultErr, Err, result_err_variant, Target::Variant, GenericRequirement::None;
+ ResultOk, sym::Ok, result_ok_variant, Target::Variant, GenericRequirement::None;
+ ResultErr, sym::Err, result_err_variant, Target::Variant, GenericRequirement::None;
- ControlFlowContinue, Continue, cf_continue_variant, Target::Variant, GenericRequirement::None;
- ControlFlowBreak, Break, cf_break_variant, Target::Variant, GenericRequirement::None;
+ ControlFlowContinue, sym::Continue, cf_continue_variant, Target::Variant, GenericRequirement::None;
+ ControlFlowBreak, sym::Break, cf_break_variant, Target::Variant, GenericRequirement::None;
- IntoFutureIntoFuture, into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- IntoIterIntoIter, into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
- IteratorNext, next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
+ IntoFutureIntoFuture, sym::into_future, into_future_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false}), GenericRequirement::None;
- PinNewUnchecked, new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
+ PinNewUnchecked, sym::new_unchecked, new_unchecked_fn, Target::Method(MethodKind::Inherent), GenericRequirement::None;
- RangeFrom, RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None;
- RangeFull, RangeFull, range_full_struct, Target::Struct, GenericRequirement::None;
- RangeInclusiveStruct, RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None;
- RangeInclusiveNew, range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None;
- Range, Range, range_struct, Target::Struct, GenericRequirement::None;
- RangeToInclusive, RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
- RangeTo, RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
+ RangeFrom, sym::RangeFrom, range_from_struct, Target::Struct, GenericRequirement::None;
+ RangeFull, sym::RangeFull, range_full_struct, Target::Struct, GenericRequirement::None;
+ RangeInclusiveStruct, sym::RangeInclusive, range_inclusive_struct, Target::Struct, GenericRequirement::None;
+ RangeInclusiveNew, sym::range_inclusive_new, range_inclusive_new_method, Target::Method(MethodKind::Inherent), GenericRequirement::None;
+ Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
+ RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
+ RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
- String, String, string, Target::Struct, GenericRequirement::None;
+ String, sym::String, string, Target::Struct, GenericRequirement::None;
+ CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None;
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/layout.rs b/src/tools/rust-analyzer/crates/hir-def/src/layout.rs
deleted file mode 100644
index 49b1190ad..000000000
--- a/src/tools/rust-analyzer/crates/hir-def/src/layout.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-//! Definitions needed for computing data layout of types.
-
-use std::cmp;
-
-use la_arena::{Idx, RawIdx};
-pub use rustc_abi::{
- Abi, AbiAndPrefAlign, AddressSpace, Align, Endian, FieldsShape, Integer, IntegerType,
- LayoutCalculator, Niche, Primitive, ReprFlags, ReprOptions, Scalar, Size, StructKind,
- TargetDataLayout, TargetDataLayoutErrors, WrappingRange,
-};
-
-use crate::LocalEnumVariantId;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct RustcEnumVariantIdx(pub LocalEnumVariantId);
-
-impl rustc_index::vec::Idx for RustcEnumVariantIdx {
- fn new(idx: usize) -> Self {
- RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32)))
- }
-
- fn index(self) -> usize {
- u32::from(self.0.into_raw()) as usize
- }
-}
-
-pub type Layout = rustc_abi::LayoutS<RustcEnumVariantIdx>;
-pub type TagEncoding = rustc_abi::TagEncoding<RustcEnumVariantIdx>;
-pub type Variants = rustc_abi::Variants<RustcEnumVariantIdx>;
-
-pub trait IntegerExt {
- fn repr_discr(
- dl: &TargetDataLayout,
- repr: &ReprOptions,
- min: i128,
- max: i128,
- ) -> Result<(Integer, bool), LayoutError>;
-}
-
-impl IntegerExt for Integer {
- /// Finds the appropriate Integer type and signedness for the given
- /// signed discriminant range and `#[repr]` attribute.
- /// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
- /// that shouldn't affect anything, other than maybe debuginfo.
- fn repr_discr(
- dl: &TargetDataLayout,
- repr: &ReprOptions,
- min: i128,
- max: i128,
- ) -> Result<(Integer, bool), LayoutError> {
- // Theoretically, negative values could be larger in unsigned representation
- // than the unsigned representation of the signed minimum. However, if there
- // are any negative values, the only valid unsigned representation is u128
- // which can fit all i128 values, so the result remains unaffected.
- let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
- let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
-
- if let Some(ity) = repr.int {
- let discr = Integer::from_attr(dl, ity);
- let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
- if discr < fit {
- return Err(LayoutError::UserError(
- "Integer::repr_discr: `#[repr]` hint too small for \
- discriminant range of enum "
- .to_string(),
- ));
- }
- return Ok((discr, ity.is_signed()));
- }
-
- let at_least = if repr.c() {
- // This is usually I32, however it can be different on some platforms,
- // notably hexagon and arm-none/thumb-none
- dl.c_enum_min_size
- } else {
- // repr(Rust) enums try to be as small as possible
- Integer::I8
- };
-
- // If there are no negative values, we can use the unsigned fit.
- Ok(if min >= 0 {
- (cmp::max(unsigned_fit, at_least), false)
- } else {
- (cmp::max(signed_fit, at_least), true)
- })
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum LayoutError {
- UserError(String),
- SizeOverflow,
- TargetLayoutNotAvailable,
- HasPlaceholder,
- NotImplemented,
- Unknown,
-}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 8c2e93f09..9d8b57a0d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -18,24 +18,23 @@ pub mod db;
pub mod attr;
pub mod path;
-pub mod type_ref;
pub mod builtin_type;
-pub mod builtin_attr;
pub mod per_ns;
pub mod item_scope;
+pub mod lower;
+pub mod expander;
+
pub mod dyn_map;
-pub mod keys;
pub mod item_tree;
-pub mod adt;
pub mod data;
pub mod generics;
pub mod lang_item;
-pub mod layout;
-pub mod expr;
+pub mod hir;
+pub use self::hir::type_ref;
pub mod body;
pub mod resolver;
@@ -49,6 +48,9 @@ pub mod visibility;
pub mod find_path;
pub mod import_map;
+pub use rustc_abi as layout;
+use triomphe::Arc;
+
#[cfg(test)]
mod test_db;
#[cfg(test)]
@@ -57,7 +59,7 @@ mod pretty;
use std::{
hash::{Hash, Hasher},
- sync::Arc,
+ panic::{RefUnwindSafe, UnwindSafe},
};
use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind};
@@ -67,11 +69,12 @@ use hir_expand::{
builtin_attr_macro::BuiltinAttrExpander,
builtin_derive_macro::BuiltinDeriveExpander,
builtin_fn_macro::{BuiltinFnLikeExpander, EagerExpander},
- eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
+ db::ExpandDatabase,
+ eager::expand_eager_macro_input,
hygiene::Hygiene,
proc_macro::ProcMacroExpander,
- AstId, ExpandError, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
- MacroDefKind, UnresolvedMacro,
+ AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
+ MacroDefId, MacroDefKind, UnresolvedMacro,
};
use item_tree::ExternBlock;
use la_arena::Idx;
@@ -82,14 +85,54 @@ use syntax::ast;
use ::tt::token_id as tt;
use crate::{
- adt::VariantData,
builtin_type::BuiltinType,
+ data::adt::VariantData,
item_tree::{
- Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, ModItem,
- Static, Struct, Trait, TraitAlias, TypeAlias, Union,
+ Const, Enum, Function, Impl, ItemTreeId, ItemTreeNode, MacroDef, MacroRules, Static,
+ Struct, Trait, TraitAlias, TypeAlias, Union,
},
};
+/// A `ModuleId` that is always a crate's root module.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct CrateRootModuleId {
+ krate: CrateId,
+}
+
+impl CrateRootModuleId {
+ pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
+ db.crate_def_map(self.krate)
+ }
+
+ pub fn krate(self) -> CrateId {
+ self.krate
+ }
+}
+
+impl From<CrateRootModuleId> for ModuleId {
+ fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
+ ModuleId { krate, block: None, local_id: DefMap::ROOT }
+ }
+}
+
+impl From<CrateRootModuleId> for ModuleDefId {
+ fn from(value: CrateRootModuleId) -> Self {
+ ModuleDefId::ModuleId(value.into())
+ }
+}
+
+impl TryFrom<ModuleId> for CrateRootModuleId {
+ type Error = ();
+
+ fn try_from(ModuleId { krate, block, local_id }: ModuleId) -> Result<Self, Self::Error> {
+ if block.is_none() && local_id == DefMap::ROOT {
+ Ok(CrateRootModuleId { krate })
+ } else {
+ Err(())
+ }
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ModuleId {
krate: CrateId,
@@ -104,13 +147,7 @@ pub struct ModuleId {
impl ModuleId {
pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> {
match self.block {
- Some(block) => {
- db.block_def_map(block).unwrap_or_else(|| {
- // NOTE: This should be unreachable - all `ModuleId`s come from their `DefMap`s,
- // so the `DefMap` here must exist.
- unreachable!("no `block_def_map` for `ModuleId` {:?}", self);
- })
- }
+ Some(block) => db.block_def_map(block),
None => db.crate_def_map(self.krate),
}
}
@@ -236,7 +273,7 @@ pub struct EnumVariantId {
pub local_id: LocalEnumVariantId,
}
-pub type LocalEnumVariantId = Idx<adt::EnumVariantData>;
+pub type LocalEnumVariantId = Idx<data::adt::EnumVariantData>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FieldId {
@@ -244,7 +281,7 @@ pub struct FieldId {
pub local_id: LocalFieldId,
}
-pub type LocalFieldId = Idx<adt::FieldData>;
+pub type LocalFieldId = Idx<data::adt::FieldData>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ConstId(salsa::InternId);
@@ -317,8 +354,7 @@ impl_intern!(MacroRulesId, MacroRulesLoc, intern_macro_rules, lookup_intern_macr
pub struct ProcMacroId(salsa::InternId);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ProcMacroLoc {
- // FIXME: this should be a crate? or just a crate-root module
- pub container: ModuleId,
+ pub container: CrateRootModuleId,
pub id: ItemTreeId<Function>,
pub expander: ProcMacroExpander,
pub kind: ProcMacroKind,
@@ -478,16 +514,228 @@ impl_from!(
for ModuleDefId
);
+/// Id of the anonymous const block expression and patterns. This is very similar to `ClosureId` and
+/// shouldn't be a `DefWithBodyId` since its type inference is dependent on its parent.
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub struct ConstBlockId(salsa::InternId);
+impl_intern!(ConstBlockId, ConstBlockLoc, intern_anonymous_const, lookup_intern_anonymous_const);
+
+#[derive(Debug, Hash, PartialEq, Eq, Clone)]
+pub struct ConstBlockLoc {
+ /// The parent of the anonymous const block.
+ pub parent: DefWithBodyId,
+ /// The root expression of this const block in the parent body.
+ pub root: hir::ExprId,
+}
+
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub enum TypeOwnerId {
+ FunctionId(FunctionId),
+ StaticId(StaticId),
+ ConstId(ConstId),
+ InTypeConstId(InTypeConstId),
+ AdtId(AdtId),
+ TraitId(TraitId),
+ TraitAliasId(TraitAliasId),
+ TypeAliasId(TypeAliasId),
+ ImplId(ImplId),
+ EnumVariantId(EnumVariantId),
+ // FIXME(const-generic-body): ModuleId should not be a type owner. This needs to be fixed to make `TypeOwnerId` actually
+ // useful for assigning ids to in type consts.
+ ModuleId(ModuleId),
+}
+
+impl TypeOwnerId {
+ fn as_generic_def_id(self) -> Option<GenericDefId> {
+ Some(match self {
+ TypeOwnerId::FunctionId(x) => GenericDefId::FunctionId(x),
+ TypeOwnerId::ConstId(x) => GenericDefId::ConstId(x),
+ TypeOwnerId::AdtId(x) => GenericDefId::AdtId(x),
+ TypeOwnerId::TraitId(x) => GenericDefId::TraitId(x),
+ TypeOwnerId::TraitAliasId(x) => GenericDefId::TraitAliasId(x),
+ TypeOwnerId::TypeAliasId(x) => GenericDefId::TypeAliasId(x),
+ TypeOwnerId::ImplId(x) => GenericDefId::ImplId(x),
+ TypeOwnerId::EnumVariantId(x) => GenericDefId::EnumVariantId(x),
+ TypeOwnerId::InTypeConstId(_) | TypeOwnerId::ModuleId(_) | TypeOwnerId::StaticId(_) => {
+ return None
+ }
+ })
+ }
+}
+
+impl_from!(
+ FunctionId,
+ StaticId,
+ ConstId,
+ InTypeConstId,
+ AdtId,
+ TraitId,
+ TraitAliasId,
+ TypeAliasId,
+ ImplId,
+ EnumVariantId,
+ ModuleId
+ for TypeOwnerId
+);
+
+// Every `DefWithBodyId` is a type owner, since bodies can contain type (e.g. `{ let x: Type = _; }`)
+impl From<DefWithBodyId> for TypeOwnerId {
+ fn from(value: DefWithBodyId) -> Self {
+ match value {
+ DefWithBodyId::FunctionId(x) => x.into(),
+ DefWithBodyId::StaticId(x) => x.into(),
+ DefWithBodyId::ConstId(x) => x.into(),
+ DefWithBodyId::InTypeConstId(x) => x.into(),
+ DefWithBodyId::VariantId(x) => x.into(),
+ }
+ }
+}
+
+impl From<GenericDefId> for TypeOwnerId {
+ fn from(value: GenericDefId) -> Self {
+ match value {
+ GenericDefId::FunctionId(x) => x.into(),
+ GenericDefId::AdtId(x) => x.into(),
+ GenericDefId::TraitId(x) => x.into(),
+ GenericDefId::TraitAliasId(x) => x.into(),
+ GenericDefId::TypeAliasId(x) => x.into(),
+ GenericDefId::ImplId(x) => x.into(),
+ GenericDefId::EnumVariantId(x) => x.into(),
+ GenericDefId::ConstId(x) => x.into(),
+ }
+ }
+}
+
+// FIXME: This should not be a thing
+/// A thing that we want to store in interned ids, but we don't know its type in `hir-def`. This is
+/// currently only used in `InTypeConstId` for storing the type (which has type `Ty` defined in
+/// the `hir-ty` crate) of the constant in its id, which is a temporary hack so we may want
+/// to remove this after removing that.
+pub trait OpaqueInternableThing:
+ std::any::Any + std::fmt::Debug + Sync + Send + UnwindSafe + RefUnwindSafe
+{
+ fn as_any(&self) -> &dyn std::any::Any;
+ fn box_any(&self) -> Box<dyn std::any::Any>;
+ fn dyn_hash(&self, state: &mut dyn Hasher);
+ fn dyn_eq(&self, other: &dyn OpaqueInternableThing) -> bool;
+ fn dyn_clone(&self) -> Box<dyn OpaqueInternableThing>;
+}
+
+impl Hash for dyn OpaqueInternableThing {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.dyn_hash(state);
+ }
+}
+
+impl PartialEq for dyn OpaqueInternableThing {
+ fn eq(&self, other: &Self) -> bool {
+ self.dyn_eq(other)
+ }
+}
+
+impl Eq for dyn OpaqueInternableThing {}
+
+impl Clone for Box<dyn OpaqueInternableThing> {
+ fn clone(&self) -> Self {
+ self.dyn_clone()
+ }
+}
+
+// FIXME(const-generic-body): Use an stable id for in type consts.
+//
+// The current id uses `AstId<ast::ConstArg>` which will be changed by every change in the code. Ideally
+// we should use an id which is relative to the type owner, so that every change will only invalidate the
+// id if it happens inside of the type owner.
+//
+// The solution probably is to have some query on `TypeOwnerId` to traverse its constant children and store
+// their `AstId` in a list (vector or arena), and use the index of that list in the id here. That query probably
+// needs name resolution, and might go far and handles the whole path lowering or type lowering for a `TypeOwnerId`.
+//
+// Whatever path the solution takes, it should answer 3 questions at the same time:
+// * Is the id stable enough?
+// * How to find a constant id using an ast node / position in the source code? This is needed when we want to
+// provide ide functionalities inside an in type const (which we currently don't support) e.g. go to definition
+// for a local defined there. A complex id might have some trouble in this reverse mapping.
+// * How to find the return type of a constant using its id? We have this data when we are doing type lowering
+// and the name of the struct that contains this constant is resolved, so a query that only traverses the
+// type owner by its syntax tree might have a hard time here.
+
+/// A constant in a type as a substitution for const generics (like `Foo<{ 2 + 2 }>`) or as an array
+/// length (like `[u8; 2 + 2]`). These constants are body owner and are a variant of `DefWithBodyId`. These
+/// are not called `AnonymousConstId` to prevent confusion with [`ConstBlockId`].
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
+pub struct InTypeConstId(salsa::InternId);
+impl_intern!(InTypeConstId, InTypeConstLoc, intern_in_type_const, lookup_intern_in_type_const);
+
+#[derive(Debug, Hash, Eq, Clone)]
+pub struct InTypeConstLoc {
+ pub id: AstId<ast::ConstArg>,
+ /// The thing this const arg appears in
+ pub owner: TypeOwnerId,
+ pub thing: Box<dyn OpaqueInternableThing>,
+}
+
+impl PartialEq for InTypeConstLoc {
+ fn eq(&self, other: &Self) -> bool {
+ self.id == other.id && self.owner == other.owner && &*self.thing == &*other.thing
+ }
+}
+
+impl InTypeConstId {
+ pub fn source(&self, db: &dyn db::DefDatabase) -> ast::ConstArg {
+ let src = self.lookup(db).id;
+ let file_id = src.file_id;
+ let root = &db.parse_or_expand(file_id);
+ db.ast_id_map(file_id).get(src.value).to_node(root)
+ }
+}
+
+/// A constant, which might appears as a const item, an annonymous const block in expressions
+/// or patterns, or as a constant in types with const generics.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum GeneralConstId {
+ ConstId(ConstId),
+ ConstBlockId(ConstBlockId),
+ InTypeConstId(InTypeConstId),
+}
+
+impl_from!(ConstId, ConstBlockId, InTypeConstId for GeneralConstId);
+
+impl GeneralConstId {
+ pub fn generic_def(self, db: &dyn db::DefDatabase) -> Option<GenericDefId> {
+ match self {
+ GeneralConstId::ConstId(it) => Some(it.into()),
+ GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(),
+ GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(),
+ }
+ }
+
+ pub fn name(self, db: &dyn db::DefDatabase) -> String {
+ match self {
+ GeneralConstId::ConstId(const_id) => db
+ .const_data(const_id)
+ .name
+ .as_ref()
+ .and_then(|x| x.as_str())
+ .unwrap_or("_")
+ .to_owned(),
+ GeneralConstId::ConstBlockId(id) => format!("{{anonymous const {id:?}}}"),
+ GeneralConstId::InTypeConstId(id) => format!("{{in type const {id:?}}}"),
+ }
+ }
+}
+
/// The defs which have a body.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DefWithBodyId {
FunctionId(FunctionId),
StaticId(StaticId),
ConstId(ConstId),
+ InTypeConstId(InTypeConstId),
VariantId(EnumVariantId),
}
-impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId);
+impl_from!(FunctionId, ConstId, StaticId, InTypeConstId for DefWithBodyId);
impl From<EnumVariantId> for DefWithBodyId {
fn from(id: EnumVariantId) -> Self {
@@ -502,6 +750,9 @@ impl DefWithBodyId {
DefWithBodyId::StaticId(_) => None,
DefWithBodyId::ConstId(c) => Some(c.into()),
DefWithBodyId::VariantId(c) => Some(c.into()),
+ // FIXME: stable rust doesn't allow generics in constants, but we should
+ // use `TypeOwnerId::as_generic_def_id` when it does.
+ DefWithBodyId::InTypeConstId(_) => None,
}
}
}
@@ -691,29 +942,37 @@ impl HasModule for MacroId {
match self {
MacroId::MacroRulesId(it) => it.lookup(db).container,
MacroId::Macro2Id(it) => it.lookup(db).container,
- MacroId::ProcMacroId(it) => it.lookup(db).container,
+ MacroId::ProcMacroId(it) => it.lookup(db).container.into(),
}
}
}
-impl HasModule for DefWithBodyId {
+impl HasModule for TypeOwnerId {
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
match self {
- DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
- DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
- DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
- DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
+ TypeOwnerId::FunctionId(x) => x.lookup(db).module(db),
+ TypeOwnerId::StaticId(x) => x.lookup(db).module(db),
+ TypeOwnerId::ConstId(x) => x.lookup(db).module(db),
+ TypeOwnerId::InTypeConstId(x) => x.lookup(db).owner.module(db),
+ TypeOwnerId::AdtId(x) => x.module(db),
+ TypeOwnerId::TraitId(x) => x.lookup(db).container,
+ TypeOwnerId::TraitAliasId(x) => x.lookup(db).container,
+ TypeOwnerId::TypeAliasId(x) => x.lookup(db).module(db),
+ TypeOwnerId::ImplId(x) => x.lookup(db).container,
+ TypeOwnerId::EnumVariantId(x) => x.parent.lookup(db).container,
+ TypeOwnerId::ModuleId(x) => *x,
}
}
}
-impl DefWithBodyId {
- pub fn as_mod_item(self, db: &dyn db::DefDatabase) -> ModItem {
+impl HasModule for DefWithBodyId {
+ fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
match self {
- DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
- DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
- DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
- DefWithBodyId::VariantId(it) => it.parent.lookup(db).id.value.into(),
+ DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
+ DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
+ DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
+ DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
+ DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db),
}
}
}
@@ -799,52 +1058,43 @@ impl AttrDefId {
pub trait AsMacroCall {
fn as_call_id(
&self,
- db: &dyn db::DefDatabase,
+ db: &dyn ExpandDatabase,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
) -> Option<MacroCallId> {
- self.as_call_id_with_errors(db, krate, resolver, &mut |_| ()).ok()?.ok()
+ self.as_call_id_with_errors(db, krate, resolver).ok()?.value
}
fn as_call_id_with_errors(
&self,
- db: &dyn db::DefDatabase,
+ db: &dyn ExpandDatabase,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
- error_sink: &mut dyn FnMut(ExpandError),
- ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro>;
+ ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro>;
}
impl AsMacroCall for InFile<&ast::MacroCall> {
fn as_call_id_with_errors(
&self,
- db: &dyn db::DefDatabase,
+ db: &dyn ExpandDatabase,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
- mut error_sink: &mut dyn FnMut(ExpandError),
- ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
+ ) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
- let h = Hygiene::new(db.upcast(), self.file_id);
- let path =
- self.value.path().and_then(|path| path::ModPath::from_src(db.upcast(), path, &h));
-
- let path = match error_sink
- .option(path, || ExpandError::Other("malformed macro invocation".into()))
- {
- Ok(path) => path,
- Err(error) => {
- return Ok(Err(error));
- }
+ let h = Hygiene::new(db, self.file_id);
+ let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
+
+ let Some(path) = path else {
+ return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation")));
};
- macro_call_as_call_id(
+ macro_call_as_call_id_(
db,
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
expands_to,
krate,
resolver,
- error_sink,
)
}
}
@@ -863,26 +1113,37 @@ impl<T: ast::AstNode> AstIdWithPath<T> {
}
fn macro_call_as_call_id(
- db: &dyn db::DefDatabase,
+ db: &dyn ExpandDatabase,
call: &AstIdWithPath<ast::MacroCall>,
expand_to: ExpandTo,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
- error_sink: &mut dyn FnMut(ExpandError),
-) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
+) -> Result<Option<MacroCallId>, UnresolvedMacro> {
+ macro_call_as_call_id_(db, call, expand_to, krate, resolver).map(|res| res.value)
+}
+
+fn macro_call_as_call_id_(
+ db: &dyn ExpandDatabase,
+ call: &AstIdWithPath<ast::MacroCall>,
+ expand_to: ExpandTo,
+ krate: CrateId,
+ resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
+) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
let def =
resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?;
let res = if let MacroDefKind::BuiltInEager(..) = def.kind {
- let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast()));
-
- expand_eager_macro(db.upcast(), krate, macro_call, def, &resolver, error_sink)?
+ let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db));
+ expand_eager_macro_input(db, krate, macro_call, def, &resolver)?
} else {
- Ok(def.as_lazy_macro(
- db.upcast(),
- krate,
- MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
- ))
+ ExpandResult {
+ value: Some(def.as_lazy_macro(
+ db,
+ krate,
+ MacroCallKind::FnLike { ast_id: call.ast_id, expand_to },
+ )),
+ err: None,
+ }
};
Ok(res)
}
@@ -986,16 +1247,15 @@ fn attr_macro_as_call_id(
macro_attr: &Attr,
krate: CrateId,
def: MacroDefId,
- is_derive: bool,
) -> MacroCallId {
let arg = match macro_attr.input.as_deref() {
- Some(AttrInput::TokenTree(tt, map)) => (
+ Some(AttrInput::TokenTree(tt)) => (
{
- let mut tt = tt.clone();
+ let mut tt = tt.0.clone();
tt.delimiter = tt::Delimiter::UNSPECIFIED;
tt
},
- map.clone(),
+ tt.1.clone(),
),
_ => (tt::Subtree::empty(), Default::default()),
};
@@ -1007,7 +1267,6 @@ fn attr_macro_as_call_id(
ast_id: item_attr.ast_id,
attr_args: Arc::new(arg),
invoc_attr_index: macro_attr.id,
- is_derive,
},
)
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
new file mode 100644
index 000000000..af623fd0e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
@@ -0,0 +1,45 @@
+//! Context for lowering paths.
+use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile};
+use once_cell::unsync::OnceCell;
+use syntax::ast;
+use triomphe::Arc;
+
+use crate::{db::DefDatabase, path::Path};
+
+pub struct LowerCtx<'a> {
+ pub db: &'a dyn DefDatabase,
+ hygiene: Hygiene,
+ ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
+}
+
+impl<'a> LowerCtx<'a> {
+ pub fn new(db: &'a dyn DefDatabase, hygiene: &Hygiene, file_id: HirFileId) -> Self {
+ LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: Some((file_id, OnceCell::new())) }
+ }
+
+ pub fn with_file_id(db: &'a dyn DefDatabase, file_id: HirFileId) -> Self {
+ LowerCtx {
+ db,
+ hygiene: Hygiene::new(db.upcast(), file_id),
+ ast_id_map: Some((file_id, OnceCell::new())),
+ }
+ }
+
+ pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: &Hygiene) -> Self {
+ LowerCtx { db, hygiene: hygiene.clone(), ast_id_map: None }
+ }
+
+ pub(crate) fn hygiene(&self) -> &Hygiene {
+ &self.hygiene
+ }
+
+ pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
+ Path::from_src(ast, self)
+ }
+
+ pub(crate) fn ast_id<N: syntax::AstNode>(&self, item: &N) -> Option<AstId<N>> {
+ let &(file_id, ref ast_id_map) = self.ast_id_map.as_ref()?;
+ let ast_id_map = ast_id_map.get_or_init(|| self.db.ast_id_map(file_id));
+ Some(InFile::new(file_id, ast_id_map.ast_id(item)))
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
index fafcde25a..f41f97190 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -16,7 +16,7 @@ struct Foo;
#[derive(Copy)]
struct Foo;
-impl < > core::marker::Copy for Foo< > {}"#]],
+impl < > core::marker::Copy for Foo< > where {}"#]],
);
}
@@ -41,7 +41,7 @@ macro Copy {}
#[derive(Copy)]
struct Foo;
-impl < > crate ::marker::Copy for Foo< > {}"#]],
+impl < > crate ::marker::Copy for Foo< > where {}"#]],
);
}
@@ -57,7 +57,7 @@ struct Foo<A, B>;
#[derive(Copy)]
struct Foo<A, B>;
-impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
+impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
);
}
@@ -74,7 +74,7 @@ struct Foo<A, B, 'a, 'b>;
#[derive(Copy)]
struct Foo<A, B, 'a, 'b>;
-impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
+impl <A: core::marker::Copy, B: core::marker::Copy, > core::marker::Copy for Foo<A, B, > where {}"#]],
);
}
@@ -84,13 +84,93 @@ fn test_clone_expand() {
r#"
//- minicore: derive, clone
#[derive(Clone)]
-struct Foo<A, B>;
+enum Command<A, B> {
+ Move { x: A, y: B },
+ Do(&'static str),
+ Jump,
+}
"#,
expect![[r#"
#[derive(Clone)]
-struct Foo<A, B>;
+enum Command<A, B> {
+ Move { x: A, y: B },
+ Do(&'static str),
+ Jump,
+}
+
+impl <A: core::clone::Clone, B: core::clone::Clone, > core::clone::Clone for Command<A, B, > where {
+ fn clone(&self ) -> Self {
+ match self {
+ Command::Move {
+ x: x, y: y,
+ }
+ =>Command::Move {
+ x: x.clone(), y: y.clone(),
+ }
+ , Command::Do(f0, )=>Command::Do(f0.clone(), ), Command::Jump=>Command::Jump,
+ }
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_clone_expand_with_associated_types() {
+ check(
+ r#"
+//- minicore: derive, clone
+trait Trait {
+ type InWc;
+ type InFieldQualified;
+ type InFieldShorthand;
+ type InGenericArg;
+}
+trait Marker {}
+struct Vec<T>(T);
+
+#[derive(Clone)]
+struct Foo<T: Trait>
+where
+ <T as Trait>::InWc: Marker,
+{
+ qualified: <T as Trait>::InFieldQualified,
+ shorthand: T::InFieldShorthand,
+ generic: Vec<T::InGenericArg>,
+}
+"#,
+ expect![[r#"
+trait Trait {
+ type InWc;
+ type InFieldQualified;
+ type InFieldShorthand;
+ type InGenericArg;
+}
+trait Marker {}
+struct Vec<T>(T);
+
+#[derive(Clone)]
+struct Foo<T: Trait>
+where
+ <T as Trait>::InWc: Marker,
+{
+ qualified: <T as Trait>::InFieldQualified,
+ shorthand: T::InFieldShorthand,
+ generic: Vec<T::InGenericArg>,
+}
-impl <T0: core::clone::Clone, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
+impl <T: core::clone::Clone, > core::clone::Clone for Foo<T, > where T: Trait, T::InFieldShorthand: core::clone::Clone, T::InGenericArg: core::clone::Clone, {
+ fn clone(&self ) -> Self {
+ match self {
+ Foo {
+ qualified: qualified, shorthand: shorthand, generic: generic,
+ }
+ =>Foo {
+ qualified: qualified.clone(), shorthand: shorthand.clone(), generic: generic.clone(),
+ }
+ ,
+ }
+ }
+}"#]],
);
}
@@ -106,6 +186,270 @@ struct Foo<const X: usize, T>(u32);
#[derive(Clone)]
struct Foo<const X: usize, T>(u32);
-impl <const T0: usize, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
+impl <const X: usize, T: core::clone::Clone, > core::clone::Clone for Foo<X, T, > where {
+ fn clone(&self ) -> Self {
+ match self {
+ Foo(f0, )=>Foo(f0.clone(), ),
+ }
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_default_expand() {
+ check(
+ r#"
+//- minicore: derive, default
+#[derive(Default)]
+struct Foo {
+ field1: i32,
+ field2: (),
+}
+#[derive(Default)]
+enum Bar {
+ Foo(u8),
+ #[default]
+ Bar,
+}
+"#,
+ expect![[r#"
+#[derive(Default)]
+struct Foo {
+ field1: i32,
+ field2: (),
+}
+#[derive(Default)]
+enum Bar {
+ Foo(u8),
+ #[default]
+ Bar,
+}
+
+impl < > core::default::Default for Foo< > where {
+ fn default() -> Self {
+ Foo {
+ field1: core::default::Default::default(), field2: core::default::Default::default(),
+ }
+ }
+}
+impl < > core::default::Default for Bar< > where {
+ fn default() -> Self {
+ Bar::Bar
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_partial_eq_expand() {
+ check(
+ r#"
+//- minicore: derive, eq
+#[derive(PartialEq, Eq)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+"#,
+ expect![[r#"
+#[derive(PartialEq, Eq)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+
+impl < > core::cmp::PartialEq for Command< > where {
+ fn eq(&self , other: &Self ) -> bool {
+ match (self , other) {
+ (Command::Move {
+ x: x_self, y: y_self,
+ }
+ , Command::Move {
+ x: x_other, y: y_other,
+ }
+ )=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false
+ }
+ }
+}
+impl < > core::cmp::Eq for Command< > where {}"#]],
+ );
+}
+
+#[test]
+fn test_partial_ord_expand() {
+ check(
+ r#"
+//- minicore: derive, ord
+#[derive(PartialOrd, Ord)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+"#,
+ expect![[r#"
+#[derive(PartialOrd, Ord)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+
+impl < > core::cmp::PartialOrd for Command< > where {
+ fn partial_cmp(&self , other: &Self ) -> core::option::Option::Option<core::cmp::Ordering> {
+ match core::intrinsics::discriminant_value(self ).partial_cmp(&core::intrinsics::discriminant_value(other)) {
+ core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ match (self , other) {
+ (Command::Move {
+ x: x_self, y: y_self,
+ }
+ , Command::Move {
+ x: x_other, y: y_other,
+ }
+ )=>match x_self.partial_cmp(&x_other) {
+ core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ match y_self.partial_cmp(&y_other) {
+ core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ core::option::Option::Some(core::cmp::Ordering::Equal)
+ }
+ c=>return c,
+ }
+ }
+ c=>return c,
+ }
+ , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) {
+ core::option::Option::Some(core::cmp::Ordering::Equal)=> {
+ core::option::Option::Some(core::cmp::Ordering::Equal)
+ }
+ c=>return c,
+ }
+ , (Command::Jump, Command::Jump)=>core::option::Option::Some(core::cmp::Ordering::Equal), _unused=>core::option::Option::Some(core::cmp::Ordering::Equal)
+ }
+ }
+ c=>return c,
+ }
+ }
+}
+impl < > core::cmp::Ord for Command< > where {
+ fn cmp(&self , other: &Self ) -> core::cmp::Ordering {
+ match core::intrinsics::discriminant_value(self ).cmp(&core::intrinsics::discriminant_value(other)) {
+ core::cmp::Ordering::Equal=> {
+ match (self , other) {
+ (Command::Move {
+ x: x_self, y: y_self,
+ }
+ , Command::Move {
+ x: x_other, y: y_other,
+ }
+ )=>match x_self.cmp(&x_other) {
+ core::cmp::Ordering::Equal=> {
+ match y_self.cmp(&y_other) {
+ core::cmp::Ordering::Equal=> {
+ core::cmp::Ordering::Equal
+ }
+ c=>return c,
+ }
+ }
+ c=>return c,
+ }
+ , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) {
+ core::cmp::Ordering::Equal=> {
+ core::cmp::Ordering::Equal
+ }
+ c=>return c,
+ }
+ , (Command::Jump, Command::Jump)=>core::cmp::Ordering::Equal, _unused=>core::cmp::Ordering::Equal
+ }
+ }
+ c=>return c,
+ }
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_hash_expand() {
+ check(
+ r#"
+//- minicore: derive, hash
+use core::hash::Hash;
+
+#[derive(Hash)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+"#,
+ expect![[r#"
+use core::hash::Hash;
+
+#[derive(Hash)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+
+impl < > core::hash::Hash for Command< > where {
+ fn hash<H: core::hash::Hasher>(&self , ra_expand_state: &mut H) {
+ core::mem::discriminant(self ).hash(ra_expand_state);
+ match self {
+ Command::Move {
+ x: x, y: y,
+ }
+ => {
+ x.hash(ra_expand_state);
+ y.hash(ra_expand_state);
+ }
+ , Command::Do(f0, )=> {
+ f0.hash(ra_expand_state);
+ }
+ , Command::Jump=> {}
+ ,
+ }
+ }
+}"#]],
+ );
+}
+
+#[test]
+fn test_debug_expand() {
+ check(
+ r#"
+//- minicore: derive, fmt
+use core::fmt::Debug;
+
+#[derive(Debug)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+"#,
+ expect![[r#"
+use core::fmt::Debug;
+
+#[derive(Debug)]
+enum Command {
+ Move { x: i32, y: i32 },
+ Do(&'static str),
+ Jump,
+}
+
+impl < > core::fmt::Debug for Command< > where {
+ fn fmt(&self , f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ match self {
+ Command::Move {
+ x: x, y: y,
+ }
+ =>f.debug_struct("Move").field("x", &x).field("y", &y).finish(), Command::Do(f0, )=>f.debug_tuple("Do").field(&f0).finish(), Command::Jump=>f.write_str("Jump"),
+ }
+ }
+}"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 5fbd1789b..07d9baa58 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -13,12 +13,12 @@ macro_rules! column {() => {}}
fn main() { column!(); }
"#,
- expect![[r##"
+ expect![[r#"
#[rustc_builtin_macro]
macro_rules! column {() => {}}
-fn main() { 0; }
-"##]],
+fn main() { 0 as u32; }
+"#]],
);
}
@@ -31,12 +31,12 @@ macro_rules! line {() => {}}
fn main() { line!() }
"#,
- expect![[r##"
+ expect![[r#"
#[rustc_builtin_macro]
macro_rules! line {() => {}}
-fn main() { 0 }
-"##]],
+fn main() { 0 as u32 }
+"#]],
);
}
@@ -79,7 +79,7 @@ fn main() { env!("TEST_ENV_VAR"); }
#[rustc_builtin_macro]
macro_rules! env {() => {}}
-fn main() { "__RA_UNIMPLEMENTED__"; }
+fn main() { "UNRESOLVED_ENV_VAR"; }
"##]],
);
}
@@ -97,7 +97,7 @@ fn main() { option_env!("TEST_ENV_VAR"); }
#[rustc_builtin_macro]
macro_rules! option_env {() => {}}
-fn main() { $crate::option::Option::None:: < &str>; }
+fn main() { ::core::option::Option::None:: < &str>; }
"#]],
);
}
@@ -193,7 +193,7 @@ fn main() {
format_args!("{} {:?}", arg1(a, b, c), arg2);
}
"#,
- expect![[r#"
+ expect![[r##"
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ });
@@ -201,9 +201,47 @@ macro_rules! format_args {
}
fn main() {
- $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(arg2), $crate::fmt::Display::fmt), ]);
+ ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(arg2), ::core::fmt::Debug::fmt), ]);
}
-"#]],
+"##]],
+ );
+}
+
+#[test]
+fn regression_15002() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ format_args!(x = 2);
+ format_args!(x =);
+ format_args!(x =, x = 2);
+ format_args!("{}", x =);
+ format_args!(=, "{}", x =);
+ format_args!(x = 2, "{}", 5);
+}
+"#,
+ expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ /* error: no rule matches input tokens */;
+ /* error: no rule matches input tokens */;
+ /* error: no rule matches input tokens */;
+ /* error: no rule matches input tokens */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::Argument::new(&(), ::core::fmt::Display::fmt), ]);
+ /* error: no rule matches input tokens */;
+ ::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::Argument::new(&(5), ::core::fmt::Display::fmt), ]);
+}
+"##]],
);
}
@@ -221,7 +259,7 @@ fn main() {
format_args!("{} {:?}", a::<A,B>(), b);
}
"#,
- expect![[r#"
+ expect![[r##"
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ });
@@ -229,9 +267,76 @@ macro_rules! format_args {
}
fn main() {
- $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a::<A, B>()), $crate::fmt::Display::fmt), $crate::fmt::Argument::new(&(b), $crate::fmt::Display::fmt), ]);
+ ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a::<A, B>()), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]);
}
-"#]],
+"##]],
+ );
+}
+
+#[test]
+fn test_format_args_expand_with_raw_strings() {
+ check(
+ r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ format_args!(
+ r#"{},mismatch,"{}","{}""#,
+ location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+ mismatch.expected.display(db),
+ mismatch.actual.display(db)
+ );
+}
+"##,
+ expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ ::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::Argument::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]);
+}
+"##]],
+ );
+}
+
+#[test]
+fn test_format_args_expand_eager() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ format_args!(concat!("xxx{}y", "{:?}zzz"), 2, b);
+}
+"#,
+ expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+#[rustc_builtin_macro]
+macro_rules! format_args {
+ ($fmt:expr) => ({ /* compiler built-in */ });
+ ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
+}
+
+fn main() {
+ ::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::Argument::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(b), ::core::fmt::Debug::fmt), ]);
+}
+"##]],
);
}
@@ -250,7 +355,7 @@ fn main() {
format_args!/*+errors*/("{} {:?}", a.);
}
"#,
- expect![[r#"
+ expect![[r##"
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ });
@@ -259,10 +364,10 @@ macro_rules! format_args {
fn main() {
let _ =
- /* parse error: expected field name or number */
-$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::Argument::new(&(a.), $crate::fmt::Display::fmt), ]);
+ /* error: no rule matches input tokens *//* parse error: expected field name or number */
+::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::Argument::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::Argument::new(&(), ::core::fmt::Debug::fmt), ]);
}
-"#]],
+"##]],
);
}
@@ -337,10 +442,6 @@ macro_rules! surprise {
() => { "s" };
}
-macro_rules! stuff {
- ($string:expr) => { concat!($string) };
-}
-
fn main() { concat!(surprise!()); }
"##,
expect![[r##"
@@ -351,10 +452,6 @@ macro_rules! surprise {
() => { "s" };
}
-macro_rules! stuff {
- ($string:expr) => { concat!($string) };
-}
-
fn main() { "s"; }
"##]],
);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 7a3e8c3b0..553ffe3d0 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -4,6 +4,7 @@
mod tt_conversion;
mod matching;
mod meta_syntax;
+mod metavar_expr;
mod regression;
use expect_test::expect;
@@ -98,7 +99,7 @@ fn#19 main#20(#21)#21 {#22
);
}
#[test]
-fn float_field_acces_macro_input() {
+fn float_field_access_macro_input() {
check(
r#"
macro_rules! foo {
@@ -922,7 +923,7 @@ macro_rules! m {
fn bar() -> &'a Baz<u8> {}
-fn bar() -> extern "Rust"fn() -> Ret {}
+fn bar() -> extern "Rust" fn() -> Ret {}
"#]],
);
}
@@ -1293,20 +1294,54 @@ ok!();
}
#[test]
-fn test_vertical_bar_with_pat() {
+fn test_vertical_bar_with_pat_param() {
check(
r#"
-macro_rules! m { (|$pat:pat| ) => { ok!(); } }
+macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
m! { |x| }
"#,
expect![[r#"
-macro_rules! m { (|$pat:pat| ) => { ok!(); } }
+macro_rules! m { (|$pat:pat_param| ) => { ok!(); } }
ok!();
"#]],
);
}
#[test]
+fn test_new_std_matches() {
+ check(
+ r#"
+macro_rules! matches {
+ ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
+ match $expression {
+ $pattern $(if $guard)? => true,
+ _ => false
+ }
+ };
+}
+fn main() {
+ matches!(0, 0 | 1 if true);
+}
+ "#,
+ expect![[r#"
+macro_rules! matches {
+ ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
+ match $expression {
+ $pattern $(if $guard)? => true,
+ _ => false
+ }
+ };
+}
+fn main() {
+ match 0 {
+ 0|1 if true =>true , _=>false
+ };
+}
+ "#]],
+ );
+}
+
+#[test]
fn test_dollar_crate_lhs_is_not_meta() {
check(
r#"
@@ -1581,92 +1616,6 @@ struct Foo;
}
#[test]
-fn test_dollar_dollar() {
- check(
- r#"
-macro_rules! register_struct { ($Struct:ident) => {
- macro_rules! register_methods { ($$($method:ident),*) => {
- macro_rules! implement_methods { ($$$$($$val:expr),*) => {
- struct $Struct;
- impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
- }}
- }}
-}}
-
-register_struct!(Foo);
-register_methods!(alpha, beta);
-implement_methods!(1, 2, 3);
-"#,
- expect![[r#"
-macro_rules! register_struct { ($Struct:ident) => {
- macro_rules! register_methods { ($$($method:ident),*) => {
- macro_rules! implement_methods { ($$$$($$val:expr),*) => {
- struct $Struct;
- impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
- }}
- }}
-}}
-
-macro_rules !register_methods {
- ($($method: ident), *) = > {
- macro_rules!implement_methods {
- ($$($val: expr), *) = > {
- struct Foo;
- impl Foo {
- $(fn $method()-> &'static[u32] {
- &[$$($$val), *]
- }
- )*
- }
- }
- }
- }
-}
-macro_rules !implement_methods {
- ($($val: expr), *) = > {
- struct Foo;
- impl Foo {
- fn alpha()-> &'static[u32] {
- &[$($val), *]
- }
- fn beta()-> &'static[u32] {
- &[$($val), *]
- }
- }
- }
-}
-struct Foo;
-impl Foo {
- fn alpha() -> &'static[u32] {
- &[1, 2, 3]
- }
- fn beta() -> &'static[u32] {
- &[1, 2, 3]
- }
-}
-"#]],
- )
-}
-
-#[test]
-fn test_metavar_exprs() {
- check(
- r#"
-macro_rules! m {
- ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
-}
-const _: i32 = m!(a b c);
- "#,
- expect![[r#"
-macro_rules! m {
- ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
-}
-const _: i32 = -0--1--2;
- "#]],
- );
-}
-
-#[test]
fn test_punct_without_space() {
// Puncts are "glued" greedily.
check(
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
index 26f16542c..0909d8c83 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
@@ -33,7 +33,7 @@ m!(&k");
"#,
expect![[r#"
macro_rules! m { ($i:literal) => {}; }
-/* error: Failed to lower macro args to token tree */"#]],
+/* error: invalid token tree */"#]],
);
}
@@ -73,7 +73,7 @@ fn main() {
macro_rules! asi { ($($stmt:stmt)*) => ($($stmt)*); }
fn main() {
- let a = 2let b = 5drop(b-a)println!("{}", a+b)
+ let a = 2 let b = 5 drop(b-a)println!("{}", a+b)
}
"#]],
)
@@ -106,7 +106,6 @@ stringify!(;
#[test]
fn range_patterns() {
- // FIXME: rustc thinks there are three patterns here, not one.
check(
r#"
macro_rules! m {
@@ -118,7 +117,7 @@ m!(.. .. ..);
macro_rules! m {
($($p:pat)*) => (stringify!($($p |)*);)
}
-stringify!(.. .. .. |);
+stringify!(.. | .. | .. |);
"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
new file mode 100644
index 000000000..967b5ad36
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs
@@ -0,0 +1,311 @@
+//! Tests for RFC 3086 metavariable expressions.
+
+use expect_test::expect;
+
+use crate::macro_expansion_tests::check;
+
+#[test]
+fn test_dollar_dollar() {
+ check(
+ r#"
+macro_rules! register_struct { ($Struct:ident) => {
+ macro_rules! register_methods { ($$($method:ident),*) => {
+ macro_rules! implement_methods { ($$$$($$val:expr),*) => {
+ struct $Struct;
+ impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
+ }}
+ }}
+}}
+
+register_struct!(Foo);
+register_methods!(alpha, beta);
+implement_methods!(1, 2, 3);
+"#,
+ expect![[r#"
+macro_rules! register_struct { ($Struct:ident) => {
+ macro_rules! register_methods { ($$($method:ident),*) => {
+ macro_rules! implement_methods { ($$$$($$val:expr),*) => {
+ struct $Struct;
+ impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*}
+ }}
+ }}
+}}
+
+macro_rules !register_methods {
+ ($($method: ident), *) = > {
+ macro_rules!implement_methods {
+ ($$($val: expr), *) = > {
+ struct Foo;
+ impl Foo {
+ $(fn $method()-> &'static[u32] {
+ &[$$($$val), *]
+ }
+ )*
+ }
+ }
+ }
+ }
+}
+macro_rules !implement_methods {
+ ($($val: expr), *) = > {
+ struct Foo;
+ impl Foo {
+ fn alpha()-> &'static[u32] {
+ &[$($val), *]
+ }
+ fn beta()-> &'static[u32] {
+ &[$($val), *]
+ }
+ }
+ }
+}
+struct Foo;
+impl Foo {
+ fn alpha() -> &'static[u32] {
+ &[1, 2, 3]
+ }
+ fn beta() -> &'static[u32] {
+ &[1, 2, 3]
+ }
+}
+"#]],
+ )
+}
+
+#[test]
+fn test_metavar_exprs() {
+ check(
+ r#"
+macro_rules! m {
+ ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
+}
+const _: i32 = m!(a b c);
+ "#,
+ expect![[r#"
+macro_rules! m {
+ ( $( $t:tt )* ) => ( $( ${ignore(t)} -${index()} )-* );
+}
+const _: i32 = -0--1--2;
+ "#]],
+ );
+}
+
+#[test]
+fn count_basic() {
+ check(
+ r#"
+macro_rules! m {
+ ($($t:ident),*) => {
+ ${count(t)}
+ }
+}
+
+fn test() {
+ m!();
+ m!(a);
+ m!(a, a);
+}
+"#,
+ expect![[r#"
+macro_rules! m {
+ ($($t:ident),*) => {
+ ${count(t)}
+ }
+}
+
+fn test() {
+ 0;
+ 1;
+ 2;
+}
+"#]],
+ );
+}
+
+#[test]
+fn count_with_depth() {
+ check(
+ r#"
+macro_rules! foo {
+ ($( $( $($t:ident)* ),* );*) => {
+ $(
+ {
+ let depth_none = ${count(t)};
+ let depth_zero = ${count(t, 0)};
+ let depth_one = ${count(t, 1)};
+ }
+ )*
+ }
+}
+
+fn bar() {
+ foo!(
+ a a a, a, a a;
+ a a a
+ )
+}
+"#,
+ expect![[r#"
+macro_rules! foo {
+ ($( $( $($t:ident)* ),* );*) => {
+ $(
+ {
+ let depth_none = ${count(t)};
+ let depth_zero = ${count(t, 0)};
+ let depth_one = ${count(t, 1)};
+ }
+ )*
+ }
+}
+
+fn bar() {
+ {
+ let depth_none = 6;
+ let depth_zero = 3;
+ let depth_one = 6;
+ } {
+ let depth_none = 3;
+ let depth_zero = 1;
+ let depth_one = 3;
+ }
+}
+"#]],
+ );
+}
+
+#[test]
+fn count_depth_out_of_bounds() {
+ check(
+ r#"
+macro_rules! foo {
+ ($($t:ident)*) => { ${count(t, 1)} };
+ ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
+}
+macro_rules! bar {
+ ($($t:ident)*) => { ${count(t, 1024)} };
+ ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
+}
+
+fn test() {
+ foo!(a b);
+ foo!(1 2; 3);
+ bar!(a b);
+ bar!(1 2; 3);
+}
+"#,
+ expect![[r#"
+macro_rules! foo {
+ ($($t:ident)*) => { ${count(t, 1)} };
+ ($( $( $l:literal )* );*) => { $(${count(l, 1)};)* }
+}
+macro_rules! bar {
+ ($($t:ident)*) => { ${count(t, 1024)} };
+ ($( $( $l:literal )* );*) => { $(${count(l, 8192)};)* }
+}
+
+fn test() {
+ /* error: ${count} out of bounds */;
+ /* error: ${count} out of bounds */;
+ /* error: ${count} out of bounds */;
+ /* error: ${count} out of bounds */;
+}
+"#]],
+ );
+}
+
+#[test]
+fn misplaced_count() {
+ check(
+ r#"
+macro_rules! foo {
+ ($($t:ident)*) => { $(${count(t)})* };
+ ($l:literal) => { ${count(l)} }
+}
+
+fn test() {
+ foo!(a b c);
+ foo!(1);
+}
+"#,
+ expect![[r#"
+macro_rules! foo {
+ ($($t:ident)*) => { $(${count(t)})* };
+ ($l:literal) => { ${count(l)} }
+}
+
+fn test() {
+ /* error: ${count} misplaced */;
+ /* error: ${count} misplaced */;
+}
+"#]],
+ );
+}
+
+#[test]
+fn malformed_count() {
+ check(
+ r#"
+macro_rules! too_many_args {
+ ($($t:ident)*) => { ${count(t, 1, leftover)} }
+}
+macro_rules! depth_suffixed {
+ ($($t:ident)*) => { ${count(t, 0usize)} }
+}
+macro_rules! depth_too_large {
+ ($($t:ident)*) => { ${count(t, 18446744073709551616)} }
+}
+
+fn test() {
+ too_many_args!();
+ depth_suffixed!();
+ depth_too_large!();
+}
+"#,
+ expect![[r#"
+macro_rules! too_many_args {
+ ($($t:ident)*) => { ${count(t, 1, leftover)} }
+}
+macro_rules! depth_suffixed {
+ ($($t:ident)*) => { ${count(t, 0usize)} }
+}
+macro_rules! depth_too_large {
+ ($($t:ident)*) => { ${count(t, 18446744073709551616)} }
+}
+
+fn test() {
+ /* error: invalid macro definition: invalid metavariable expression */;
+ /* error: invalid macro definition: invalid metavariable expression */;
+ /* error: invalid macro definition: invalid metavariable expression */;
+}
+"#]],
+ );
+}
+
+#[test]
+fn count_interaction_with_empty_binding() {
+ // FIXME: Should this error? rustc currently accepts it.
+ check(
+ r#"
+macro_rules! m {
+ ($($t:ident),*) => {
+ ${count(t, 100)}
+ }
+}
+
+fn test() {
+ m!();
+}
+"#,
+ expect![[r#"
+macro_rules! m {
+ ($($t:ident),*) => {
+ ${count(t, 100)}
+ }
+}
+
+fn test() {
+ 0;
+}
+"#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index b663a2917..d8e4a4dcc 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -297,55 +297,55 @@ macro_rules! impl_fn_for_zst {
#[derive(Clone)] struct CharEscapeDebugContinue;
impl Fn<(char, )> for CharEscapeDebugContinue {
- #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeDebug { {
+ #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDebug { {
c.escape_debug_ext(false )
}
}
}
impl FnMut<(char, )> for CharEscapeDebugContinue {
- #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug {
+ #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDebug {
Fn::call(&*self , (c, ))
}
}
impl FnOnce<(char, )> for CharEscapeDebugContinue {
type Output = char::EscapeDebug;
- #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeDebug {
+ #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDebug {
Fn::call(&self , (c, ))
}
}
#[derive(Clone)] struct CharEscapeUnicode;
impl Fn<(char, )> for CharEscapeUnicode {
- #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { {
+ #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeUnicode { {
c.escape_unicode()
}
}
}
impl FnMut<(char, )> for CharEscapeUnicode {
- #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode {
+ #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeUnicode {
Fn::call(&*self , (c, ))
}
}
impl FnOnce<(char, )> for CharEscapeUnicode {
type Output = char::EscapeUnicode;
- #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode {
+ #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeUnicode {
Fn::call(&self , (c, ))
}
}
#[derive(Clone)] struct CharEscapeDefault;
impl Fn<(char, )> for CharEscapeDefault {
- #[inline] extern "rust-call"fn call(&self , (c, ): (char, )) -> char::EscapeDefault { {
+ #[inline] extern "rust-call" fn call(&self , (c, ): (char, )) -> char::EscapeDefault { {
c.escape_default()
}
}
}
impl FnMut<(char, )> for CharEscapeDefault {
- #[inline] extern "rust-call"fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault {
+ #[inline] extern "rust-call" fn call_mut(&mut self , (c, ): (char, )) -> char::EscapeDefault {
Fn::call(&*self , (c, ))
}
}
impl FnOnce<(char, )> for CharEscapeDefault {
type Output = char::EscapeDefault;
- #[inline] extern "rust-call"fn call_once(self , (c, ): (char, )) -> char::EscapeDefault {
+ #[inline] extern "rust-call" fn call_once(self , (c, ): (char, )) -> char::EscapeDefault {
Fn::call(&self , (c, ))
}
}
@@ -833,7 +833,7 @@ macro_rules! rgb_color {
/* parse error: expected SEMICOLON */
/* parse error: expected expression, item or let statement */
pub fn new() {
- let _ = 0as u32<<(8+8);
+ let _ = 0 as u32<<(8+8);
}
// MACRO_ITEMS@0..31
// FN@0..31
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
index b8d2ca687..ae56934f6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs
@@ -98,7 +98,7 @@ macro_rules! m1 { ($x:ident) => { ($x } }
macro_rules! m2 { ($x:ident) => {} }
/* error: invalid macro definition: expected subtree */
-/* error: Failed to lower macro args to token tree */
+/* error: invalid token tree */
"#]],
)
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
index 314bf22b9..4a62696df 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -14,7 +14,7 @@ mod builtin_fn_macro;
mod builtin_derive_macro;
mod proc_macros;
-use std::{iter, ops::Range, sync::Arc};
+use std::{iter, ops::Range, sync};
use ::mbe::TokenMap;
use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase};
@@ -33,8 +33,13 @@ use syntax::{
use tt::token_id::{Subtree, TokenId};
use crate::{
- db::DefDatabase, macro_id_to_def_id, nameres::ModuleSource, resolver::HasResolver,
- src::HasSource, test_db::TestDB, AdtId, AsMacroCall, Lookup, ModuleDefId,
+ db::DefDatabase,
+ macro_id_to_def_id,
+ nameres::{DefMap, MacroSubNs, ModuleSource},
+ resolver::HasResolver,
+ src::HasSource,
+ test_db::TestDB,
+ AdtId, AsMacroCall, Lookup, ModuleDefId,
};
#[track_caller]
@@ -50,13 +55,13 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
ProcMacro {
name: "identity_when_valid".into(),
kind: base_db::ProcMacroKind::Attr,
- expander: Arc::new(IdentityWhenValidProcMacroExpander),
+ expander: sync::Arc::new(IdentityWhenValidProcMacroExpander),
},
)];
let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);
let krate = db.crate_graph().iter().next().unwrap();
let def_map = db.crate_def_map(krate);
- let local_id = def_map.root();
+ let local_id = DefMap::ROOT;
let module = def_map.module_id(local_id);
let resolver = module.resolver(&db);
let source = def_map[local_id].definition_source(&db);
@@ -125,21 +130,17 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
for macro_call in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
let macro_call = InFile::new(source.file_id, &macro_call);
- let mut error = None;
- let macro_call_id = macro_call
- .as_call_id_with_errors(
- &db,
- krate,
- |path| {
- resolver.resolve_path_as_macro(&db, &path).map(|it| macro_id_to_def_id(&db, it))
- },
- &mut |err| error = Some(err),
- )
- .unwrap()
+ let res = macro_call
+ .as_call_id_with_errors(&db, krate, |path| {
+ resolver
+ .resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang))
+ .map(|it| macro_id_to_def_id(&db, it))
+ })
.unwrap();
+ let macro_call_id = res.value.unwrap();
let macro_file = MacroFile { macro_call_id };
let mut expansion_result = db.parse_macro_expansion(macro_file);
- expansion_result.err = expansion_result.err.or(error);
+ expansion_result.err = expansion_result.err.or(res.err);
expansions.push((macro_call.value.clone(), expansion_result, db.macro_arg(macro_call_id)));
}
@@ -157,34 +158,33 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
if let Some(err) = exp.err {
format_to!(expn_text, "/* error: {} */", err);
}
- if let Some((parse, token_map)) = exp.value {
- if expect_errors {
- assert!(!parse.errors().is_empty(), "no parse errors in expansion");
- for e in parse.errors() {
- format_to!(expn_text, "/* parse error: {} */\n", e);
- }
- } else {
- assert!(
- parse.errors().is_empty(),
- "parse errors in expansion: \n{:#?}",
- parse.errors()
- );
+ let (parse, token_map) = exp.value;
+ if expect_errors {
+ assert!(!parse.errors().is_empty(), "no parse errors in expansion");
+ for e in parse.errors() {
+ format_to!(expn_text, "/* parse error: {} */\n", e);
}
- let pp = pretty_print_macro_expansion(
- parse.syntax_node(),
- show_token_ids.then_some(&*token_map),
+ } else {
+ assert!(
+ parse.errors().is_empty(),
+ "parse errors in expansion: \n{:#?}",
+ parse.errors()
);
- let indent = IndentLevel::from_node(call.syntax());
- let pp = reindent(indent, pp);
- format_to!(expn_text, "{}", pp);
+ }
+ let pp = pretty_print_macro_expansion(
+ parse.syntax_node(),
+ show_token_ids.then_some(&*token_map),
+ );
+ let indent = IndentLevel::from_node(call.syntax());
+ let pp = reindent(indent, pp);
+ format_to!(expn_text, "{}", pp);
- if tree {
- let tree = format!("{:#?}", parse.syntax_node())
- .split_inclusive('\n')
- .map(|line| format!("// {line}"))
- .collect::<String>();
- format_to!(expn_text, "\n{}", tree)
- }
+ if tree {
+ let tree = format!("{:#?}", parse.syntax_node())
+ .split_inclusive('\n')
+ .map(|line| format!("// {line}"))
+ .collect::<String>();
+ format_to!(expn_text, "\n{}", tree)
}
let range = call.syntax().text_range();
let range: Range<usize> = range.into();
@@ -287,6 +287,7 @@ fn pretty_print_macro_expansion(expn: SyntaxNode, map: Option<&TokenMap>) -> Str
let curr_kind = token.kind();
let space = match (prev_kind, curr_kind) {
_ if prev_kind.is_trivia() || curr_kind.is_trivia() => "",
+ _ if prev_kind.is_literal() && !curr_kind.is_punct() => " ",
(T!['{'], T!['}']) => "",
(T![=], _) | (_, T![=]) => " ",
(_, T!['{']) => " ",
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index 4efe8c58a..0ab1bd849 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -57,9 +57,9 @@ mod path_resolution;
#[cfg(test)]
mod tests;
-use std::{cmp::Ord, ops::Deref, sync::Arc};
+use std::{cmp::Ord, ops::Deref};
-use base_db::{CrateId, Edition, FileId};
+use base_db::{CrateId, Edition, FileId, ProcMacroKind};
use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId};
use itertools::Itertools;
use la_arena::Arena;
@@ -67,6 +67,7 @@ use profile::Count;
use rustc_hash::{FxHashMap, FxHashSet};
use stdx::format_to;
use syntax::{ast, SmolStr};
+use triomphe::Arc;
use crate::{
db::DefDatabase,
@@ -76,7 +77,8 @@ use crate::{
path::ModPath,
per_ns::PerNs,
visibility::Visibility,
- AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, MacroId, ModuleId, ProcMacroId,
+ AstId, BlockId, BlockLoc, CrateRootModuleId, FunctionId, LocalModuleId, Lookup, MacroExpander,
+ MacroId, ModuleId, ProcMacroId,
};
/// Contains the results of (early) name resolution.
@@ -91,8 +93,10 @@ use crate::{
#[derive(Debug, PartialEq, Eq)]
pub struct DefMap {
_c: Count<Self>,
+ /// When this is a block def map, this will hold the block id of the the block and module that
+ /// contains this block.
block: Option<BlockInfo>,
- root: LocalModuleId,
+ /// The modules and their data declared in this crate.
modules: Arena<ModuleData>,
krate: CrateId,
/// The prelude module for this crate. This either comes from an import
@@ -102,17 +106,32 @@ pub struct DefMap {
/// but that attribute is nightly and when used in a block, it affects resolution globally
/// so we aren't handling this correctly anyways).
prelude: Option<ModuleId>,
- /// The extern prelude is only populated for non-block DefMaps
- extern_prelude: FxHashMap<Name, ModuleId>,
+ /// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that
+ /// this contains all kinds of macro, not just `macro_rules!` macro.
+ macro_use_prelude: FxHashMap<Name, MacroId>,
+
+ /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
+ /// attributes.
+ derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
+
+ /// The diagnostics that need to be emitted for this crate.
+ diagnostics: Vec<DefDiagnostic>,
+
+ /// The crate data that is shared between a crate's def map and all its block def maps.
+ data: Arc<DefMapCrateData>,
+}
+
+/// Data that belongs to a crate which is shared between a crate's def map and all its block def maps.
+#[derive(Clone, Debug, PartialEq, Eq)]
+struct DefMapCrateData {
+ /// The extern prelude which contains all root modules of external crates that are in scope.
+ extern_prelude: FxHashMap<Name, CrateRootModuleId>,
/// Side table for resolving derive helpers.
exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
fn_proc_macro_mapping: FxHashMap<FunctionId, ProcMacroId>,
/// The error that occurred when failing to load the proc-macro dll.
proc_macro_loading_error: Option<Box<str>>,
- /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
- /// attributes.
- derive_helpers_in_scope: FxHashMap<AstId<ast::Item>, Vec<(Name, MacroId, MacroCallId)>>,
/// Custom attributes registered with `#![register_attr]`.
registered_attrs: Vec<SmolStr>,
@@ -122,10 +141,36 @@ pub struct DefMap {
unstable_features: FxHashSet<SmolStr>,
/// #[rustc_coherence_is_core]
rustc_coherence_is_core: bool,
+ no_core: bool,
+ no_std: bool,
edition: Edition,
recursion_limit: Option<u32>,
- diagnostics: Vec<DefDiagnostic>,
+}
+
+impl DefMapCrateData {
+ fn shrink_to_fit(&mut self) {
+ let Self {
+ extern_prelude,
+ exported_derives,
+ fn_proc_macro_mapping,
+ registered_attrs,
+ registered_tools,
+ unstable_features,
+ proc_macro_loading_error: _,
+ rustc_coherence_is_core: _,
+ no_core: _,
+ no_std: _,
+ edition: _,
+ recursion_limit: _,
+ } = self;
+ extern_prelude.shrink_to_fit();
+ exported_derives.shrink_to_fit();
+ fn_proc_macro_mapping.shrink_to_fit();
+ registered_attrs.shrink_to_fit();
+ registered_tools.shrink_to_fit();
+ unstable_features.shrink_to_fit();
+ }
}
/// For `DefMap`s computed for a block expression, this stores its location in the parent map.
@@ -134,7 +179,23 @@ struct BlockInfo {
/// The `BlockId` this `DefMap` was created from.
block: BlockId,
/// The containing module.
- parent: ModuleId,
+ parent: BlockRelativeModuleId,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+struct BlockRelativeModuleId {
+ block: Option<BlockId>,
+ local_id: LocalModuleId,
+}
+
+impl BlockRelativeModuleId {
+ fn def_map(self, db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
+ self.into_module(krate).def_map(db)
+ }
+
+ fn into_module(self, krate: CrateId) -> ModuleId {
+ ModuleId { krate, block: self.block, local_id: self.local_id }
+ }
}
impl std::ops::Index<LocalModuleId> for DefMap {
@@ -224,6 +285,9 @@ pub struct ModuleData {
}
impl DefMap {
+ /// The module id of a crate or block root.
+ pub const ROOT: LocalModuleId = LocalModuleId::from_raw(la_arena::RawIdx::from_u32(0));
+
pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
let _p = profile::span("crate_def_map_query").detail(|| {
db.crate_graph()[krate].display_name.as_deref().unwrap_or_default().to_string()
@@ -243,17 +307,10 @@ impl DefMap {
Arc::new(def_map)
}
- pub(crate) fn block_def_map_query(
- db: &dyn DefDatabase,
- block_id: BlockId,
- ) -> Option<Arc<DefMap>> {
+ pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> {
let block: BlockLoc = db.lookup_intern_block(block_id);
let tree_id = TreeId::new(block.ast_id.file_id, Some(block_id));
- let item_tree = tree_id.item_tree(db);
- if item_tree.top_level_items().is_empty() {
- return None;
- }
let parent_map = block.module.def_map(db);
let krate = block.module.krate;
@@ -265,36 +322,48 @@ impl DefMap {
let module_data =
ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility);
- let mut def_map = DefMap::empty(krate, parent_map.edition, module_data);
- def_map.block = Some(BlockInfo { block: block_id, parent: block.module });
+ let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data);
+ def_map.data = parent_map.data.clone();
+ def_map.block = Some(BlockInfo {
+ block: block_id,
+ parent: BlockRelativeModuleId {
+ block: block.module.block,
+ local_id: block.module.local_id,
+ },
+ });
let def_map = collector::collect_defs(db, def_map, tree_id);
- Some(Arc::new(def_map))
+ Arc::new(def_map)
}
fn empty(krate: CrateId, edition: Edition, module_data: ModuleData) -> DefMap {
let mut modules: Arena<ModuleData> = Arena::default();
let root = modules.alloc(module_data);
+ assert_eq!(root, Self::ROOT);
DefMap {
_c: Count::new(),
block: None,
+ modules,
krate,
- edition,
- recursion_limit: None,
- extern_prelude: FxHashMap::default(),
- exported_derives: FxHashMap::default(),
- fn_proc_macro_mapping: FxHashMap::default(),
- proc_macro_loading_error: None,
- derive_helpers_in_scope: FxHashMap::default(),
prelude: None,
- root,
- modules,
- registered_attrs: Vec::new(),
- registered_tools: Vec::new(),
- unstable_features: FxHashSet::default(),
+ macro_use_prelude: FxHashMap::default(),
+ derive_helpers_in_scope: FxHashMap::default(),
diagnostics: Vec::new(),
- rustc_coherence_is_core: false,
+ data: Arc::new(DefMapCrateData {
+ extern_prelude: FxHashMap::default(),
+ exported_derives: FxHashMap::default(),
+ fn_proc_macro_mapping: FxHashMap::default(),
+ proc_macro_loading_error: None,
+ registered_attrs: Vec::new(),
+ registered_tools: Vec::new(),
+ unstable_features: FxHashSet::default(),
+ rustc_coherence_is_core: false,
+ no_core: false,
+ no_std: false,
+ edition,
+ recursion_limit: None,
+ }),
}
}
@@ -317,31 +386,31 @@ impl DefMap {
}
pub fn registered_tools(&self) -> &[SmolStr] {
- &self.registered_tools
+ &self.data.registered_tools
}
pub fn registered_attrs(&self) -> &[SmolStr] {
- &self.registered_attrs
+ &self.data.registered_attrs
}
pub fn is_unstable_feature_enabled(&self, feature: &str) -> bool {
- self.unstable_features.contains(feature)
+ self.data.unstable_features.contains(feature)
}
pub fn is_rustc_coherence_is_core(&self) -> bool {
- self.rustc_coherence_is_core
+ self.data.rustc_coherence_is_core
}
- pub fn root(&self) -> LocalModuleId {
- self.root
+ pub fn is_no_std(&self) -> bool {
+ self.data.no_std || self.data.no_core
}
pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option<ProcMacroId> {
- self.fn_proc_macro_mapping.get(&id).copied()
+ self.data.fn_proc_macro_mapping.get(&id).copied()
}
pub fn proc_macro_loading_error(&self) -> Option<&str> {
- self.proc_macro_loading_error.as_deref()
+ self.data.proc_macro_loading_error.as_deref()
}
pub fn krate(&self) -> CrateId {
@@ -356,8 +425,12 @@ impl DefMap {
self.prelude
}
- pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, &ModuleId)> + '_ {
- self.extern_prelude.iter()
+ pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, ModuleId)> + '_ {
+ self.data.extern_prelude.iter().map(|(name, &def)| (name, def.into()))
+ }
+
+ pub(crate) fn macro_use_prelude(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ {
+ self.macro_use_prelude.iter().map(|(name, &def)| (name, def))
}
pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId {
@@ -365,11 +438,8 @@ impl DefMap {
ModuleId { krate: self.krate, local_id, block }
}
- pub(crate) fn crate_root(&self, db: &dyn DefDatabase) -> ModuleId {
- self.with_ancestor_maps(db, self.root, &mut |def_map, _module| {
- if def_map.block.is_none() { Some(def_map.module_id(def_map.root)) } else { None }
- })
- .expect("DefMap chain without root")
+ pub fn crate_root(&self) -> CrateRootModuleId {
+ CrateRootModuleId { krate: self.krate }
}
pub(crate) fn resolve_path(
@@ -378,9 +448,16 @@ impl DefMap {
original_module: LocalModuleId,
path: &ModPath,
shadow: BuiltinShadowMode,
+ expected_macro_subns: Option<MacroSubNs>,
) -> (PerNs, Option<usize>) {
- let res =
- self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow);
+ let res = self.resolve_path_fp_with_macro(
+ db,
+ ResolveMode::Other,
+ original_module,
+ path,
+ shadow,
+ expected_macro_subns,
+ );
(res.resolved_def, res.segment_index)
}
@@ -397,6 +474,7 @@ impl DefMap {
original_module,
path,
shadow,
+ None, // Currently this function isn't used for macro resolution.
);
(res.resolved_def, res.segment_index)
}
@@ -405,7 +483,7 @@ impl DefMap {
///
/// If `f` returns `Some(val)`, iteration is stopped and `Some(val)` is returned. If `f` returns
/// `None`, iteration continues.
- pub fn with_ancestor_maps<T>(
+ pub(crate) fn with_ancestor_maps<T>(
&self,
db: &dyn DefDatabase,
local_mod: LocalModuleId,
@@ -416,7 +494,7 @@ impl DefMap {
}
let mut block = self.block;
while let Some(block_info) = block {
- let parent = block_info.parent.def_map(db);
+ let parent = block_info.parent.def_map(db, self.krate);
if let Some(it) = f(&parent, block_info.parent.local_id) {
return Some(it);
}
@@ -429,7 +507,8 @@ impl DefMap {
/// If this `DefMap` is for a block expression, returns the module containing the block (which
/// might again be a block, or a module inside a block).
pub fn parent(&self) -> Option<ModuleId> {
- Some(self.block?.parent)
+ let BlockRelativeModuleId { block, local_id } = self.block?.parent;
+ Some(ModuleId { krate: self.krate, block, local_id })
}
/// Returns the module containing `local_mod`, either the parent `mod`, or the module (or block) containing
@@ -437,7 +516,13 @@ impl DefMap {
pub fn containing_module(&self, local_mod: LocalModuleId) -> Option<ModuleId> {
match self[local_mod].parent {
Some(parent) => Some(self.module_id(parent)),
- None => self.block.map(|block| block.parent),
+ None => {
+ self.block.map(
+ |BlockInfo { parent: BlockRelativeModuleId { block, local_id }, .. }| {
+ ModuleId { krate: self.krate, block, local_id }
+ },
+ )
+ }
}
}
@@ -448,25 +533,31 @@ impl DefMap {
let mut arc;
let mut current_map = self;
while let Some(block) = current_map.block {
- go(&mut buf, current_map, "block scope", current_map.root);
+ go(&mut buf, db, current_map, "block scope", Self::ROOT);
buf.push('\n');
- arc = block.parent.def_map(db);
+ arc = block.parent.def_map(db, self.krate);
current_map = &arc;
}
- go(&mut buf, current_map, "crate", current_map.root);
+ go(&mut buf, db, current_map, "crate", Self::ROOT);
return buf;
- fn go(buf: &mut String, map: &DefMap, path: &str, module: LocalModuleId) {
+ fn go(
+ buf: &mut String,
+ db: &dyn DefDatabase,
+ map: &DefMap,
+ path: &str,
+ module: LocalModuleId,
+ ) {
format_to!(buf, "{}\n", path);
- map.modules[module].scope.dump(buf);
+ map.modules[module].scope.dump(db.upcast(), buf);
for (name, child) in
map.modules[module].children.iter().sorted_by(|a, b| Ord::cmp(&a.0, &b.0))
{
- let path = format!("{path}::{name}");
+ let path = format!("{path}::{}", name.display(db.upcast()));
buf.push('\n');
- go(buf, map, &path, *child);
+ go(buf, db, map, &path, *child);
}
}
}
@@ -477,7 +568,7 @@ impl DefMap {
let mut current_map = self;
while let Some(block) = current_map.block {
format_to!(buf, "{:?} in {:?}\n", block.block, block.parent);
- arc = block.parent.def_map(db);
+ arc = block.parent.def_map(db, self.krate);
current_map = &arc;
}
@@ -489,34 +580,20 @@ impl DefMap {
// Exhaustive match to require handling new fields.
let Self {
_c: _,
- exported_derives,
- extern_prelude,
+ macro_use_prelude,
diagnostics,
modules,
- registered_attrs,
- registered_tools,
- fn_proc_macro_mapping,
derive_helpers_in_scope,
- unstable_features,
- proc_macro_loading_error: _,
block: _,
- edition: _,
- recursion_limit: _,
krate: _,
prelude: _,
- root: _,
- rustc_coherence_is_core: _,
+ data: _,
} = self;
- extern_prelude.shrink_to_fit();
- exported_derives.shrink_to_fit();
+ macro_use_prelude.shrink_to_fit();
diagnostics.shrink_to_fit();
modules.shrink_to_fit();
- registered_attrs.shrink_to_fit();
- registered_tools.shrink_to_fit();
- fn_proc_macro_mapping.shrink_to_fit();
derive_helpers_in_scope.shrink_to_fit();
- unstable_features.shrink_to_fit();
for (_, module) in modules.iter_mut() {
module.children.shrink_to_fit();
module.scope.shrink_to_fit();
@@ -529,7 +606,7 @@ impl DefMap {
}
pub fn recursion_limit(&self) -> Option<u32> {
- self.recursion_limit
+ self.data.recursion_limit
}
}
@@ -564,3 +641,48 @@ pub enum ModuleSource {
Module(ast::Module),
BlockExpr(ast::BlockExpr),
}
+
+/// See `sub_namespace_match()`.
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum MacroSubNs {
+ /// Function-like macros, suffixed with `!`.
+ Bang,
+ /// Macros inside attributes, i.e. attribute macros and derive macros.
+ Attr,
+}
+
+impl MacroSubNs {
+ fn from_id(db: &dyn DefDatabase, macro_id: MacroId) -> Self {
+ let expander = match macro_id {
+ MacroId::Macro2Id(it) => it.lookup(db).expander,
+ MacroId::MacroRulesId(it) => it.lookup(db).expander,
+ MacroId::ProcMacroId(it) => {
+ return match it.lookup(db).kind {
+ ProcMacroKind::CustomDerive | ProcMacroKind::Attr => Self::Attr,
+ ProcMacroKind::FuncLike => Self::Bang,
+ };
+ }
+ };
+
+ // Eager macros aren't *guaranteed* to be bang macros, but they *are* all bang macros currently.
+ match expander {
+ MacroExpander::Declarative
+ | MacroExpander::BuiltIn(_)
+ | MacroExpander::BuiltInEager(_) => Self::Bang,
+ MacroExpander::BuiltInAttr(_) | MacroExpander::BuiltInDerive(_) => Self::Attr,
+ }
+ }
+}
+
+/// Quoted from [rustc]:
+/// Macro namespace is separated into two sub-namespaces, one for bang macros and
+/// one for attribute-like macros (attributes, derives).
+/// We ignore resolutions from one sub-namespace when searching names in scope for another.
+///
+/// [rustc]: https://github.com/rust-lang/rust/blob/1.69.0/compiler/rustc_resolve/src/macros.rs#L75
+fn sub_namespace_match(candidate: Option<MacroSubNs>, expected: Option<MacroSubNs>) -> bool {
+ match (candidate, expected) {
+ (Some(candidate), Some(expected)) => candidate == expected,
+ _ => true,
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
index 79cabeb0f..a7abf4459 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/attr_resolution.rs
@@ -4,7 +4,8 @@ use hir_expand::{attrs::Attr, MacroCallId};
use syntax::{ast, SmolStr};
use crate::{
- attr_macro_as_call_id, builtin_attr,
+ attr::builtin::{find_builtin_attr_idx, TOOL_MODULES},
+ attr_macro_as_call_id,
db::DefDatabase,
item_scope::BuiltinShadowMode,
macro_id_to_def_id,
@@ -13,7 +14,7 @@ use crate::{
AstIdWithPath, LocalModuleId, UnresolvedMacro,
};
-use super::DefMap;
+use super::{DefMap, MacroSubNs};
pub enum ResolvedAttr {
/// Attribute resolved to an attribute macro.
@@ -42,9 +43,12 @@ impl DefMap {
original_module,
&ast_id.path,
BuiltinShadowMode::Module,
+ Some(MacroSubNs::Attr),
);
let def = match resolved_res.resolved_def.take_macros() {
Some(def) => {
+ // `MacroSubNs` is just a hint, so the path may still resolve to a custom derive
+ // macro, or even function-like macro when the path is qualified.
if def.is_attribute(db) {
def
} else {
@@ -60,7 +64,6 @@ impl DefMap {
attr,
self.krate,
macro_id_to_def_id(db, def),
- false,
)))
}
@@ -75,20 +78,16 @@ impl DefMap {
let name = name.to_smol_str();
let pred = |n: &_| *n == name;
- let registered = self.registered_tools.iter().map(SmolStr::as_str);
- let is_tool = builtin_attr::TOOL_MODULES.iter().copied().chain(registered).any(pred);
+ let registered = self.data.registered_tools.iter().map(SmolStr::as_str);
+ let is_tool = TOOL_MODULES.iter().copied().chain(registered).any(pred);
// FIXME: tool modules can be shadowed by actual modules
if is_tool {
return true;
}
if segments.len() == 1 {
- let registered = self.registered_attrs.iter().map(SmolStr::as_str);
- let is_inert = builtin_attr::INERT_ATTRIBUTES
- .iter()
- .map(|it| it.name)
- .chain(registered)
- .any(pred);
+ let mut registered = self.data.registered_attrs.iter().map(SmolStr::as_str);
+ let is_inert = find_builtin_attr_idx(&name).is_some() || registered.any(pred);
return is_inert;
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index ddcee77ec..62fb3c788 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -3,9 +3,9 @@
//! `DefCollector::collect` contains the fixed-point iteration loop which
//! resolves imports and expands macros.
-use std::{iter, mem};
+use std::{cmp::Ordering, iter, mem};
-use base_db::{CrateId, Edition, FileId};
+use base_db::{CrateId, Dependency, Edition, FileId};
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{
@@ -14,10 +14,11 @@ use hir_expand::{
builtin_attr_macro::find_builtin_attr,
builtin_derive_macro::find_builtin_derive,
builtin_fn_macro::find_builtin_macro,
+ hygiene::Hygiene,
name::{name, AsName, Name},
proc_macro::ProcMacroExpander,
- ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
- MacroDefKind,
+ ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc,
+ MacroDefId, MacroDefKind,
};
use itertools::{izip, Itertools};
use la_arena::Idx;
@@ -25,6 +26,7 @@ use limit::Limit;
use rustc_hash::{FxHashMap, FxHashSet};
use stdx::always;
use syntax::{ast, SmolStr};
+use triomphe::Arc;
use crate::{
attr::Attrs,
@@ -33,8 +35,8 @@ use crate::{
derive_macro_as_call_id,
item_scope::{ImportType, PerNsGlobImports},
item_tree::{
- self, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, MacroCall,
- MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
+ self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
+ MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
},
macro_call_as_call_id, macro_id_to_def_id,
nameres::{
@@ -42,24 +44,25 @@ use crate::{
mod_resolution::ModDir,
path_resolution::ReachedFixedPoint,
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
- BuiltinShadowMode, DefMap, ModuleData, ModuleOrigin, ResolveMode,
+ sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin,
+ ResolveMode,
},
path::{ImportAlias, ModPath, PathKind},
per_ns::PerNs,
tt,
visibility::{RawVisibility, Visibility},
- AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, ExternBlockLoc, FunctionId,
- FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId, Macro2Id, Macro2Loc,
- MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId,
- ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc,
- UnresolvedMacro,
+ AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId,
+ ExternBlockLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, LocalModuleId,
+ Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc, ModuleDefId,
+ ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc, TraitLoc,
+ TypeAliasLoc, UnionLoc, UnresolvedMacro,
};
static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
static EXPANSION_DEPTH_LIMIT: Limit = Limit::new(128);
static FIXED_POINT_LIMIT: Limit = Limit::new(8192);
-pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: TreeId) -> DefMap {
+pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeId) -> DefMap {
let crate_graph = db.crate_graph();
let mut deps = FxHashMap::default();
@@ -67,36 +70,33 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
let krate = &crate_graph[def_map.krate];
for dep in &krate.dependencies {
tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
- let dep_def_map = db.crate_def_map(dep.crate_id);
- let dep_root = dep_def_map.module_id(dep_def_map.root);
- deps.insert(dep.as_name(), dep_root);
-
- if dep.is_prelude() && !tree_id.is_block() {
- def_map.extern_prelude.insert(dep.as_name(), dep_root);
- }
+ deps.insert(dep.as_name(), dep.clone());
}
let cfg_options = &krate.cfg_options;
- let proc_macros = match &krate.proc_macro {
- Ok(proc_macros) => {
- proc_macros
- .iter()
- .enumerate()
- .map(|(idx, it)| {
- // FIXME: a hacky way to create a Name from string.
- let name =
- tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() };
- (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32)))
- })
- .collect()
- }
- Err(e) => {
- def_map.proc_macro_loading_error = Some(e.clone().into_boxed_str());
- Vec::new()
+
+ let is_proc_macro = krate.is_proc_macro;
+ let proc_macros = if is_proc_macro {
+ match db.proc_macros().get(&def_map.krate) {
+ Some(Ok(proc_macros)) => {
+ Ok(proc_macros
+ .iter()
+ .enumerate()
+ .map(|(idx, it)| {
+ // FIXME: a hacky way to create a Name from string.
+ let name =
+ tt::Ident { text: it.name.clone(), span: tt::TokenId::unspecified() };
+ (name.as_name(), ProcMacroExpander::new(base_db::ProcMacroId(idx as u32)))
+ })
+ .collect())
+ }
+ Some(Err(e)) => Err(e.clone().into_boxed_str()),
+ None => Err("No proc-macros present for crate".to_owned().into_boxed_str()),
}
+ } else {
+ Ok(vec![])
};
- let is_proc_macro = krate.is_proc_macro;
let mut collector = DefCollector {
db,
@@ -112,6 +112,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T
from_glob_import: Default::default(),
skip_attrs: Default::default(),
is_proc_macro,
+ hygienes: FxHashMap::default(),
};
if tree_id.is_block() {
collector.seed_with_inner(tree_id);
@@ -238,7 +239,7 @@ enum MacroDirectiveKind {
struct DefCollector<'a> {
db: &'a dyn DefDatabase,
def_map: DefMap,
- deps: FxHashMap<Name, ModuleId>,
+ deps: FxHashMap<Name, Dependency>,
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
unresolved_imports: Vec<ImportDirective>,
indeterminate_imports: Vec<ImportDirective>,
@@ -249,7 +250,7 @@ struct DefCollector<'a> {
/// built by the build system, and is the list of proc. macros we can actually expand. It is
/// empty when proc. macro support is disabled (in which case we still do name resolution for
/// them).
- proc_macros: Vec<(Name, ProcMacroExpander)>,
+ proc_macros: Result<Vec<(Name, ProcMacroExpander)>, Box<str>>,
is_proc_macro: bool,
from_glob_import: PerNsGlobImports,
/// If we fail to resolve an attribute on a `ModItem`, we fall back to ignoring the attribute.
@@ -259,6 +260,12 @@ struct DefCollector<'a> {
/// This also stores the attributes to skip when we resolve derive helpers and non-macro
/// non-builtin attributes in general.
skip_attrs: FxHashMap<InFile<ModItem>, AttrId>,
+ /// `Hygiene` cache, because `Hygiene` construction is expensive.
+ ///
+ /// Almost all paths should have been lowered to `ModPath` during `ItemTree` construction.
+ /// However, `DefCollector` still needs to lower paths in attributes, in particular those in
+ /// derive meta item list.
+ hygienes: FxHashMap<HirFileId, Hygiene>,
}
impl DefCollector<'_> {
@@ -267,87 +274,113 @@ impl DefCollector<'_> {
let file_id = self.db.crate_graph()[self.def_map.krate].root_file_id;
let item_tree = self.db.file_item_tree(file_id.into());
- let module_id = self.def_map.root;
-
let attrs = item_tree.top_level_attrs(self.db, self.def_map.krate);
- if attrs.cfg().map_or(true, |cfg| self.cfg_options.check(&cfg) != Some(false)) {
- self.inject_prelude(&attrs);
-
- // Process other crate-level attributes.
- for attr in &*attrs {
- let attr_name = match attr.path.as_ident() {
- Some(name) => name,
- None => continue,
- };
+ let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
- if *attr_name == hir_expand::name![recursion_limit] {
- if let Some(limit) = attr.string_value() {
- if let Ok(limit) = limit.parse() {
- self.def_map.recursion_limit = Some(limit);
- }
- }
- continue;
+ if let Err(e) = &self.proc_macros {
+ crate_data.proc_macro_loading_error = Some(e.clone());
+ }
+
+ for (name, dep) in &self.deps {
+ if dep.is_prelude() {
+ crate_data
+ .extern_prelude
+ .insert(name.clone(), CrateRootModuleId { krate: dep.crate_id });
+ }
+ }
+
+ // Process other crate-level attributes.
+ for attr in &*attrs {
+ if let Some(cfg) = attr.cfg() {
+ if self.cfg_options.check(&cfg) == Some(false) {
+ return;
}
+ }
+ let attr_name = match attr.path.as_ident() {
+ Some(name) => name,
+ None => continue,
+ };
- if *attr_name == hir_expand::name![crate_type] {
- if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) {
- self.is_proc_macro = true;
+ if *attr_name == hir_expand::name![recursion_limit] {
+ if let Some(limit) = attr.string_value() {
+ if let Ok(limit) = limit.parse() {
+ crate_data.recursion_limit = Some(limit);
}
- continue;
}
+ continue;
+ }
- if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") {
- self.def_map.rustc_coherence_is_core = true;
- continue;
+ if *attr_name == hir_expand::name![crate_type] {
+ if let Some("proc-macro") = attr.string_value().map(SmolStr::as_str) {
+ self.is_proc_macro = true;
}
+ continue;
+ }
- if *attr_name == hir_expand::name![feature] {
- let features =
- attr.parse_path_comma_token_tree().into_iter().flatten().filter_map(
- |feat| match feat.segments() {
- [name] => Some(name.to_smol_str()),
- _ => None,
- },
- );
- self.def_map.unstable_features.extend(features);
- }
+ if *attr_name == hir_expand::name![no_core] {
+ crate_data.no_core = true;
+ continue;
+ }
- let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
- || *attr_name == hir_expand::name![register_tool];
- if !attr_is_register_like {
- continue;
- }
+ if *attr_name == hir_expand::name![no_std] {
+ crate_data.no_std = true;
+ continue;
+ }
- let registered_name = match attr.single_ident_value() {
- Some(ident) => ident.as_name(),
- _ => continue,
- };
+ if attr_name.as_text().as_deref() == Some("rustc_coherence_is_core") {
+ crate_data.rustc_coherence_is_core = true;
+ continue;
+ }
- if *attr_name == hir_expand::name![register_attr] {
- self.def_map.registered_attrs.push(registered_name.to_smol_str());
- cov_mark::hit!(register_attr);
- } else {
- self.def_map.registered_tools.push(registered_name.to_smol_str());
- cov_mark::hit!(register_tool);
- }
+ if *attr_name == hir_expand::name![feature] {
+ let hygiene = &Hygiene::new_unhygienic();
+ let features = attr
+ .parse_path_comma_token_tree(self.db.upcast(), hygiene)
+ .into_iter()
+ .flatten()
+ .filter_map(|feat| match feat.segments() {
+ [name] => Some(name.to_smol_str()),
+ _ => None,
+ });
+ crate_data.unstable_features.extend(features);
}
- ModCollector {
- def_collector: self,
- macro_depth: 0,
- module_id,
- tree_id: TreeId::new(file_id.into(), None),
- item_tree: &item_tree,
- mod_dir: ModDir::root(),
+ let attr_is_register_like = *attr_name == hir_expand::name![register_attr]
+ || *attr_name == hir_expand::name![register_tool];
+ if !attr_is_register_like {
+ continue;
}
- .collect_in_top_module(item_tree.top_level_items());
+
+ let registered_name = match attr.single_ident_value() {
+ Some(ident) => ident.as_name(),
+ _ => continue,
+ };
+
+ if *attr_name == hir_expand::name![register_attr] {
+ crate_data.registered_attrs.push(registered_name.to_smol_str());
+ cov_mark::hit!(register_attr);
+ } else {
+ crate_data.registered_tools.push(registered_name.to_smol_str());
+ cov_mark::hit!(register_tool);
+ }
+ }
+
+ crate_data.shrink_to_fit();
+ self.inject_prelude();
+
+ ModCollector {
+ def_collector: self,
+ macro_depth: 0,
+ module_id: DefMap::ROOT,
+ tree_id: TreeId::new(file_id.into(), None),
+ item_tree: &item_tree,
+ mod_dir: ModDir::root(),
}
+ .collect_in_top_module(item_tree.top_level_items());
}
fn seed_with_inner(&mut self, tree_id: TreeId) {
let item_tree = tree_id.item_tree(self.db);
- let module_id = self.def_map.root;
-
let is_cfg_enabled = item_tree
.top_level_attrs(self.db, self.def_map.krate)
.cfg()
@@ -356,7 +389,7 @@ impl DefCollector<'_> {
ModCollector {
def_collector: self,
macro_depth: 0,
- module_id,
+ module_id: DefMap::ROOT,
tree_id,
item_tree: &item_tree,
mod_dir: ModDir::root(),
@@ -428,7 +461,7 @@ impl DefCollector<'_> {
// Additionally, while the proc macro entry points must be `pub`, they are not publicly
// exported in type/value namespace. This function reduces the visibility of all items
// in the crate root that aren't proc macros.
- let root = self.def_map.root;
+ let root = DefMap::ROOT;
let module_id = self.def_map.module_id(root);
let root = &mut self.def_map.modules[root];
root.scope.censor_non_proc_macros(module_id);
@@ -456,12 +489,8 @@ impl DefCollector<'_> {
directive.module_id,
MacroCallKind::Attr {
ast_id: ast_id.ast_id,
- attr_args: std::sync::Arc::new((
- tt::Subtree::empty(),
- Default::default(),
- )),
+ attr_args: Arc::new((tt::Subtree::empty(), Default::default())),
invoc_attr_index: attr.id,
- is_derive: false,
},
attr.path().clone(),
));
@@ -495,15 +524,15 @@ impl DefCollector<'_> {
}
}
- fn inject_prelude(&mut self, crate_attrs: &Attrs) {
+ fn inject_prelude(&mut self) {
// See compiler/rustc_builtin_macros/src/standard_library_imports.rs
- if crate_attrs.by_key("no_core").exists() {
+ if self.def_map.data.no_core {
// libcore does not get a prelude.
return;
}
- let krate = if crate_attrs.by_key("no_std").exists() {
+ let krate = if self.def_map.data.no_std {
name![core]
} else {
let std = name![std];
@@ -516,43 +545,31 @@ impl DefCollector<'_> {
}
};
- let edition = match self.def_map.edition {
+ let edition = match self.def_map.data.edition {
Edition::Edition2015 => name![rust_2015],
Edition::Edition2018 => name![rust_2018],
Edition::Edition2021 => name![rust_2021],
};
- let path_kind = match self.def_map.edition {
+ let path_kind = match self.def_map.data.edition {
Edition::Edition2015 => PathKind::Plain,
_ => PathKind::Abs,
};
- let path =
- ModPath::from_segments(path_kind, [krate.clone(), name![prelude], edition].into_iter());
- // Fall back to the older `std::prelude::v1` for compatibility with Rust <1.52.0
- // FIXME remove this fallback
- let fallback_path =
- ModPath::from_segments(path_kind, [krate, name![prelude], name![v1]].into_iter());
-
- for path in &[path, fallback_path] {
- let (per_ns, _) = self.def_map.resolve_path(
- self.db,
- self.def_map.root,
- path,
- BuiltinShadowMode::Other,
- );
+ let path = ModPath::from_segments(path_kind, [krate, name![prelude], edition]);
- match per_ns.types {
- Some((ModuleDefId::ModuleId(m), _)) => {
- self.def_map.prelude = Some(m);
- break;
- }
- types => {
- tracing::debug!(
- "could not resolve prelude path `{}` to module (resolved to {:?})",
- path,
- types
- );
- }
+ let (per_ns, _) =
+ self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None);
+
+ match per_ns.types {
+ Some((ModuleDefId::ModuleId(m), _)) => {
+ self.def_map.prelude = Some(m);
+ }
+ types => {
+ tracing::debug!(
+ "could not resolve prelude path `{}` to module (resolved to {:?})",
+ path.display(self.db.upcast()),
+ types
+ );
}
}
}
@@ -578,23 +595,28 @@ impl DefCollector<'_> {
def: ProcMacroDef,
id: ItemTreeId<item_tree::Function>,
fn_id: FunctionId,
- module_id: ModuleId,
) {
+ if self.def_map.block.is_some() {
+ return;
+ }
let kind = def.kind.to_basedb_kind();
- let (expander, kind) = match self.proc_macros.iter().find(|(n, _)| n == &def.name) {
- Some(&(_, expander)) => (expander, kind),
- None => (ProcMacroExpander::dummy(), kind),
- };
+ let (expander, kind) =
+ match self.proc_macros.as_ref().map(|it| it.iter().find(|(n, _)| n == &def.name)) {
+ Ok(Some(&(_, expander))) => (expander, kind),
+ _ => (ProcMacroExpander::dummy(), kind),
+ };
let proc_macro_id =
- ProcMacroLoc { container: module_id, id, expander, kind }.intern(self.db);
+ ProcMacroLoc { container: self.def_map.crate_root(), id, expander, kind }
+ .intern(self.db);
self.define_proc_macro(def.name.clone(), proc_macro_id);
+ let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap();
if let ProcMacroKind::CustomDerive { helpers } = def.kind {
- self.def_map
+ crate_data
.exported_derives
.insert(macro_id_to_def_id(self.db, proc_macro_id.into()), helpers);
}
- self.def_map.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
+ crate_data.fn_proc_macro_mapping.insert(fn_id, proc_macro_id);
}
/// Define a macro with `macro_rules`.
@@ -636,7 +658,7 @@ impl DefCollector<'_> {
// In Rust, `#[macro_export]` macros are unconditionally visible at the
// crate root, even if the parent modules is **not** visible.
if export {
- let module_id = self.def_map.root;
+ let module_id = DefMap::ROOT;
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
@@ -687,7 +709,7 @@ impl DefCollector<'_> {
/// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped.
/// And unconditionally exported.
fn define_proc_macro(&mut self, name: Name, macro_: ProcMacroId) {
- let module_id = self.def_map.root;
+ let module_id = DefMap::ROOT;
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
@@ -697,39 +719,28 @@ impl DefCollector<'_> {
);
}
- /// Import macros from `#[macro_use] extern crate`.
- fn import_macros_from_extern_crate(
- &mut self,
- current_module_id: LocalModuleId,
- extern_crate: &item_tree::ExternCrate,
- ) {
- tracing::debug!(
- "importing macros from extern crate: {:?} ({:?})",
- extern_crate,
- self.def_map.edition,
- );
-
- if let Some(m) = self.resolve_extern_crate(&extern_crate.name) {
- if m == self.def_map.module_id(current_module_id) {
- cov_mark::hit!(ignore_macro_use_extern_crate_self);
- return;
- }
-
- cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
- self.import_all_macros_exported(current_module_id, m.krate);
- }
- }
-
- /// Import all exported macros from another crate
+ /// Import exported macros from another crate. `names`, if `Some(_)`, specifies the name of
+ /// macros to be imported. Otherwise this method imports all exported macros.
///
/// Exported macros are just all macros in the root module scope.
/// Note that it contains not only all `#[macro_export]` macros, but also all aliases
/// created by `use` in the root module, ignoring the visibility of `use`.
- fn import_all_macros_exported(&mut self, current_module_id: LocalModuleId, krate: CrateId) {
+ fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option<Vec<Name>>) {
let def_map = self.db.crate_def_map(krate);
- for (name, def) in def_map[def_map.root].scope.macros() {
- // `#[macro_use]` brings macros into legacy scope. Yes, even non-`macro_rules!` macros.
- self.define_legacy_macro(current_module_id, name.clone(), def);
+ // `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
+ // macros.
+ let root_scope = &def_map[DefMap::ROOT].scope;
+ if let Some(names) = names {
+ for name in names {
+ // FIXME: Report diagnostic on 404.
+ if let Some(def) = root_scope.get(&name).take_macros() {
+ self.def_map.macro_use_prelude.insert(name, def);
+ }
+ }
+ } else {
+ for (name, def) in root_scope.macros() {
+ self.def_map.macro_use_prelude.insert(name.clone(), def);
+ }
}
}
@@ -762,8 +773,9 @@ impl DefCollector<'_> {
}
fn resolve_import(&self, module_id: LocalModuleId, import: &Import) -> PartialResolvedImport {
- let _p = profile::span("resolve_import").detail(|| format!("{}", import.path));
- tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
+ let _p = profile::span("resolve_import")
+ .detail(|| format!("{}", import.path.display(self.db.upcast())));
+ tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
if import.is_extern_crate {
let name = import
.path
@@ -785,6 +797,7 @@ impl DefCollector<'_> {
module_id,
&import.path,
BuiltinShadowMode::Module,
+ None, // An import may resolve to any kind of macro.
);
let def = res.resolved_def;
@@ -812,19 +825,12 @@ impl DefCollector<'_> {
}
}
- fn resolve_extern_crate(&self, name: &Name) -> Option<ModuleId> {
+ fn resolve_extern_crate(&self, name: &Name) -> Option<CrateRootModuleId> {
if *name == name!(self) {
cov_mark::hit!(extern_crate_self_as);
- let root = match self.def_map.block {
- Some(_) => {
- let def_map = self.def_map.crate_root(self.db).def_map(self.db);
- def_map.module_id(def_map.root())
- }
- None => self.def_map.module_id(self.def_map.root()),
- };
- Some(root)
+ Some(self.def_map.crate_root())
} else {
- self.deps.get(name).copied()
+ self.deps.get(name).map(|dep| CrateRootModuleId { krate: dep.crate_id })
}
}
@@ -863,11 +869,16 @@ impl DefCollector<'_> {
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
if import.is_extern_crate
&& self.def_map.block.is_none()
- && module_id == self.def_map.root
+ && module_id == DefMap::ROOT
{
if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name)
{
- self.def_map.extern_prelude.insert(name.clone(), def);
+ if let Ok(def) = def.try_into() {
+ Arc::get_mut(&mut self.def_map.data)
+ .unwrap()
+ .extern_prelude
+ .insert(name.clone(), def);
+ }
}
}
@@ -1082,7 +1093,14 @@ impl DefCollector<'_> {
resolved.push((directive.module_id, directive.depth, directive.container, call_id));
};
let mut res = ReachedFixedPoint::Yes;
+ // Retain unresolved macros after this round of resolution.
macros.retain(|directive| {
+ let subns = match &directive.kind {
+ MacroDirectiveKind::FnLike { .. } => MacroSubNs::Bang,
+ MacroDirectiveKind::Attr { .. } | MacroDirectiveKind::Derive { .. } => {
+ MacroSubNs::Attr
+ }
+ };
let resolver = |path| {
let resolved_res = self.def_map.resolve_path_fp_with_macro(
self.db,
@@ -1090,6 +1108,7 @@ impl DefCollector<'_> {
directive.module_id,
&path,
BuiltinShadowMode::Module,
+ Some(subns),
);
resolved_res
.resolved_def
@@ -1101,15 +1120,15 @@ impl DefCollector<'_> {
match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
let call_id = macro_call_as_call_id(
- self.db,
+ self.db.upcast(),
ast_id,
*expand_to,
self.def_map.krate,
resolver_def_id,
- &mut |_err| (),
);
- if let Ok(Ok(call_id)) = call_id {
+ if let Ok(Some(call_id)) = call_id {
push_resolved(directive, call_id);
+
res = ReachedFixedPoint::No;
return false;
}
@@ -1134,7 +1153,7 @@ impl DefCollector<'_> {
// Record its helper attributes.
if def_id.krate != self.def_map.krate {
let def_map = self.db.crate_def_map(def_id.krate);
- if let Some(helpers) = def_map.exported_derives.get(&def_id) {
+ if let Some(helpers) = def_map.data.exported_derives.get(&def_id) {
self.def_map
.derive_helpers_in_scope
.entry(ast_id.ast_id.map(|it| it.upcast()))
@@ -1214,7 +1233,19 @@ impl DefCollector<'_> {
};
let ast_id = ast_id.with_value(ast_adt_id);
- match attr.parse_path_comma_token_tree() {
+ let extend_unhygenic;
+ let hygiene = if file_id.is_macro() {
+ self.hygienes
+ .entry(file_id)
+ .or_insert_with(|| Hygiene::new(self.db.upcast(), file_id))
+ } else {
+ // Avoid heap allocation (`Hygiene` embraces `Arc`) and hash map entry
+ // when we're in an oridinary (non-macro) file.
+ extend_unhygenic = Hygiene::new_unhygienic();
+ &extend_unhygenic
+ };
+
+ match attr.parse_path_comma_token_tree(self.db.upcast(), hygiene) {
Some(derive_macros) => {
let mut len = 0;
for (idx, path) in derive_macros.enumerate() {
@@ -1241,7 +1272,6 @@ impl DefCollector<'_> {
attr,
self.def_map.krate,
def,
- true,
);
self.def_map.modules[directive.module_id]
.scope
@@ -1261,18 +1291,12 @@ impl DefCollector<'_> {
}
// Not resolved to a derive helper or the derive attribute, so try to treat as a normal attribute.
- let call_id = attr_macro_as_call_id(
- self.db,
- file_ast_id,
- attr,
- self.def_map.krate,
- def,
- false,
- );
+ let call_id =
+ attr_macro_as_call_id(self.db, file_ast_id, attr, self.def_map.krate, def);
let loc: MacroCallLoc = self.db.lookup_intern_macro_call(call_id);
// If proc attribute macro expansion is disabled, skip expanding it here
- if !self.db.enable_proc_attr_macros() {
+ if !self.db.expand_proc_attr_macros() {
self.def_map.diagnostics.push(DefDiagnostic::unresolved_proc_macro(
directive.module_id,
loc.kind,
@@ -1345,25 +1369,31 @@ impl DefCollector<'_> {
let file_id = macro_call_id.as_file();
// First, fetch the raw expansion result for purposes of error reporting. This goes through
- // `macro_expand_error` to avoid depending on the full expansion result (to improve
+ // `parse_macro_expansion_error` to avoid depending on the full expansion result (to improve
// incrementality).
- let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
- let err = self.db.macro_expand_error(macro_call_id);
+ let ExpandResult { value, err } = self.db.parse_macro_expansion_error(macro_call_id);
if let Some(err) = err {
+ let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
let diag = match err {
+ // why is this reported here?
hir_expand::ExpandError::UnresolvedProcMacro(krate) => {
always!(krate == loc.def.krate);
- // Missing proc macros are non-fatal, so they are handled specially.
DefDiagnostic::unresolved_proc_macro(module_id, loc.kind.clone(), loc.def.krate)
}
- _ => DefDiagnostic::macro_error(module_id, loc.kind, err.to_string()),
+ _ => DefDiagnostic::macro_error(module_id, loc.kind.clone(), err.to_string()),
};
self.def_map.diagnostics.push(diag);
}
+ if let errors @ [_, ..] = &*value {
+ let loc: MacroCallLoc = self.db.lookup_intern_macro_call(macro_call_id);
+ let diag = DefDiagnostic::macro_expansion_parse_error(module_id, loc.kind, &errors);
+ self.def_map.diagnostics.push(diag);
+ }
// Then, fetch and process the item tree. This will reuse the expansion result from above.
let item_tree = self.db.file_item_tree(file_id);
+
let mod_dir = self.mod_dirs[&module_id].clone();
ModCollector {
def_collector: &mut *self,
@@ -1384,8 +1414,9 @@ impl DefCollector<'_> {
for directive in &self.unresolved_macros {
match &directive.kind {
MacroDirectiveKind::FnLike { ast_id, expand_to } => {
+ // FIXME: we shouldn't need to re-resolve the macro here just to get the unresolved error!
let macro_call_as_call_id = macro_call_as_call_id(
- self.db,
+ self.db.upcast(),
ast_id,
*expand_to,
self.def_map.krate,
@@ -1396,13 +1427,13 @@ impl DefCollector<'_> {
directive.module_id,
&path,
BuiltinShadowMode::Module,
+ Some(MacroSubNs::Bang),
);
resolved_res
.resolved_def
.take_macros()
.map(|it| macro_id_to_def_id(self.db, it))
},
- &mut |_| (),
);
if let Err(UnresolvedMacro { path }) = macro_call_as_call_id {
self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call(
@@ -1489,6 +1520,7 @@ impl ModCollector<'_, '_> {
fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
let krate = self.def_collector.def_map.krate;
+ let is_crate_root = self.module_id == DefMap::ROOT;
// Note: don't assert that inserted value is fresh: it's simply not true
// for macros.
@@ -1496,28 +1528,22 @@ impl ModCollector<'_, '_> {
// Prelude module is always considered to be `#[macro_use]`.
if let Some(prelude_module) = self.def_collector.def_map.prelude {
- if prelude_module.krate != krate {
+ if prelude_module.krate != krate && is_crate_root {
cov_mark::hit!(prelude_is_macro_use);
- self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate);
+ self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None);
}
}
// This should be processed eagerly instead of deferred to resolving.
// `#[macro_use] extern crate` is hoisted to imports macros before collecting
// any other items.
- for &item in items {
- let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
- if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
+ //
+ // If we're not at the crate root, `macro_use`d extern crates are an error so let's just
+ // ignore them.
+ if is_crate_root {
+ for &item in items {
if let ModItem::ExternCrate(id) = item {
- let import = &self.item_tree[id];
- let attrs = self.item_tree.attrs(
- self.def_collector.db,
- krate,
- ModItem::from(id).into(),
- );
- if attrs.by_key("macro_use").exists() {
- self.def_collector.import_macros_from_extern_crate(self.module_id, import);
- }
+ self.process_macro_use_extern_crate(id);
}
}
}
@@ -1610,14 +1636,12 @@ impl ModCollector<'_, '_> {
FunctionLoc { container, id: ItemTreeId::new(self.tree_id, id) }.intern(db);
let vis = resolve_vis(def_map, &self.item_tree[it.visibility]);
- if self.def_collector.is_proc_macro && self.module_id == def_map.root {
+ if self.def_collector.is_proc_macro && self.module_id == DefMap::ROOT {
if let Some(proc_macro) = attrs.parse_proc_macro_decl(&it.name) {
- let crate_root = def_map.module_id(def_map.root);
self.def_collector.export_proc_macro(
proc_macro,
ItemTreeId::new(self.tree_id, id),
fn_id,
- crate_root,
);
}
}
@@ -1744,6 +1768,50 @@ impl ModCollector<'_, '_> {
}
}
+ fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId<ExternCrate>) {
+ let db = self.def_collector.db;
+ let attrs = self.item_tree.attrs(
+ db,
+ self.def_collector.def_map.krate,
+ ModItem::from(extern_crate).into(),
+ );
+ if let Some(cfg) = attrs.cfg() {
+ if !self.is_cfg_enabled(&cfg) {
+ return;
+ }
+ }
+
+ let target_crate =
+ match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) {
+ Some(m) if m.krate == self.def_collector.def_map.krate => {
+ cov_mark::hit!(ignore_macro_use_extern_crate_self);
+ return;
+ }
+ Some(m) => m.krate,
+ None => return,
+ };
+
+ cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
+
+ let mut single_imports = Vec::new();
+ let hygiene = Hygiene::new_unhygienic();
+ for attr in attrs.by_key("macro_use").attrs() {
+ let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else {
+ // `#[macro_use]` (without any paths) found, forget collected names and just import
+ // all visible macros.
+ self.def_collector.import_macros_from_extern_crate(target_crate, None);
+ return;
+ };
+ for path in paths {
+ if let Some(name) = path.as_ident() {
+ single_imports.push(name.clone());
+ }
+ }
+ }
+
+ self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports));
+ }
+
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
let path_attr = attrs.by_key("path").string_value();
let is_macro_use = attrs.by_key("macro_use").exists();
@@ -1844,7 +1912,6 @@ impl ModCollector<'_, '_> {
let vis = def_map
.resolve_visibility(self.def_collector.db, self.module_id, visibility, false)
.unwrap_or(Visibility::Public);
- let modules = &mut def_map.modules;
let origin = match definition {
None => ModuleOrigin::Inline {
definition: declaration,
@@ -1858,11 +1925,16 @@ impl ModCollector<'_, '_> {
},
};
+ let modules = &mut def_map.modules;
let res = modules.alloc(ModuleData::new(origin, vis));
modules[res].parent = Some(self.module_id);
- for (name, mac) in modules[self.module_id].scope.collect_legacy_macros() {
- for &mac in &mac {
- modules[res].scope.define_legacy_macro(name.clone(), mac);
+
+ if let Some((target, source)) = Self::borrow_modules(modules.as_mut(), res, self.module_id)
+ {
+ for (name, macs) in source.scope.legacy_macros() {
+ for &mac in macs {
+ target.scope.define_legacy_macro(name.clone(), mac);
+ }
}
}
modules[self.module_id].children.insert(name.clone(), res);
@@ -1919,7 +1991,10 @@ impl ModCollector<'_, '_> {
if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) {
continue;
}
- tracing::debug!("non-builtin attribute {}", attr.path);
+ tracing::debug!(
+ "non-builtin attribute {}",
+ attr.path.display(self.def_collector.db.upcast())
+ );
let ast_id = AstIdWithPath::new(
self.file_id(),
@@ -2053,8 +2128,8 @@ impl ModCollector<'_, '_> {
stdx::always!(
name == mac.name,
"built-in macro {} has #[rustc_builtin_macro] which declares different name {}",
- mac.name,
- name
+ mac.name.display(self.def_collector.db.upcast()),
+ name.display(self.def_collector.db.upcast())
);
helpers_opt = Some(helpers);
}
@@ -2089,73 +2164,60 @@ impl ModCollector<'_, '_> {
&self.item_tree[mac.visibility],
);
if let Some(helpers) = helpers_opt {
- self.def_collector
- .def_map
- .exported_derives
- .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers);
+ if self.def_collector.def_map.block.is_none() {
+ Arc::get_mut(&mut self.def_collector.def_map.data)
+ .unwrap()
+ .exported_derives
+ .insert(macro_id_to_def_id(self.def_collector.db, macro_id.into()), helpers);
+ }
}
}
fn collect_macro_call(&mut self, mac: &MacroCall, container: ItemContainerId) {
let ast_id = AstIdWithPath::new(self.file_id(), mac.ast_id, ModPath::clone(&mac.path));
+ let db = self.def_collector.db;
+
+ // FIXME: Immediately expanding in "Case 1" is insufficient since "Case 2" may also define
+ // new legacy macros that create textual scopes. We need a way to resolve names in textual
+ // scopes without eager expansion.
- // Case 1: try to resolve in legacy scope and expand macro_rules
- let mut error = None;
- match macro_call_as_call_id(
- self.def_collector.db,
+ // Case 1: try to resolve macro calls with single-segment name and expand macro_rules
+ if let Ok(res) = macro_call_as_call_id(
+ db.upcast(),
&ast_id,
mac.expand_to,
self.def_collector.def_map.krate,
|path| {
path.as_ident().and_then(|name| {
- self.def_collector.def_map.with_ancestor_maps(
- self.def_collector.db,
- self.module_id,
- &mut |map, module| {
- map[module]
- .scope
- .get_legacy_macro(name)
- .and_then(|it| it.last())
- .map(|&it| macro_id_to_def_id(self.def_collector.db, it))
- },
- )
+ let def_map = &self.def_collector.def_map;
+ def_map
+ .with_ancestor_maps(db, self.module_id, &mut |map, module| {
+ map[module].scope.get_legacy_macro(name)?.last().copied()
+ })
+ .or_else(|| def_map[self.module_id].scope.get(name).take_macros())
+ .or_else(|| def_map.macro_use_prelude.get(name).copied())
+ .filter(|&id| {
+ sub_namespace_match(
+ Some(MacroSubNs::from_id(db, id)),
+ Some(MacroSubNs::Bang),
+ )
+ })
+ .map(|it| macro_id_to_def_id(self.def_collector.db, it))
})
},
- &mut |err| {
- error.get_or_insert(err);
- },
) {
- Ok(Ok(macro_call_id)) => {
- // Legacy macros need to be expanded immediately, so that any macros they produce
- // are in scope.
+ // Legacy macros need to be expanded immediately, so that any macros they produce
+ // are in scope.
+ if let Some(val) = res {
self.def_collector.collect_macro_expansion(
self.module_id,
- macro_call_id,
+ val,
self.macro_depth + 1,
container,
);
-
- if let Some(err) = error {
- self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
- self.module_id,
- MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
- err.to_string(),
- ));
- }
-
- return;
}
- Ok(Err(_)) => {
- // Built-in macro failed eager expansion.
- self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
- self.module_id,
- MacroCallKind::FnLike { ast_id: ast_id.ast_id, expand_to: mac.expand_to },
- error.unwrap().to_string(),
- ));
- return;
- }
- Err(UnresolvedMacro { .. }) => (),
+ return;
}
// Case 2: resolve in module scope, expand during name resolution.
@@ -2168,14 +2230,40 @@ impl ModCollector<'_, '_> {
}
fn import_all_legacy_macros(&mut self, module_id: LocalModuleId) {
- let macros = self.def_collector.def_map[module_id].scope.collect_legacy_macros();
- for (name, macs) in macros {
+ let Some((source, target)) = Self::borrow_modules(self.def_collector.def_map.modules.as_mut(), module_id, self.module_id) else {
+ return
+ };
+
+ for (name, macs) in source.scope.legacy_macros() {
macs.last().map(|&mac| {
- self.def_collector.define_legacy_macro(self.module_id, name.clone(), mac)
+ target.scope.define_legacy_macro(name.clone(), mac);
});
}
}
+ /// Mutably borrow two modules at once, retu
+ fn borrow_modules(
+ modules: &mut [ModuleData],
+ a: LocalModuleId,
+ b: LocalModuleId,
+ ) -> Option<(&mut ModuleData, &mut ModuleData)> {
+ let a = a.into_raw().into_u32() as usize;
+ let b = b.into_raw().into_u32() as usize;
+
+ let (a, b) = match a.cmp(&b) {
+ Ordering::Equal => return None,
+ Ordering::Less => {
+ let (prefix, b) = modules.split_at_mut(b);
+ (&mut prefix[a], &mut b[0])
+ }
+ Ordering::Greater => {
+ let (prefix, a) = modules.split_at_mut(a);
+ (&mut a[0], &mut prefix[b])
+ }
+ };
+ Some((a, b))
+ }
+
fn is_cfg_enabled(&self, cfg: &CfgExpr) -> bool {
self.def_collector.cfg_options.check(cfg) != Some(false)
}
@@ -2215,10 +2303,11 @@ mod tests {
unresolved_macros: Vec::new(),
mod_dirs: FxHashMap::default(),
cfg_options: &CfgOptions::default(),
- proc_macros: Default::default(),
+ proc_macros: Ok(vec![]),
from_glob_import: Default::default(),
skip_attrs: Default::default(),
is_proc_macro: false,
+ hygienes: FxHashMap::default(),
};
collector.seed_with_top_level();
collector.collect();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
index b024d7c67..18b424255 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
@@ -4,7 +4,10 @@ use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
use hir_expand::{attrs::AttrId, MacroCallKind};
use la_arena::Idx;
-use syntax::ast::{self, AnyHasAttrs};
+use syntax::{
+ ast::{self, AnyHasAttrs},
+ SyntaxError,
+};
use crate::{
item_tree::{self, ItemTreeId},
@@ -29,11 +32,15 @@ pub enum DefDiagnosticKind {
MacroError { ast: MacroCallKind, message: String },
+ MacroExpansionParseError { ast: MacroCallKind, errors: Box<[SyntaxError]> },
+
UnimplementedBuiltinMacro { ast: AstId<ast::Macro> },
InvalidDeriveTarget { ast: AstId<ast::Item>, id: usize },
MalformedDerive { ast: AstId<ast::Adt>, id: usize },
+
+ MacroDefError { ast: AstId<ast::Macro>, message: String },
}
#[derive(Debug, PartialEq, Eq)]
@@ -81,7 +88,8 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
}
- pub(super) fn unresolved_proc_macro(
+ // FIXME: Whats the difference between this and unresolved_macro_call
+ pub(crate) fn unresolved_proc_macro(
container: LocalModuleId,
ast: MacroCallKind,
krate: CrateId,
@@ -89,7 +97,7 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::UnresolvedProcMacro { ast, krate } }
}
- pub(super) fn macro_error(
+ pub(crate) fn macro_error(
container: LocalModuleId,
ast: MacroCallKind,
message: String,
@@ -97,7 +105,22 @@ impl DefDiagnostic {
Self { in_module: container, kind: DefDiagnosticKind::MacroError { ast, message } }
}
- pub(super) fn unresolved_macro_call(
+ pub(crate) fn macro_expansion_parse_error(
+ container: LocalModuleId,
+ ast: MacroCallKind,
+ errors: &[SyntaxError],
+ ) -> Self {
+ Self {
+ in_module: container,
+ kind: DefDiagnosticKind::MacroExpansionParseError {
+ ast,
+ errors: errors.to_vec().into_boxed_slice(),
+ },
+ }
+ }
+
+ // FIXME: Whats the difference between this and unresolved_proc_macro
+ pub(crate) fn unresolved_macro_call(
container: LocalModuleId,
ast: MacroCallKind,
path: ModPath,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
index 51c565fe1..2dcc2c30f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs
@@ -74,12 +74,20 @@ impl ModDir {
candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
}
None if file_id.is_include_macro(db.upcast()) => {
- candidate_files.push(format!("{name}.rs"));
- candidate_files.push(format!("{name}/mod.rs"));
+ candidate_files.push(format!("{}.rs", name.display(db.upcast())));
+ candidate_files.push(format!("{}/mod.rs", name.display(db.upcast())));
}
None => {
- candidate_files.push(format!("{}{name}.rs", self.dir_path.0));
- candidate_files.push(format!("{}{name}/mod.rs", self.dir_path.0));
+ candidate_files.push(format!(
+ "{}{}.rs",
+ self.dir_path.0,
+ name.display(db.upcast())
+ ));
+ candidate_files.push(format!(
+ "{}{}/mod.rs",
+ self.dir_path.0,
+ name.display(db.upcast())
+ ));
}
};
@@ -91,7 +99,7 @@ impl ModDir {
let (dir_path, root_non_dir_owner) = if is_mod_rs || attr_path.is_some() {
(DirPath::empty(), false)
} else {
- (DirPath::new(format!("{name}/")), true)
+ (DirPath::new(format!("{}/", name.display(db.upcast()))), true)
};
if let Some(mod_dir) = self.child(dir_path, root_non_dir_owner) {
return Ok((file_id, is_mod_rs, mod_dir));
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
index 25478481d..5f6163175 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
@@ -16,11 +16,11 @@ use hir_expand::name::Name;
use crate::{
db::DefDatabase,
item_scope::BUILTIN_SCOPE,
- nameres::{BuiltinShadowMode, DefMap},
+ nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs},
path::{ModPath, PathKind},
per_ns::PerNs,
visibility::{RawVisibility, Visibility},
- AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
+ AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -58,18 +58,22 @@ impl ResolvePathResult {
}
}
-impl DefMap {
- pub(super) fn resolve_name_in_extern_prelude(
- &self,
+impl PerNs {
+ pub(super) fn filter_macro(
+ mut self,
db: &dyn DefDatabase,
- name: &Name,
- ) -> Option<ModuleId> {
- match self.block {
- Some(_) => self.crate_root(db).def_map(db).extern_prelude.get(name).copied(),
- None => self.extern_prelude.get(name).copied(),
- }
+ expected: Option<MacroSubNs>,
+ ) -> Self {
+ self.macros = self.macros.filter(|&(id, _)| {
+ let this = MacroSubNs::from_id(db, id);
+ sub_namespace_match(Some(this), expected)
+ });
+
+ self
}
+}
+impl DefMap {
pub(crate) fn resolve_visibility(
&self,
db: &dyn DefDatabase,
@@ -83,7 +87,7 @@ impl DefMap {
let mut vis = match visibility {
RawVisibility::Module(path) => {
let (result, remaining) =
- self.resolve_path(db, original_module, path, BuiltinShadowMode::Module);
+ self.resolve_path(db, original_module, path, BuiltinShadowMode::Module, None);
if remaining.is_some() {
return None;
}
@@ -106,7 +110,7 @@ impl DefMap {
// ...unless we're resolving visibility for an associated item in an impl.
if self.block_id() != m.block && !within_impl {
cov_mark::hit!(adjust_vis_in_block_def_map);
- vis = Visibility::Module(self.module_id(self.root()));
+ vis = Visibility::Module(self.module_id(Self::ROOT));
tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis);
}
}
@@ -124,6 +128,9 @@ impl DefMap {
mut original_module: LocalModuleId,
path: &ModPath,
shadow: BuiltinShadowMode,
+ // Pass `MacroSubNs` if we know we're resolving macro names and which kind of macro we're
+ // resolving them to. Pass `None` otherwise, e.g. when we're resolving import paths.
+ expected_macro_subns: Option<MacroSubNs>,
) -> ResolvePathResult {
let mut result = ResolvePathResult::empty(ReachedFixedPoint::No);
@@ -136,6 +143,7 @@ impl DefMap {
original_module,
path,
shadow,
+ expected_macro_subns,
);
// Merge `new` into `result`.
@@ -154,7 +162,7 @@ impl DefMap {
match &current_map.block {
Some(block) => {
original_module = block.parent.local_id;
- arc = block.parent.def_map(db);
+ arc = block.parent.def_map(db, current_map.krate);
current_map = &*arc;
}
None => return result,
@@ -169,11 +177,15 @@ impl DefMap {
original_module: LocalModuleId,
path: &ModPath,
shadow: BuiltinShadowMode,
+ expected_macro_subns: Option<MacroSubNs>,
) -> ResolvePathResult {
let graph = db.crate_graph();
let _cx = stdx::panic_context::enter(format!(
- "DefMap {:?} crate_name={:?} block={:?} path={path}",
- self.krate, graph[self.krate].display_name, self.block
+ "DefMap {:?} crate_name={:?} block={:?} path={}",
+ self.krate,
+ graph[self.krate].display_name,
+ self.block,
+ path.display(db.upcast())
));
let mut segments = path.segments().iter().enumerate();
@@ -181,21 +193,21 @@ impl DefMap {
PathKind::DollarCrate(krate) => {
if krate == self.krate {
cov_mark::hit!(macro_dollar_crate_self);
- PerNs::types(self.crate_root(db).into(), Visibility::Public)
+ PerNs::types(self.crate_root().into(), Visibility::Public)
} else {
let def_map = db.crate_def_map(krate);
- let module = def_map.module_id(def_map.root);
+ let module = def_map.module_id(Self::ROOT);
cov_mark::hit!(macro_dollar_crate_other);
PerNs::types(module.into(), Visibility::Public)
}
}
- PathKind::Crate => PerNs::types(self.crate_root(db).into(), Visibility::Public),
+ PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public),
// plain import or absolute path in 2015: crate-relative with
// fallback to extern prelude (with the simplification in
// rust-lang/rust#57745)
// FIXME there must be a nicer way to write this condition
PathKind::Plain | PathKind::Abs
- if self.edition == Edition::Edition2015
+ if self.data.edition == Edition::Edition2015
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
{
let (_, segment) = match segments.next() {
@@ -220,7 +232,13 @@ impl DefMap {
if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module };
tracing::debug!("resolving {:?} in module", segment);
- self.resolve_name_in_module(db, original_module, segment, prefer_module)
+ self.resolve_name_in_module(
+ db,
+ original_module,
+ segment,
+ prefer_module,
+ expected_macro_subns,
+ )
}
PathKind::Super(lvl) => {
let mut module = original_module;
@@ -236,16 +254,20 @@ impl DefMap {
);
tracing::debug!(
"`super` path: {} -> {} in parent map",
- path,
- new_path
- );
- return block.parent.def_map(db).resolve_path_fp_with_macro(
- db,
- mode,
- block.parent.local_id,
- &new_path,
- shadow,
+ path.display(db.upcast()),
+ new_path.display(db.upcast())
);
+ return block
+ .parent
+ .def_map(db, self.krate)
+ .resolve_path_fp_with_macro(
+ db,
+ mode,
+ block.parent.local_id,
+ &new_path,
+ shadow,
+ expected_macro_subns,
+ );
}
None => {
tracing::debug!("super path in root module");
@@ -271,7 +293,7 @@ impl DefMap {
Some((_, segment)) => segment,
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
};
- if let Some(&def) = self.extern_prelude.get(segment) {
+ if let Some(&def) = self.data.extern_prelude.get(segment) {
tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
PerNs::types(def.into(), Visibility::Public)
} else {
@@ -303,7 +325,12 @@ impl DefMap {
);
tracing::debug!("resolving {:?} in other crate", path);
let defp_map = module.def_map(db);
- let (def, s) = defp_map.resolve_path(db, module.local_id, &path, shadow);
+ // Macro sub-namespaces only matter when resolving single-segment paths
+ // because `macro_use` and other preludes should be taken into account. At
+ // this point, we know we're resolving a multi-segment path so macro kind
+ // expectation is discarded.
+ let (def, s) =
+ defp_map.resolve_path(db, module.local_id, &path, shadow, None);
return ResolvePathResult::with(
def,
ReachedFixedPoint::Yes,
@@ -331,11 +358,11 @@ impl DefMap {
Some(local_id) => {
let variant = EnumVariantId { parent: e, local_id };
match &*enum_data.variants[local_id].variant_data {
- crate::adt::VariantData::Record(_) => {
+ crate::data::adt::VariantData::Record(_) => {
PerNs::types(variant.into(), Visibility::Public)
}
- crate::adt::VariantData::Tuple(_)
- | crate::adt::VariantData::Unit => {
+ crate::data::adt::VariantData::Tuple(_)
+ | crate::data::adt::VariantData::Unit => {
PerNs::both(variant.into(), variant.into(), Visibility::Public)
}
}
@@ -381,19 +408,24 @@ impl DefMap {
module: LocalModuleId,
name: &Name,
shadow: BuiltinShadowMode,
+ expected_macro_subns: Option<MacroSubNs>,
) -> PerNs {
// Resolve in:
// - legacy scope of macro
// - current module / scope
- // - extern prelude
+ // - extern prelude / macro_use prelude
// - std prelude
let from_legacy_macro = self[module]
.scope
.get_legacy_macro(name)
// FIXME: shadowing
.and_then(|it| it.last())
- .map_or_else(PerNs::none, |&m| PerNs::macros(m, Visibility::Public));
- let from_scope = self[module].scope.get(name);
+ .copied()
+ .filter(|&id| {
+ sub_namespace_match(Some(MacroSubNs::from_id(db, id)), expected_macro_subns)
+ })
+ .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
+ let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns);
let from_builtin = match self.block {
Some(_) => {
// Only resolve to builtins in the root `DefMap`.
@@ -410,13 +442,27 @@ impl DefMap {
};
let extern_prelude = || {
- self.extern_prelude
+ if self.block.is_some() {
+ // Don't resolve extern prelude in block `DefMap`s.
+ return PerNs::none();
+ }
+ self.data
+ .extern_prelude
.get(name)
.map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
};
+ let macro_use_prelude = || {
+ self.macro_use_prelude
+ .get(name)
+ .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public))
+ };
let prelude = || self.resolve_in_prelude(db, name);
- from_legacy_macro.or(from_scope_or_builtin).or_else(extern_prelude).or_else(prelude)
+ from_legacy_macro
+ .or(from_scope_or_builtin)
+ .or_else(extern_prelude)
+ .or_else(macro_use_prelude)
+ .or_else(prelude)
}
fn resolve_name_in_crate_root_or_extern_prelude(
@@ -426,13 +472,20 @@ impl DefMap {
) -> PerNs {
let from_crate_root = match self.block {
Some(_) => {
- let def_map = self.crate_root(db).def_map(db);
- def_map[def_map.root].scope.get(name)
+ let def_map = self.crate_root().def_map(db);
+ def_map[Self::ROOT].scope.get(name)
}
- None => self[self.root].scope.get(name),
+ None => self[Self::ROOT].scope.get(name),
};
let from_extern_prelude = || {
- self.resolve_name_in_extern_prelude(db, name)
+ if self.block.is_some() {
+ // Don't resolve extern prelude in block `DefMap`s.
+ return PerNs::none();
+ }
+ self.data
+ .extern_prelude
+ .get(name)
+ .copied()
.map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public))
};
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
index caad4a1f3..751b7beaa 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/proc_macro.rs
@@ -52,7 +52,7 @@ impl Attrs {
}
// This fn is intended for `#[proc_macro_derive(..)]` and `#[rustc_builtin_macro(..)]`, which have
-// the same strucuture.
+// the same structure.
#[rustfmt::skip]
pub(crate) fn parse_macro_name_and_helper_attrs(tt: &[TokenTree]) -> Option<(Name, Box<[Name]>)> {
match tt {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
index 8a27c60df..dd7c3c363 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
@@ -4,10 +4,9 @@ mod macros;
mod mod_resolution;
mod primitives;
-use std::sync::Arc;
-
use base_db::{fixture::WithFixture, SourceDatabase};
use expect_test::{expect, Expect};
+use triomphe::Arc;
use crate::{db::DefDatabase, test_db::TestDB};
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
index 13e6825f8..4931c36bb 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
@@ -1,8 +1,7 @@
-use std::sync::Arc;
-
use base_db::SourceDatabaseExt;
+use triomphe::Arc;
-use crate::{AdtId, ModuleDefId};
+use crate::{db::DefDatabase, AdtId, ModuleDefId};
use super::*;
@@ -15,7 +14,7 @@ fn check_def_map_is_not_recomputed(ra_fixture_initial: &str, ra_fixture_change:
});
assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
}
- db.set_file_text(pos.file_id, Arc::new(ra_fixture_change.to_string()));
+ db.set_file_text(pos.file_id, Arc::from(ra_fixture_change));
{
let events = db.log_executed(|| {
@@ -96,7 +95,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
});
assert!(format!("{events:?}").contains("crate_def_map"), "{events:#?}")
}
- db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string()));
+ db.set_file_text(pos.file_id, Arc::from("m!(Y);"));
{
let events = db.log_executed(|| {
@@ -109,7 +108,7 @@ fn typing_inside_a_macro_should_not_invalidate_def_map() {
}
#[test]
-fn typing_inside_a_function_should_not_invalidate_expansions() {
+fn typing_inside_a_function_should_not_invalidate_item_expansions() {
let (mut db, pos) = TestDB::with_position(
r#"
//- /lib.rs
@@ -140,7 +139,7 @@ m!(Z);
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
assert_eq!(n_recalculated_item_trees, 6);
let n_reparsed_macros =
- events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
+ events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
assert_eq!(n_reparsed_macros, 3);
}
@@ -150,7 +149,7 @@ fn quux() { 92 }
m!(Y);
m!(Z);
"#;
- db.set_file_text(pos.file_id, Arc::new(new_text.to_string()));
+ db.set_file_text(pos.file_id, Arc::from(new_text));
{
let events = db.log_executed(|| {
@@ -161,7 +160,7 @@ m!(Z);
let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
assert_eq!(n_recalculated_item_trees, 1);
let n_reparsed_macros =
- events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
+ events.iter().filter(|it| it.contains("parse_macro_expansion(")).count();
assert_eq!(n_reparsed_macros, 0);
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
index a4ccd14cb..f4cca8d68 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
@@ -260,6 +260,72 @@ mod priv_mod {
}
#[test]
+fn macro_use_filter() {
+ check(
+ r#"
+//- /main.rs crate:main deps:empty,multiple,all
+#[macro_use()]
+extern crate empty;
+
+foo_not_imported!();
+
+#[macro_use(bar1)]
+#[macro_use()]
+#[macro_use(bar2, bar3)]
+extern crate multiple;
+
+bar1!();
+bar2!();
+bar3!();
+bar_not_imported!();
+
+#[macro_use(baz1)]
+#[macro_use]
+#[macro_use(baz2)]
+extern crate all;
+
+baz1!();
+baz2!();
+baz3!();
+
+//- /empty.rs crate:empty
+#[macro_export]
+macro_rules! foo_not_imported { () => { struct NotOkFoo; } }
+
+//- /multiple.rs crate:multiple
+#[macro_export]
+macro_rules! bar1 { () => { struct OkBar1; } }
+#[macro_export]
+macro_rules! bar2 { () => { struct OkBar2; } }
+#[macro_export]
+macro_rules! bar3 { () => { struct OkBar3; } }
+#[macro_export]
+macro_rules! bar_not_imported { () => { struct NotOkBar; } }
+
+//- /all.rs crate:all
+#[macro_export]
+macro_rules! baz1 { () => { struct OkBaz1; } }
+#[macro_export]
+macro_rules! baz2 { () => { struct OkBaz2; } }
+#[macro_export]
+macro_rules! baz3 { () => { struct OkBaz3; } }
+"#,
+ expect![[r#"
+ crate
+ OkBar1: t v
+ OkBar2: t v
+ OkBar3: t v
+ OkBaz1: t v
+ OkBaz2: t v
+ OkBaz3: t v
+ all: t
+ empty: t
+ multiple: t
+ "#]],
+ );
+}
+
+#[test]
fn prelude_is_macro_use() {
cov_mark::check!(prelude_is_macro_use);
check(
@@ -665,6 +731,29 @@ pub struct bar;
}
#[test]
+fn macro_dollar_crate_is_correct_in_derive_meta() {
+ let map = compute_crate_def_map(
+ r#"
+//- minicore: derive, clone
+//- /main.rs crate:main deps:lib
+lib::foo!();
+
+//- /lib.rs crate:lib
+#[macro_export]
+macro_rules! foo {
+ () => {
+ #[derive($crate::Clone)]
+ struct S;
+ }
+}
+
+pub use core::clone::Clone;
+"#,
+ );
+ assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
+}
+
+#[test]
fn expand_derive() {
let map = compute_crate_def_map(
r#"
@@ -683,7 +772,7 @@ pub macro Copy {}
pub macro Clone {}
"#,
);
- assert_eq!(map.modules[map.root].scope.impls().len(), 2);
+ assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 2);
}
#[test]
@@ -726,7 +815,7 @@ pub macro derive($item:item) {}
pub macro Clone {}
"#,
);
- assert_eq!(map.modules[map.root].scope.impls().len(), 1);
+ assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
}
#[test]
@@ -991,7 +1080,7 @@ macro_rules! mbe {
#[test]
fn collects_derive_helpers() {
- let def_map = compute_crate_def_map(
+ let db = TestDB::with_files(
r#"
#![crate_type="proc-macro"]
struct TokenStream;
@@ -1002,11 +1091,13 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
}
"#,
);
+ let krate = db.crate_graph().iter().next().unwrap();
+ let def_map = db.crate_def_map(krate);
- assert_eq!(def_map.exported_derives.len(), 1);
- match def_map.exported_derives.values().next() {
+ assert_eq!(def_map.data.exported_derives.len(), 1);
+ match def_map.data.exported_derives.values().next() {
Some(helpers) => match &**helpers {
- [attr] => assert_eq!(attr.to_string(), "helper_attr"),
+ [attr] => assert_eq!(attr.display(&db).to_string(), "helper_attr"),
_ => unreachable!(),
},
_ => unreachable!(),
@@ -1169,7 +1260,7 @@ struct A;
#[test]
fn macro_use_imports_all_macro_types() {
- let def_map = compute_crate_def_map(
+ let db = TestDB::with_files(
r#"
//- /main.rs crate:main deps:lib
#[macro_use]
@@ -1192,18 +1283,153 @@ struct TokenStream;
fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
"#,
);
+ let krate = db.crate_graph().iter().next().unwrap();
+ let def_map = db.crate_def_map(krate);
+
+ let root_module = &def_map[DefMap::ROOT].scope;
+ assert!(
+ root_module.legacy_macros().count() == 0,
+ "`#[macro_use]` shouldn't bring macros into textual macro scope",
+ );
- let root = &def_map[def_map.root()].scope;
- let actual = root
- .legacy_macros()
- .sorted_by(|a, b| std::cmp::Ord::cmp(&a.0, &b.0))
- .map(|(name, _)| format!("{name}\n"))
- .collect::<String>();
+ let actual = def_map
+ .macro_use_prelude
+ .iter()
+ .map(|(name, _)| name.display(&db).to_string())
+ .sorted()
+ .join("\n");
expect![[r#"
legacy
macro20
- proc_attr
- "#]]
+ proc_attr"#]]
.assert_eq(&actual);
}
+
+#[test]
+fn non_prelude_macros_take_precedence_over_macro_use_prelude() {
+ check(
+ r#"
+//- /lib.rs edition:2021 crate:lib deps:dep,core
+#[macro_use]
+extern crate dep;
+
+macro foo() { struct Ok; }
+macro bar() { fn ok() {} }
+
+foo!();
+bar!();
+
+//- /dep.rs crate:dep
+#[macro_export]
+macro_rules! foo {
+ () => { struct NotOk; }
+}
+
+//- /core.rs crate:core
+pub mod prelude {
+ pub mod rust_2021 {
+ #[macro_export]
+ macro_rules! bar {
+ () => { fn not_ok() {} }
+ }
+ }
+}
+ "#,
+ expect![[r#"
+ crate
+ Ok: t v
+ bar: m
+ dep: t
+ foo: m
+ ok: v
+ "#]],
+ );
+}
+
+#[test]
+fn macro_use_prelude_is_eagerly_expanded() {
+ // See FIXME in `ModCollector::collect_macro_call()`.
+ check(
+ r#"
+//- /main.rs crate:main deps:lib
+#[macro_use]
+extern crate lib;
+mk_foo!();
+mod a {
+ foo!();
+}
+//- /lib.rs crate:lib
+#[macro_export]
+macro_rules! mk_foo {
+ () => {
+ macro_rules! foo {
+ () => { struct Ok; }
+ }
+ }
+}
+ "#,
+ expect![[r#"
+ crate
+ a: t
+ lib: t
+
+ crate::a
+ Ok: t v
+ "#]],
+ );
+}
+
+#[test]
+fn macro_sub_namespace() {
+ let map = compute_crate_def_map(
+ r#"
+//- minicore: derive, clone
+macro_rules! Clone { () => {} }
+macro_rules! derive { () => {} }
+
+#[derive(Clone)]
+struct S;
+ "#,
+ );
+ assert_eq!(map.modules[DefMap::ROOT].scope.impls().len(), 1);
+}
+
+#[test]
+fn macro_sub_namespace2() {
+ check(
+ r#"
+//- /main.rs edition:2021 crate:main deps:proc,core
+use proc::{foo, bar};
+
+foo!();
+bar!();
+
+//- /proc.rs crate:proc
+#![crate_type="proc-macro"]
+#[proc_macro_derive(foo)]
+pub fn foo() {}
+#[proc_macro_attribute]
+pub fn bar() {}
+
+//- /core.rs crate:core
+pub mod prelude {
+ pub mod rust_2021 {
+ pub macro foo() {
+ struct Ok;
+ }
+ pub macro bar() {
+ fn ok() {}
+ }
+ }
+}
+ "#,
+ expect![[r#"
+ crate
+ Ok: t v
+ bar: m
+ foo: m
+ ok: v
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
index a01931288..81bc0ff91 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
@@ -887,7 +887,7 @@ mod module;
//- /module.rs
#![cfg(NEVER)]
-struct AlsoShoulntAppear;
+struct AlsoShouldNotAppear;
"#,
expect![[r#"
crate
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
index f3197d180..ff4ae6954 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -7,15 +7,14 @@ use std::{
};
use crate::{
- body::LowerCtx,
- type_ref::{ConstRefOrPath, LifetimeRef},
+ lang_item::LangItemTarget,
+ lower::LowerCtx,
+ type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
};
use hir_expand::name::Name;
use intern::Interned;
use syntax::ast;
-use crate::type_ref::{TypeBound, TypeRef};
-
pub use hir_expand::mod_path::{path, ModPath, PathKind};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -36,13 +35,19 @@ impl Display for ImportAlias {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Path {
- /// Type based path like `<T>::foo`.
- /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
- type_anchor: Option<Interned<TypeRef>>,
- mod_path: Interned<ModPath>,
- /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
- generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
+pub enum Path {
+ /// A normal path
+ Normal {
+ /// Type based path like `<T>::foo`.
+ /// Note that paths like `<Type as Trait>::foo` are desugared to `Trait::<Self=Type>::foo`.
+ type_anchor: Option<Interned<TypeRef>>,
+ mod_path: Interned<ModPath>,
+ /// Invariant: the same len as `self.mod_path.segments` or `None` if all segments are `None`.
+ generic_args: Option<Box<[Option<Interned<GenericArgs>>]>>,
+ },
+ /// A link to a lang item. It is used in desugaring of things like `x?`. We can show these
+ /// links via a normal path since they might be private and not accessible in the usage place.
+ LangItem(LangItemTarget),
}
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
@@ -85,7 +90,7 @@ pub struct AssociatedTypeBinding {
pub enum GenericArg {
Type(TypeRef),
Lifetime(LifetimeRef),
- Const(ConstRefOrPath),
+ Const(ConstRef),
}
impl Path {
@@ -102,51 +107,77 @@ impl Path {
) -> Path {
let generic_args = generic_args.into();
assert_eq!(path.len(), generic_args.len());
- Path { type_anchor: None, mod_path: Interned::new(path), generic_args: Some(generic_args) }
+ Path::Normal {
+ type_anchor: None,
+ mod_path: Interned::new(path),
+ generic_args: Some(generic_args),
+ }
+ }
+
+ /// Converts a known mod path to `Path`.
+ pub fn from_known_path_with_no_generic(path: ModPath) -> Path {
+ Path::Normal { type_anchor: None, mod_path: Interned::new(path), generic_args: None }
}
pub fn kind(&self) -> &PathKind {
- &self.mod_path.kind
+ match self {
+ Path::Normal { mod_path, .. } => &mod_path.kind,
+ Path::LangItem(_) => &PathKind::Abs,
+ }
}
pub fn type_anchor(&self) -> Option<&TypeRef> {
- self.type_anchor.as_deref()
+ match self {
+ Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
+ Path::LangItem(_) => None,
+ }
}
pub fn segments(&self) -> PathSegments<'_> {
- let s = PathSegments {
- segments: self.mod_path.segments(),
- generic_args: self.generic_args.as_deref(),
+ let Path::Normal { mod_path, generic_args, .. } = self else {
+ return PathSegments {
+ segments: &[],
+ generic_args: None,
+ };
};
+ let s =
+ PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
if let Some(generic_args) = s.generic_args {
assert_eq!(s.segments.len(), generic_args.len());
}
s
}
- pub fn mod_path(&self) -> &ModPath {
- &self.mod_path
+ pub fn mod_path(&self) -> Option<&ModPath> {
+ match self {
+ Path::Normal { mod_path, .. } => Some(&mod_path),
+ Path::LangItem(_) => None,
+ }
}
pub fn qualifier(&self) -> Option<Path> {
- if self.mod_path.is_ident() {
+ let Path::Normal { mod_path, generic_args, type_anchor } = self else {
+ return None;
+ };
+ if mod_path.is_ident() {
return None;
}
- let res = Path {
- type_anchor: self.type_anchor.clone(),
+ let res = Path::Normal {
+ type_anchor: type_anchor.clone(),
mod_path: Interned::new(ModPath::from_segments(
- self.mod_path.kind,
- self.mod_path.segments()[..self.mod_path.segments().len() - 1].iter().cloned(),
+ mod_path.kind,
+ mod_path.segments()[..mod_path.segments().len() - 1].iter().cloned(),
)),
- generic_args: self.generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
+ generic_args: generic_args.as_ref().map(|it| it[..it.len() - 1].to_vec().into()),
};
Some(res)
}
pub fn is_self_type(&self) -> bool {
- self.type_anchor.is_none()
- && self.generic_args.as_deref().is_none()
- && self.mod_path.is_Self()
+ let Path::Normal { mod_path, generic_args, type_anchor } = self else {
+ return false;
+ };
+ type_anchor.is_none() && generic_args.as_deref().is_none() && mod_path.is_Self()
}
}
@@ -222,7 +253,7 @@ impl GenericArgs {
impl From<Name> for Path {
fn from(name: Name) -> Path {
- Path {
+ Path::Normal {
type_anchor: None,
mod_path: Interned::new(ModPath::from_segments(PathKind::Plain, iter::once(name))),
generic_args: None,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
index b7542bd77..1cb17ff0d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path/lower.rs
@@ -2,17 +2,15 @@
use std::iter;
-use crate::type_ref::ConstRefOrPath;
+use crate::{lower::LowerCtx, type_ref::ConstRef};
use either::Either;
use hir_expand::name::{name, AsName};
use intern::Interned;
use syntax::ast::{self, AstNode, HasTypeBounds};
-use super::AssociatedTypeBinding;
use crate::{
- body::LowerCtx,
- path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
+ path::{AssociatedTypeBinding, GenericArg, GenericArgs, ModPath, Path, PathKind},
type_ref::{LifetimeRef, TypeBound, TypeRef},
};
@@ -75,8 +73,11 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
}
// <T as Trait<A>>::Foo desugars to Trait<Self=T, A>::Foo
Some(trait_ref) => {
- let Path { mod_path, generic_args: path_generic_args, .. } =
- Path::from_src(trait_ref.path()?, ctx)?;
+ let Path::Normal { mod_path, generic_args: path_generic_args, .. } =
+ Path::from_src(trait_ref.path()?, ctx)? else
+ {
+ return None;
+ };
let num_segments = mod_path.segments().len();
kind = mod_path.kind;
@@ -157,7 +158,7 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
}
let mod_path = Interned::new(ModPath::from_segments(kind, segments));
- return Some(Path {
+ return Some(Path::Normal {
type_anchor,
mod_path,
generic_args: if generic_args.is_empty() { None } else { Some(generic_args.into()) },
@@ -188,6 +189,10 @@ pub(super) fn lower_generic_args(
args.push(GenericArg::Type(type_ref));
}
ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
+ if assoc_type_arg.param_list().is_some() {
+ // We currently ignore associated return type bounds.
+ continue;
+ }
if let Some(name_ref) = assoc_type_arg.name_ref() {
let name = name_ref.as_name();
let args = assoc_type_arg
@@ -212,7 +217,7 @@ pub(super) fn lower_generic_args(
}
}
ast::GenericArg::ConstArg(arg) => {
- let arg = ConstRefOrPath::from_expr_opt(arg.expr());
+ let arg = ConstRef::from_const_arg(lower_ctx, Some(arg));
args.push(GenericArg::Const(arg))
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
index 2d45c8c8d..0aead6f37 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
@@ -2,7 +2,7 @@
use std::fmt::{self, Write};
-use hir_expand::mod_path::PathKind;
+use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
use intern::Interned;
use itertools::Itertools;
@@ -11,11 +11,14 @@ use crate::{
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
};
-pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
+ if let Path::LangItem(x) = path {
+ return write!(buf, "$lang_item::{x:?}");
+ }
match path.type_anchor() {
Some(anchor) => {
write!(buf, "<")?;
- print_type_ref(anchor, buf)?;
+ print_type_ref(db, anchor, buf)?;
write!(buf, ">::")?;
}
None => match path.kind() {
@@ -41,10 +44,10 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
write!(buf, "::")?;
}
- write!(buf, "{}", segment.name)?;
+ write!(buf, "{}", segment.name.display(db))?;
if let Some(generics) = segment.args_and_bindings {
write!(buf, "::<")?;
- print_generic_args(generics, buf)?;
+ print_generic_args(db, generics, buf)?;
write!(buf, ">")?;
}
@@ -53,12 +56,16 @@ pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result {
Ok(())
}
-pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_generic_args(
+ db: &dyn ExpandDatabase,
+ generics: &GenericArgs,
+ buf: &mut dyn Write,
+) -> fmt::Result {
let mut first = true;
let args = if generics.has_self_type {
let (self_ty, args) = generics.args.split_first().unwrap();
write!(buf, "Self=")?;
- print_generic_arg(self_ty, buf)?;
+ print_generic_arg(db, self_ty, buf)?;
first = false;
args
} else {
@@ -69,35 +76,43 @@ pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) ->
write!(buf, ", ")?;
}
first = false;
- print_generic_arg(arg, buf)?;
+ print_generic_arg(db, arg, buf)?;
}
for binding in generics.bindings.iter() {
if !first {
write!(buf, ", ")?;
}
first = false;
- write!(buf, "{}", binding.name)?;
+ write!(buf, "{}", binding.name.display(db))?;
if !binding.bounds.is_empty() {
write!(buf, ": ")?;
- print_type_bounds(&binding.bounds, buf)?;
+ print_type_bounds(db, &binding.bounds, buf)?;
}
if let Some(ty) = &binding.type_ref {
write!(buf, " = ")?;
- print_type_ref(ty, buf)?;
+ print_type_ref(db, ty, buf)?;
}
}
Ok(())
}
-pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_generic_arg(
+ db: &dyn ExpandDatabase,
+ arg: &GenericArg,
+ buf: &mut dyn Write,
+) -> fmt::Result {
match arg {
- GenericArg::Type(ty) => print_type_ref(ty, buf),
- GenericArg::Const(c) => write!(buf, "{c}"),
- GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name),
+ GenericArg::Type(ty) => print_type_ref(db, ty, buf),
+ GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
+ GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)),
}
}
-pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Result {
+pub(crate) fn print_type_ref(
+ db: &dyn ExpandDatabase,
+ type_ref: &TypeRef,
+ buf: &mut dyn Write,
+) -> fmt::Result {
// FIXME: deduplicate with `HirDisplay` impl
match type_ref {
TypeRef::Never => write!(buf, "!")?,
@@ -108,18 +123,18 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
if i != 0 {
write!(buf, ", ")?;
}
- print_type_ref(field, buf)?;
+ print_type_ref(db, field, buf)?;
}
write!(buf, ")")?;
}
- TypeRef::Path(path) => print_path(path, buf)?,
+ TypeRef::Path(path) => print_path(db, path, buf)?,
TypeRef::RawPtr(pointee, mtbl) => {
let mtbl = match mtbl {
Mutability::Shared => "*const",
Mutability::Mut => "*mut",
};
write!(buf, "{mtbl} ")?;
- print_type_ref(pointee, buf)?;
+ print_type_ref(db, pointee, buf)?;
}
TypeRef::Reference(pointee, lt, mtbl) => {
let mtbl = match mtbl {
@@ -128,19 +143,19 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
};
write!(buf, "&")?;
if let Some(lt) = lt {
- write!(buf, "{} ", lt.name)?;
+ write!(buf, "{} ", lt.name.display(db))?;
}
write!(buf, "{mtbl}")?;
- print_type_ref(pointee, buf)?;
+ print_type_ref(db, pointee, buf)?;
}
TypeRef::Array(elem, len) => {
write!(buf, "[")?;
- print_type_ref(elem, buf)?;
- write!(buf, "; {len}]")?;
+ print_type_ref(db, elem, buf)?;
+ write!(buf, "; {}]", len.display(db))?;
}
TypeRef::Slice(elem) => {
write!(buf, "[")?;
- print_type_ref(elem, buf)?;
+ print_type_ref(db, elem, buf)?;
write!(buf, "]")?;
}
TypeRef::Fn(args_and_ret, varargs, is_unsafe) => {
@@ -154,7 +169,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
if i != 0 {
write!(buf, ", ")?;
}
- print_type_ref(typeref, buf)?;
+ print_type_ref(db, typeref, buf)?;
}
if *varargs {
if !args.is_empty() {
@@ -163,7 +178,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
write!(buf, "...")?;
}
write!(buf, ") -> ")?;
- print_type_ref(return_type, buf)?;
+ print_type_ref(db, return_type, buf)?;
}
TypeRef::Macro(_ast_id) => {
write!(buf, "<macro>")?;
@@ -171,11 +186,11 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
TypeRef::Error => write!(buf, "{{unknown}}")?,
TypeRef::ImplTrait(bounds) => {
write!(buf, "impl ")?;
- print_type_bounds(bounds, buf)?;
+ print_type_bounds(db, bounds, buf)?;
}
TypeRef::DynTrait(bounds) => {
write!(buf, "dyn ")?;
- print_type_bounds(bounds, buf)?;
+ print_type_bounds(db, bounds, buf)?;
}
}
@@ -183,6 +198,7 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re
}
pub(crate) fn print_type_bounds(
+ db: &dyn ExpandDatabase,
bounds: &[Interned<TypeBound>],
buf: &mut dyn Write,
) -> fmt::Result {
@@ -197,13 +213,13 @@ pub(crate) fn print_type_bounds(
TraitBoundModifier::None => (),
TraitBoundModifier::Maybe => write!(buf, "?")?,
}
- print_path(path, buf)?;
+ print_path(db, path, buf)?;
}
TypeBound::ForLifetime(lifetimes, path) => {
- write!(buf, "for<{}> ", lifetimes.iter().format(", "))?;
- print_path(path, buf)?;
+ write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?;
+ print_path(db, path, buf)?;
}
- TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name)?,
+ TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?,
TypeBound::Error => write!(buf, "{{unknown}}")?,
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index 61e64fc10..0d6f55411 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -1,5 +1,5 @@
//! Name resolution façade.
-use std::{fmt, hash::BuildHasherDefault, sync::Arc};
+use std::{fmt, hash::BuildHasherDefault};
use base_db::CrateId;
use hir_expand::name::{name, Name};
@@ -7,23 +7,25 @@ use indexmap::IndexMap;
use intern::Interned;
use rustc_hash::FxHashSet;
use smallvec::{smallvec, SmallVec};
+use triomphe::Arc;
use crate::{
body::scope::{ExprScopes, ScopeId},
builtin_type::BuiltinType,
db::DefDatabase,
- expr::{BindingId, ExprId, LabelId},
generics::{GenericParams, TypeOrConstParamData},
+ hir::{BindingId, ExprId, LabelId},
item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
- nameres::DefMap,
- path::{ModPath, PathKind},
+ lang_item::LangItemTarget,
+ nameres::{DefMap, MacroSubNs},
+ path::{ModPath, Path, PathKind},
per_ns::PerNs,
visibility::{RawVisibility, Visibility},
- AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId,
- FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
- LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId,
- StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId,
- VariantId,
+ AdtId, AssocItemId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId,
+ EnumVariantId, ExternBlockId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId,
+ ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId,
+ ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
+ TypeOrConstParamId, TypeOwnerId, TypeParamId, VariantId,
};
#[derive(Debug, Clone)]
@@ -78,7 +80,7 @@ enum Scope {
ExprScope(ExprScope),
}
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TypeNs {
SelfType(ImplId),
GenericParam(TypeParamId),
@@ -153,7 +155,8 @@ impl Resolver {
path: &ModPath,
) -> Option<PerNs> {
let (item_map, module) = self.item_scope();
- let (module_res, idx) = item_map.resolve_path(db, module, path, BuiltinShadowMode::Module);
+ let (module_res, idx) =
+ item_map.resolve_path(db, module, path, BuiltinShadowMode::Module, None);
match module_res.take_types()? {
ModuleDefId::TraitId(it) => {
let idx = idx?;
@@ -176,8 +179,27 @@ impl Resolver {
pub fn resolve_path_in_type_ns(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<(TypeNs, Option<usize>)> {
+ let path = match path {
+ Path::Normal { mod_path, .. } => mod_path,
+ Path::LangItem(l) => {
+ return Some((
+ match *l {
+ LangItemTarget::Union(x) => TypeNs::AdtId(x.into()),
+ LangItemTarget::TypeAlias(x) => TypeNs::TypeAliasId(x),
+ LangItemTarget::Struct(x) => TypeNs::AdtId(x.into()),
+ LangItemTarget::EnumVariant(x) => TypeNs::EnumVariantId(x),
+ LangItemTarget::EnumId(x) => TypeNs::AdtId(x.into()),
+ LangItemTarget::Trait(x) => TypeNs::TraitId(x),
+ LangItemTarget::Function(_)
+ | LangItemTarget::ImplDef(_)
+ | LangItemTarget::Static(_) => return None,
+ },
+ None,
+ ))
+ }
+ };
let first_name = path.segments().first()?;
let skip_to_mod = path.kind != PathKind::Plain;
if skip_to_mod {
@@ -217,7 +239,7 @@ impl Resolver {
pub fn resolve_path_in_type_ns_fully(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<TypeNs> {
let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
if unresolved.is_some() {
@@ -245,8 +267,24 @@ impl Resolver {
pub fn resolve_path_in_value_ns(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<ResolveValueResult> {
+ let path = match path {
+ Path::Normal { mod_path, .. } => mod_path,
+ Path::LangItem(l) => {
+ return Some(ResolveValueResult::ValueNs(match *l {
+ LangItemTarget::Function(x) => ValueNs::FunctionId(x),
+ LangItemTarget::Static(x) => ValueNs::StaticId(x),
+ LangItemTarget::Struct(x) => ValueNs::StructId(x),
+ LangItemTarget::EnumVariant(x) => ValueNs::EnumVariantId(x),
+ LangItemTarget::Union(_)
+ | LangItemTarget::ImplDef(_)
+ | LangItemTarget::TypeAlias(_)
+ | LangItemTarget::Trait(_)
+ | LangItemTarget::EnumId(_) => return None,
+ }))
+ }
+ };
let n_segments = path.segments().len();
let tmp = name![self];
let first_name = if path.is_self() { &tmp } else { path.segments().first()? };
@@ -340,7 +378,7 @@ impl Resolver {
pub fn resolve_path_in_value_ns_fully(
&self,
db: &dyn DefDatabase,
- path: &ModPath,
+ path: &Path,
) -> Option<ValueNs> {
match self.resolve_path_in_value_ns(db, path)? {
ResolveValueResult::ValueNs(it) => Some(it),
@@ -348,9 +386,17 @@ impl Resolver {
}
}
- pub fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroId> {
+ pub fn resolve_path_as_macro(
+ &self,
+ db: &dyn DefDatabase,
+ path: &ModPath,
+ expected_macro_kind: Option<MacroSubNs>,
+ ) -> Option<MacroId> {
let (item_map, module) = self.item_scope();
- item_map.resolve_path(db, module, path, BuiltinShadowMode::Other).0.take_macros()
+ item_map
+ .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind)
+ .0
+ .take_macros()
}
/// Returns a set of names available in the current scope.
@@ -415,7 +461,10 @@ impl Resolver {
res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
})
});
- def_map.extern_prelude().for_each(|(name, &def)| {
+ def_map.macro_use_prelude().for_each(|(name, def)| {
+ res.add(name, ScopeDef::ModuleDef(def.into()));
+ });
+ def_map.extern_prelude().for_each(|(name, def)| {
res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def)));
});
BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
@@ -441,7 +490,7 @@ impl Resolver {
&Scope::ImplDefScope(impl_) => {
if let Some(target_trait) = &db.impl_data(impl_).target_trait {
if let Some(TypeNs::TraitId(trait_)) =
- self.resolve_path_in_type_ns_fully(db, target_trait.path.mod_path())
+ self.resolve_path_in_type_ns_fully(db, &target_trait.path)
{
traits.insert(trait_);
}
@@ -536,15 +585,13 @@ impl Resolver {
scope_id,
}));
if let Some(block) = expr_scopes.block(scope_id) {
- if let Some(def_map) = db.block_def_map(block) {
- let root = def_map.root();
- resolver
- .scopes
- .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: root }));
- // FIXME: This adds as many module scopes as there are blocks, but resolving in each
- // already traverses all parents, so this is O(n²). I think we could only store the
- // innermost module scope instead?
- }
+ let def_map = db.block_def_map(block);
+ resolver
+ .scopes
+ .push(Scope::BlockScope(ModuleItemMap { def_map, module_id: DefMap::ROOT }));
+ // FIXME: This adds as many module scopes as there are blocks, but resolving in each
+ // already traverses all parents, so this is O(n²). I think we could only store the
+ // innermost module scope instead?
}
}
@@ -592,7 +639,8 @@ impl Resolver {
shadow: BuiltinShadowMode,
) -> PerNs {
let (item_map, module) = self.item_scope();
- let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow);
+ // This method resolves `path` just like import paths, so no expected macro subns is given.
+ let (module_res, segment_index) = item_map.resolve_path(db, module, path, shadow, None);
if segment_index.is_some() {
return PerNs::none();
}
@@ -705,13 +753,11 @@ fn resolver_for_scope_(
for scope in scope_chain.into_iter().rev() {
if let Some(block) = scopes.block(scope) {
- if let Some(def_map) = db.block_def_map(block) {
- let root = def_map.root();
- r = r.push_block_scope(def_map, root);
- // FIXME: This adds as many module scopes as there are blocks, but resolving in each
- // already traverses all parents, so this is O(n²). I think we could only store the
- // innermost module scope instead?
- }
+ let def_map = db.block_def_map(block);
+ r = r.push_block_scope(def_map, DefMap::ROOT);
+ // FIXME: This adds as many module scopes as there are blocks, but resolving in each
+ // already traverses all parents, so this is O(n²). I think we could only store the
+ // innermost module scope instead?
}
r = r.push_expr_scope(owner, Arc::clone(&scopes), scope);
@@ -900,6 +946,15 @@ impl HasResolver for ModuleId {
}
}
+impl HasResolver for CrateRootModuleId {
+ fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+ Resolver {
+ scopes: vec![],
+ module_scope: ModuleItemMap { def_map: self.def_map(db), module_id: DefMap::ROOT },
+ }
+ }
+}
+
impl HasResolver for TraitId {
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
self.lookup(db).container.resolver(db).push_generic_params_scope(db, self.into())
@@ -963,6 +1018,24 @@ impl HasResolver for ExternBlockId {
}
}
+impl HasResolver for TypeOwnerId {
+ fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+ match self {
+ TypeOwnerId::FunctionId(x) => x.resolver(db),
+ TypeOwnerId::StaticId(x) => x.resolver(db),
+ TypeOwnerId::ConstId(x) => x.resolver(db),
+ TypeOwnerId::InTypeConstId(x) => x.lookup(db).owner.resolver(db),
+ TypeOwnerId::AdtId(x) => x.resolver(db),
+ TypeOwnerId::TraitId(x) => x.resolver(db),
+ TypeOwnerId::TraitAliasId(x) => x.resolver(db),
+ TypeOwnerId::TypeAliasId(x) => x.resolver(db),
+ TypeOwnerId::ImplId(x) => x.resolver(db),
+ TypeOwnerId::EnumVariantId(x) => x.resolver(db),
+ TypeOwnerId::ModuleId(x) => x.resolver(db),
+ }
+ }
+}
+
impl HasResolver for DefWithBodyId {
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
match self {
@@ -970,6 +1043,7 @@ impl HasResolver for DefWithBodyId {
DefWithBodyId::FunctionId(f) => f.resolver(db),
DefWithBodyId::StaticId(s) => s.resolver(db),
DefWithBodyId::VariantId(v) => v.parent.resolver(db),
+ DefWithBodyId::InTypeConstId(c) => c.lookup(db).owner.resolver(db),
}
}
}
@@ -1000,6 +1074,12 @@ impl HasResolver for GenericDefId {
}
}
+impl HasResolver for EnumVariantId {
+ fn resolver(self, db: &dyn DefDatabase) -> Resolver {
+ self.parent.resolver(db)
+ }
+}
+
impl HasResolver for VariantId {
fn resolver(self, db: &dyn DefDatabase) -> Resolver {
match self {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
index f69356cac..6047f770d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
@@ -20,7 +20,7 @@ impl<N: ItemTreeNode> HasSource for AssocItemLoc<N> {
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -33,7 +33,7 @@ impl<N: ItemTreeNode> HasSource for ItemLoc<N> {
fn source(&self, db: &dyn DefDatabase) -> InFile<N::Source> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -46,7 +46,7 @@ impl HasSource for Macro2Loc {
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -59,7 +59,7 @@ impl HasSource for MacroRulesLoc {
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
@@ -72,7 +72,7 @@ impl HasSource for ProcMacroLoc {
fn source(&self, db: &dyn DefDatabase) -> InFile<Self::Value> {
let tree = self.id.item_tree(db);
let ast_id_map = db.ast_id_map(self.id.file_id());
- let root = db.parse_or_expand(self.id.file_id()).unwrap();
+ let root = db.parse_or_expand(self.id.file_id());
let node = &tree[self.id.value];
InFile::new(self.id.file_id(), ast_id_map.get(node.ast_id()).to_node(&root))
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
index ee143b19a..a6befc8a8 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/test_db.rs
@@ -1,17 +1,16 @@
//! Database used for testing `hir_def`.
-use std::{
- fmt, panic,
- sync::{Arc, Mutex},
-};
+use std::{fmt, panic, sync::Mutex};
use base_db::{
- salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition,
- SourceDatabase, Upcast,
+ salsa::{self, Durability},
+ AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, FilePosition, SourceDatabase,
+ Upcast,
};
use hir_expand::{db::ExpandDatabase, InFile};
-use stdx::hash::NoHashHashSet;
+use rustc_hash::FxHashSet;
use syntax::{algo, ast, AstNode};
+use triomphe::Arc;
use crate::{
db::DefDatabase,
@@ -35,7 +34,7 @@ pub(crate) struct TestDB {
impl Default for TestDB {
fn default() -> Self {
let mut this = Self { storage: Default::default(), events: Default::default() };
- this.set_enable_proc_attr_macros(true);
+ this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
this
}
}
@@ -70,13 +69,13 @@ impl fmt::Debug for TestDB {
impl panic::RefUnwindSafe for TestDB {}
impl FileLoader for TestDB {
- fn file_text(&self, file_id: FileId) -> Arc<String> {
+ fn file_text(&self, file_id: FileId) -> Arc<str> {
FileLoaderDelegate(self).file_text(file_id)
}
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
FileLoaderDelegate(self).relevant_crates(file_id)
}
}
@@ -111,7 +110,7 @@ impl TestDB {
}
_ => {
// FIXME: handle `mod` inside block expression
- return def_map.module_id(def_map.root());
+ return def_map.module_id(DefMap::ROOT);
}
}
}
@@ -120,7 +119,7 @@ impl TestDB {
/// Finds the smallest/innermost module in `def_map` containing `position`.
fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId {
let mut size = None;
- let mut res = def_map.root();
+ let mut res = DefMap::ROOT;
for (module, data) in def_map.modules() {
let src = data.definition_source(self);
if src.file_id != position.file_id.into() {
@@ -209,13 +208,11 @@ impl TestDB {
});
for scope in scope_iter {
- let containing_blocks =
+ let mut containing_blocks =
scopes.scope_chain(Some(scope)).filter_map(|scope| scopes.block(scope));
- for block in containing_blocks {
- if let Some(def_map) = self.block_def_map(block) {
- return Some(def_map);
- }
+ if let Some(block) = containing_blocks.next().map(|block| self.block_def_map(block)) {
+ return Some(block);
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
index ab76ed43d..30f48de61 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
@@ -1,10 +1,11 @@
//! Defines hir-level representation of visibility (e.g. `pub` and `pub(crate)`).
-use std::{iter, sync::Arc};
+use std::iter;
use hir_expand::{hygiene::Hygiene, InFile};
use la_arena::ArenaMap;
use syntax::ast;
+use triomphe::Arc;
use crate::{
db::DefDatabase,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
index 5c684be03..40d8659f2 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
@@ -22,6 +22,7 @@ hashbrown = { version = "0.12.1", features = [
"inline-more",
], default-features = false }
smallvec.workspace = true
+triomphe.workspace = true
# local deps
stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
index 2b27db0e9..c2b0d5985 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
@@ -20,7 +20,7 @@ use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
/// `AstId` points to an AST node in a specific file.
pub struct FileAstId<N: AstNode> {
raw: ErasedFileAstId,
- _ty: PhantomData<fn() -> N>,
+ covariant: PhantomData<fn() -> N>,
}
impl<N: AstNode> Clone for FileAstId<N> {
@@ -54,7 +54,7 @@ impl<N: AstNode> FileAstId<N> {
where
N: Into<M>,
{
- FileAstId { raw: self.raw, _ty: PhantomData }
+ FileAstId { raw: self.raw, covariant: PhantomData }
}
}
@@ -98,6 +98,7 @@ impl AstIdMap {
|| ast::Variant::can_cast(kind)
|| ast::RecordField::can_cast(kind)
|| ast::TupleField::can_cast(kind)
+ || ast::ConstArg::can_cast(kind)
{
res.alloc(&it);
true
@@ -115,12 +116,17 @@ impl AstIdMap {
}
}
}
+ res.arena.shrink_to_fit();
res
}
pub fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
let raw = self.erased_ast_id(item.syntax());
- FileAstId { raw, _ty: PhantomData }
+ FileAstId { raw, covariant: PhantomData }
+ }
+
+ pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
+ AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap()
}
fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
@@ -136,10 +142,6 @@ impl AstIdMap {
}
}
- pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
- AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap()
- }
-
fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
self.arena.alloc(SyntaxNodePtr::new(item))
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
index 8d1e88725..4c918e55b 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
@@ -1,5 +1,5 @@
//! A higher level attributes based on TokenTree, with also some shortcuts.
-use std::{fmt, ops, sync::Arc};
+use std::{fmt, ops};
use base_db::CrateId;
use cfg::CfgExpr;
@@ -8,12 +8,12 @@ use intern::Interned;
use mbe::{syntax_node_to_token_tree, DelimiterKind, Punct};
use smallvec::{smallvec, SmallVec};
use syntax::{ast, match_ast, AstNode, SmolStr, SyntaxNode};
+use triomphe::Arc;
use crate::{
db::ExpandDatabase,
hygiene::Hygiene,
- mod_path::{ModPath, PathKind},
- name::AsName,
+ mod_path::ModPath,
tt::{self, Subtree},
InFile,
};
@@ -21,6 +21,7 @@ use crate::{
/// Syntactical attributes, without filtering of `cfg_attr`s.
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct RawAttrs {
+ // FIXME: Make this a ThinArc
entries: Option<Arc<[Attr]>>,
}
@@ -50,7 +51,9 @@ impl RawAttrs {
path: Interned::new(ModPath::from(crate::name!(doc))),
}),
})
- .collect::<Arc<_>>();
+ .collect::<Vec<_>>();
+ // FIXME: use `Arc::from_iter` when it becomes available
+ let entries: Arc<[Attr]> = Arc::from(entries);
Self { entries: if entries.is_empty() { None } else { Some(entries) } }
}
@@ -68,7 +71,7 @@ impl RawAttrs {
(Some(a), Some(b)) => {
let last_ast_index = a.last().map_or(0, |it| it.id.ast_index() + 1) as u32;
Self {
- entries: Some(
+ entries: Some(Arc::from(
a.iter()
.cloned()
.chain(b.iter().map(|it| {
@@ -78,8 +81,9 @@ impl RawAttrs {
<< AttrId::AST_INDEX_BITS;
it
}))
- .collect(),
- ),
+ // FIXME: use `Arc::from_iter` when it becomes available
+ .collect::<Vec<_>>(),
+ )),
}
}
}
@@ -96,48 +100,51 @@ impl RawAttrs {
}
let crate_graph = db.crate_graph();
- let new_attrs = self
- .iter()
- .flat_map(|attr| -> SmallVec<[_; 1]> {
- let is_cfg_attr =
- attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
- if !is_cfg_attr {
- return smallvec![attr.clone()];
- }
-
- let subtree = match attr.token_tree_value() {
- Some(it) => it,
- _ => return smallvec![attr.clone()],
- };
-
- let (cfg, parts) = match parse_cfg_attr_input(subtree) {
- Some(it) => it,
- None => return smallvec![attr.clone()],
- };
- let index = attr.id;
- let attrs =
- parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(|(idx, attr)| {
- let tree = Subtree {
- delimiter: tt::Delimiter::unspecified(),
- token_trees: attr.to_vec(),
- };
- // FIXME hygiene
- let hygiene = Hygiene::new_unhygienic();
- Attr::from_tt(db, &tree, &hygiene, index.with_cfg_attr(idx))
- });
-
- let cfg_options = &crate_graph[krate].cfg_options;
- let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
- let cfg = CfgExpr::parse(&cfg);
- if cfg_options.check(&cfg) == Some(false) {
- smallvec![]
- } else {
- cov_mark::hit!(cfg_attr_active);
-
- attrs.collect()
- }
- })
- .collect();
+ let new_attrs = Arc::from(
+ self.iter()
+ .flat_map(|attr| -> SmallVec<[_; 1]> {
+ let is_cfg_attr =
+ attr.path.as_ident().map_or(false, |name| *name == crate::name![cfg_attr]);
+ if !is_cfg_attr {
+ return smallvec![attr.clone()];
+ }
+
+ let subtree = match attr.token_tree_value() {
+ Some(it) => it,
+ _ => return smallvec![attr.clone()],
+ };
+
+ let (cfg, parts) = match parse_cfg_attr_input(subtree) {
+ Some(it) => it,
+ None => return smallvec![attr.clone()],
+ };
+ let index = attr.id;
+ let attrs = parts.enumerate().take(1 << AttrId::CFG_ATTR_BITS).filter_map(
+ |(idx, attr)| {
+ let tree = Subtree {
+ delimiter: tt::Delimiter::unspecified(),
+ token_trees: attr.to_vec(),
+ };
+ // FIXME hygiene
+ let hygiene = Hygiene::new_unhygienic();
+ Attr::from_tt(db, &tree, &hygiene, index.with_cfg_attr(idx))
+ },
+ );
+
+ let cfg_options = &crate_graph[krate].cfg_options;
+ let cfg = Subtree { delimiter: subtree.delimiter, token_trees: cfg.to_vec() };
+ let cfg = CfgExpr::parse(&cfg);
+ if cfg_options.check(&cfg) == Some(false) {
+ smallvec![]
+ } else {
+ cov_mark::hit!(cfg_attr_active);
+
+ attrs.collect()
+ }
+ })
+ // FIXME: use `Arc::from_iter` when it becomes available
+ .collect::<Vec<_>>(),
+ );
RawAttrs { entries: Some(new_attrs) }
}
@@ -185,14 +192,14 @@ pub enum AttrInput {
/// `#[attr = "string"]`
Literal(SmolStr),
/// `#[attr(subtree)]`
- TokenTree(tt::Subtree, mbe::TokenMap),
+ TokenTree(Box<(tt::Subtree, mbe::TokenMap)>),
}
impl fmt::Display for AttrInput {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AttrInput::Literal(lit) => write!(f, " = \"{}\"", lit.escape_debug()),
- AttrInput::TokenTree(subtree, _) => subtree.fmt(f),
+ AttrInput::TokenTree(tt) => tt.0.fmt(f),
}
}
}
@@ -213,7 +220,7 @@ impl Attr {
Some(Interned::new(AttrInput::Literal(value)))
} else if let Some(tt) = ast.token_tree() {
let (tree, map) = syntax_node_to_token_tree(tt.syntax());
- Some(Interned::new(AttrInput::TokenTree(tree, map)))
+ Some(Interned::new(AttrInput::TokenTree(Box::new((tree, map)))))
} else {
None
};
@@ -249,7 +256,7 @@ impl Attr {
/// #[path(ident)]
pub fn single_ident_value(&self) -> Option<&tt::Ident> {
match self.input.as_deref()? {
- AttrInput::TokenTree(subtree, _) => match &*subtree.token_trees {
+ AttrInput::TokenTree(tt) => match &*tt.0.token_trees {
[tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] => Some(ident),
_ => None,
},
@@ -260,13 +267,17 @@ impl Attr {
/// #[path TokenTree]
pub fn token_tree_value(&self) -> Option<&Subtree> {
match self.input.as_deref()? {
- AttrInput::TokenTree(subtree, _) => Some(subtree),
+ AttrInput::TokenTree(tt) => Some(&tt.0),
_ => None,
}
}
/// Parses this attribute as a token tree consisting of comma separated paths.
- pub fn parse_path_comma_token_tree(&self) -> Option<impl Iterator<Item = ModPath> + '_> {
+ pub fn parse_path_comma_token_tree<'a>(
+ &'a self,
+ db: &'a dyn ExpandDatabase,
+ hygiene: &'a Hygiene,
+ ) -> Option<impl Iterator<Item = ModPath> + 'a> {
let args = self.token_tree_value()?;
if args.delimiter.kind != DelimiterKind::Parenthesis {
@@ -275,19 +286,37 @@ impl Attr {
let paths = args
.token_trees
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(Punct { char: ',', .. }))))
- .filter_map(|tts| {
+ .filter_map(move |tts| {
if tts.is_empty() {
return None;
}
- let segments = tts.iter().filter_map(|tt| match tt {
- tt::TokenTree::Leaf(tt::Leaf::Ident(id)) => Some(id.as_name()),
- _ => None,
- });
- Some(ModPath::from_segments(PathKind::Plain, segments))
+ // FIXME: This is necessarily a hack. It'd be nice if we could avoid allocation here.
+ let subtree = tt::Subtree {
+ delimiter: tt::Delimiter::unspecified(),
+ token_trees: tts.into_iter().cloned().collect(),
+ };
+ let (parse, _) =
+ mbe::token_tree_to_syntax_node(&subtree, mbe::TopEntryPoint::MetaItem);
+ let meta = ast::Meta::cast(parse.syntax_node())?;
+ // Only simple paths are allowed.
+ if meta.eq_token().is_some() || meta.expr().is_some() || meta.token_tree().is_some()
+ {
+ return None;
+ }
+ let path = meta.path()?;
+ ModPath::from_src(db, path, hygiene)
});
Some(paths)
}
+
+ pub fn cfg(&self) -> Option<CfgExpr> {
+ if *self.path.as_ident()? == crate::name![cfg] {
+ self.token_tree_value().map(CfgExpr::parse)
+ } else {
+ None
+ }
+ }
}
pub fn collect_attrs(
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs
index 277ecd939..80695bc06 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_attr_macro.rs
@@ -96,7 +96,7 @@ fn derive_attr_expand(
) -> ExpandResult<tt::Subtree> {
let loc = db.lookup_intern_macro_call(id);
let derives = match &loc.kind {
- MacroCallKind::Attr { attr_args, is_derive: true, .. } => &attr_args.0,
+ MacroCallKind::Attr { attr_args, .. } if loc.def.is_attribute_derive() => &attr_args.0,
_ => return ExpandResult::ok(tt::Subtree::empty()),
};
pseudo_derive_attr_expansion(tt, derives)
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
index 5c1a75132..3d1e272b9 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
@@ -1,12 +1,19 @@
//! Builtin derives.
+use ::tt::Ident;
use base_db::{CrateOrigin, LangCrateOrigin};
+use itertools::izip;
+use mbe::TokenMap;
+use rustc_hash::FxHashSet;
+use stdx::never;
use tracing::debug;
-use crate::tt::{self, TokenId};
-use syntax::{
- ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName},
- match_ast,
+use crate::{
+ name::{AsName, Name},
+ tt::{self, TokenId},
+};
+use syntax::ast::{
+ self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName, HasTypeBounds,
};
use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult, MacroCallId};
@@ -58,46 +65,201 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
BuiltinDeriveExpander::find_by_name(ident)
}
+enum VariantShape {
+ Struct(Vec<tt::Ident>),
+ Tuple(usize),
+ Unit,
+}
+
+fn tuple_field_iterator(n: usize) -> impl Iterator<Item = tt::Ident> {
+ (0..n).map(|x| Ident::new(format!("f{x}"), tt::TokenId::unspecified()))
+}
+
+impl VariantShape {
+ fn as_pattern(&self, path: tt::Subtree) -> tt::Subtree {
+ self.as_pattern_map(path, |x| quote!(#x))
+ }
+
+ fn field_names(&self) -> Vec<tt::Ident> {
+ match self {
+ VariantShape::Struct(s) => s.clone(),
+ VariantShape::Tuple(n) => tuple_field_iterator(*n).collect(),
+ VariantShape::Unit => vec![],
+ }
+ }
+
+ fn as_pattern_map(
+ &self,
+ path: tt::Subtree,
+ field_map: impl Fn(&tt::Ident) -> tt::Subtree,
+ ) -> tt::Subtree {
+ match self {
+ VariantShape::Struct(fields) => {
+ let fields = fields.iter().map(|x| {
+ let mapped = field_map(x);
+ quote! { #x : #mapped , }
+ });
+ quote! {
+ #path { ##fields }
+ }
+ }
+ &VariantShape::Tuple(n) => {
+ let fields = tuple_field_iterator(n).map(|x| {
+ let mapped = field_map(&x);
+ quote! {
+ #mapped ,
+ }
+ });
+ quote! {
+ #path ( ##fields )
+ }
+ }
+ VariantShape::Unit => path,
+ }
+ }
+
+ fn from(value: Option<FieldList>, token_map: &TokenMap) -> Result<Self, ExpandError> {
+ let r = match value {
+ None => VariantShape::Unit,
+ Some(FieldList::RecordFieldList(x)) => VariantShape::Struct(
+ x.fields()
+ .map(|x| x.name())
+ .map(|x| name_to_token(token_map, x))
+ .collect::<Result<_, _>>()?,
+ ),
+ Some(FieldList::TupleFieldList(x)) => VariantShape::Tuple(x.fields().count()),
+ };
+ Ok(r)
+ }
+}
+
+enum AdtShape {
+ Struct(VariantShape),
+ Enum { variants: Vec<(tt::Ident, VariantShape)>, default_variant: Option<usize> },
+ Union,
+}
+
+impl AdtShape {
+ fn as_pattern(&self, name: &tt::Ident) -> Vec<tt::Subtree> {
+ self.as_pattern_map(name, |x| quote!(#x))
+ }
+
+ fn field_names(&self) -> Vec<Vec<tt::Ident>> {
+ match self {
+ AdtShape::Struct(s) => {
+ vec![s.field_names()]
+ }
+ AdtShape::Enum { variants, .. } => {
+ variants.iter().map(|(_, fields)| fields.field_names()).collect()
+ }
+ AdtShape::Union => {
+ never!("using fields of union in derive is always wrong");
+ vec![]
+ }
+ }
+ }
+
+ fn as_pattern_map(
+ &self,
+ name: &tt::Ident,
+ field_map: impl Fn(&tt::Ident) -> tt::Subtree,
+ ) -> Vec<tt::Subtree> {
+ match self {
+ AdtShape::Struct(s) => {
+ vec![s.as_pattern_map(quote! { #name }, field_map)]
+ }
+ AdtShape::Enum { variants, .. } => variants
+ .iter()
+ .map(|(v, fields)| fields.as_pattern_map(quote! { #name :: #v }, &field_map))
+ .collect(),
+ AdtShape::Union => {
+ never!("pattern matching on union is always wrong");
+ vec![quote! { un }]
+ }
+ }
+ }
+}
+
struct BasicAdtInfo {
name: tt::Ident,
- /// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
- param_types: Vec<Option<tt::Subtree>>,
+ shape: AdtShape,
+ /// first field is the name, and
+ /// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
+ /// third fields is where bounds, if any
+ param_types: Vec<(tt::Subtree, Option<tt::Subtree>, Option<tt::Subtree>)>,
+ associated_types: Vec<tt::Subtree>,
}
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
let (parsed, token_map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MacroItems);
let macro_items = ast::MacroItems::cast(parsed.syntax_node()).ok_or_else(|| {
debug!("derive node didn't parse");
- ExpandError::Other("invalid item definition".into())
+ ExpandError::other("invalid item definition")
})?;
let item = macro_items.items().next().ok_or_else(|| {
debug!("no module item parsed");
- ExpandError::Other("no item found".into())
+ ExpandError::other("no item found")
})?;
- let node = item.syntax();
- let (name, params) = match_ast! {
- match node {
- ast::Struct(it) => (it.name(), it.generic_param_list()),
- ast::Enum(it) => (it.name(), it.generic_param_list()),
- ast::Union(it) => (it.name(), it.generic_param_list()),
- _ => {
- debug!("unexpected node is {:?}", node);
- return Err(ExpandError::Other("expected struct, enum or union".into()))
- },
+ let adt = ast::Adt::cast(item.syntax().clone()).ok_or_else(|| {
+ debug!("expected adt, found: {:?}", item);
+ ExpandError::other("expected struct, enum or union")
+ })?;
+ let (name, generic_param_list, shape) = match &adt {
+ ast::Adt::Struct(it) => (
+ it.name(),
+ it.generic_param_list(),
+ AdtShape::Struct(VariantShape::from(it.field_list(), &token_map)?),
+ ),
+ ast::Adt::Enum(it) => {
+ let default_variant = it
+ .variant_list()
+ .into_iter()
+ .flat_map(|x| x.variants())
+ .position(|x| x.attrs().any(|x| x.simple_name() == Some("default".into())));
+ (
+ it.name(),
+ it.generic_param_list(),
+ AdtShape::Enum {
+ default_variant,
+ variants: it
+ .variant_list()
+ .into_iter()
+ .flat_map(|x| x.variants())
+ .map(|x| {
+ Ok((
+ name_to_token(&token_map, x.name())?,
+ VariantShape::from(x.field_list(), &token_map)?,
+ ))
+ })
+ .collect::<Result<_, ExpandError>>()?,
+ },
+ )
}
+ ast::Adt::Union(it) => (it.name(), it.generic_param_list(), AdtShape::Union),
};
- let name = name.ok_or_else(|| {
- debug!("parsed item has no name");
- ExpandError::Other("missing name".into())
- })?;
- let name_token_id =
- token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
- let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
- let param_types = params
+
+ let mut param_type_set: FxHashSet<Name> = FxHashSet::default();
+ let param_types = generic_param_list
.into_iter()
.flat_map(|param_list| param_list.type_or_const_params())
.map(|param| {
- if let ast::TypeOrConstParam::Const(param) = param {
+ let name = {
+ let this = param.name();
+ match this {
+ Some(x) => {
+ param_type_set.insert(x.as_name());
+ mbe::syntax_node_to_token_tree(x.syntax()).0
+ }
+ None => tt::Subtree::empty(),
+ }
+ };
+ let bounds = match &param {
+ ast::TypeOrConstParam::Type(x) => {
+ x.type_bound_list().map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
+ }
+ ast::TypeOrConstParam::Const(_) => None,
+ };
+ let ty = if let ast::TypeOrConstParam::Const(param) = param {
let ty = param
.ty()
.map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0)
@@ -105,27 +267,103 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
Some(ty)
} else {
None
- }
+ };
+ (name, ty, bounds)
+ })
+ .collect();
+
+ // For a generic parameter `T`, when shorthand associated type `T::Assoc` appears in field
+ // types (of any variant for enums), we generate trait bound for it. It sounds reasonable to
+ // also generate trait bound for qualified associated type `<T as Trait>::Assoc`, but rustc
+ // does not do that for some unknown reason.
+ //
+ // See the analogous function in rustc [find_type_parameters()] and rust-lang/rust#50730.
+ // [find_type_parameters()]: https://github.com/rust-lang/rust/blob/1.70.0/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs#L378
+
+ // It's cumbersome to deal with the distinct structures of ADTs, so let's just get untyped
+ // `SyntaxNode` that contains fields and look for descendant `ast::PathType`s. Of note is that
+ // we should not inspect `ast::PathType`s in parameter bounds and where clauses.
+ let field_list = match adt {
+ ast::Adt::Enum(it) => it.variant_list().map(|list| list.syntax().clone()),
+ ast::Adt::Struct(it) => it.field_list().map(|list| list.syntax().clone()),
+ ast::Adt::Union(it) => it.record_field_list().map(|list| list.syntax().clone()),
+ };
+ let associated_types = field_list
+ .into_iter()
+ .flat_map(|it| it.descendants())
+ .filter_map(ast::PathType::cast)
+ .filter_map(|p| {
+ let name = p.path()?.qualifier()?.as_single_name_ref()?.as_name();
+ param_type_set.contains(&name).then_some(p)
})
+ .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0)
.collect();
- Ok(BasicAdtInfo { name: name_token, param_types })
+ let name_token = name_to_token(&token_map, name)?;
+ Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types })
}
-fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
+fn name_to_token(token_map: &TokenMap, name: Option<ast::Name>) -> Result<tt::Ident, ExpandError> {
+ let name = name.ok_or_else(|| {
+ debug!("parsed item has no name");
+ ExpandError::other("missing name")
+ })?;
+ let name_token_id =
+ token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
+ let name_token = tt::Ident { span: name_token_id, text: name.text().into() };
+ Ok(name_token)
+}
+
+/// Given that we are deriving a trait `DerivedTrait` for a type like:
+///
+/// ```ignore (only-for-syntax-highlight)
+/// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait {
+/// a: A,
+/// b: B::Item,
+/// b1: <B as DeclaredTrait>::Item,
+/// c1: <C as WhereTrait>::Item,
+/// c2: Option<<C as WhereTrait>::Item>,
+/// ...
+/// }
+/// ```
+///
+/// create an impl like:
+///
+/// ```ignore (only-for-syntax-highlight)
+/// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where
+/// C: WhereTrait,
+/// A: DerivedTrait + B1 + ... + BN,
+/// B: DerivedTrait + B1 + ... + BN,
+/// C: DerivedTrait + B1 + ... + BN,
+/// B::Item: DerivedTrait + B1 + ... + BN,
+/// <C as WhereTrait>::Item: DerivedTrait + B1 + ... + BN,
+/// ...
+/// {
+/// ...
+/// }
+/// ```
+///
+/// where B1, ..., BN are the bounds given by `bounds_paths`. Z is a phantom type, and
+/// therefore does not get bound by the derived trait.
+fn expand_simple_derive(
+ tt: &tt::Subtree,
+ trait_path: tt::Subtree,
+ make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
let info = match parse_adt(tt) {
Ok(info) => info,
- Err(e) => return ExpandResult::with_err(tt::Subtree::empty(), e),
+ Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
};
+ let trait_body = make_trait_body(&info);
+ let mut where_block = vec![];
let (params, args): (Vec<_>, Vec<_>) = info
.param_types
.into_iter()
- .enumerate()
- .map(|(idx, param_ty)| {
- let ident = tt::Leaf::Ident(tt::Ident {
- span: tt::TokenId::unspecified(),
- text: format!("T{idx}").into(),
- });
+ .map(|(ident, param_ty, bound)| {
let ident_ = ident.clone();
+ if let Some(b) = bound {
+ let ident = ident.clone();
+ where_block.push(quote! { #ident : #b , });
+ }
if let Some(ty) = param_ty {
(quote! { const #ident : #ty , }, quote! { #ident_ , })
} else {
@@ -134,9 +372,16 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
}
})
.unzip();
+
+ where_block.extend(info.associated_types.iter().map(|x| {
+ let x = x.clone();
+ let bound = trait_path.clone();
+ quote! { #x : #bound , }
+ }));
+
let name = info.name;
let expanded = quote! {
- impl < ##params > #trait_path for #name < ##args > {}
+ impl < ##params > #trait_path for #name < ##args > where ##where_block { #trait_body }
};
ExpandResult::ok(expanded)
}
@@ -163,7 +408,7 @@ fn copy_expand(
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::marker::Copy })
+ expand_simple_derive(tt, quote! { #krate::marker::Copy }, |_| quote! {})
}
fn clone_expand(
@@ -172,7 +417,63 @@ fn clone_expand(
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::clone::Clone })
+ expand_simple_derive(tt, quote! { #krate::clone::Clone }, |adt| {
+ if matches!(adt.shape, AdtShape::Union) {
+ let star = tt::Punct {
+ char: '*',
+ spacing: ::tt::Spacing::Alone,
+ span: tt::TokenId::unspecified(),
+ };
+ return quote! {
+ fn clone(&self) -> Self {
+ #star self
+ }
+ };
+ }
+ if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
+ let star = tt::Punct {
+ char: '*',
+ spacing: ::tt::Spacing::Alone,
+ span: tt::TokenId::unspecified(),
+ };
+ return quote! {
+ fn clone(&self) -> Self {
+ match #star self {}
+ }
+ };
+ }
+ let name = &adt.name;
+ let patterns = adt.shape.as_pattern(name);
+ let exprs = adt.shape.as_pattern_map(name, |x| quote! { #x .clone() });
+ let arms = patterns.into_iter().zip(exprs.into_iter()).map(|(pat, expr)| {
+ let fat_arrow = fat_arrow();
+ quote! {
+ #pat #fat_arrow #expr,
+ }
+ });
+
+ quote! {
+ fn clone(&self) -> Self {
+ match self {
+ ##arms
+ }
+ }
+ }
+ })
+}
+
+/// This function exists since `quote! { => }` doesn't work.
+fn fat_arrow() -> ::tt::Subtree<TokenId> {
+ let eq =
+ tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() };
+ quote! { #eq> }
+}
+
+/// This function exists since `quote! { && }` doesn't work.
+fn and_and() -> ::tt::Subtree<TokenId> {
+ let and =
+ tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() };
+ quote! { #and& }
}
fn default_expand(
@@ -180,8 +481,38 @@ fn default_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
- let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::default::Default })
+ let krate = &find_builtin_crate(db, id);
+ expand_simple_derive(tt, quote! { #krate::default::Default }, |adt| {
+ let body = match &adt.shape {
+ AdtShape::Struct(fields) => {
+ let name = &adt.name;
+ fields
+ .as_pattern_map(quote!(#name), |_| quote!(#krate::default::Default::default()))
+ }
+ AdtShape::Enum { default_variant, variants } => {
+ if let Some(d) = default_variant {
+ let (name, fields) = &variants[*d];
+ let adt_name = &adt.name;
+ fields.as_pattern_map(
+ quote!(#adt_name :: #name),
+ |_| quote!(#krate::default::Default::default()),
+ )
+ } else {
+ // FIXME: Return expand error here
+ quote!()
+ }
+ }
+ AdtShape::Union => {
+ // FIXME: Return expand error here
+ quote!()
+ }
+ };
+ quote! {
+ fn default() -> Self {
+ #body
+ }
+ }
+ })
}
fn debug_expand(
@@ -189,8 +520,79 @@ fn debug_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
- let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::fmt::Debug })
+ let krate = &find_builtin_crate(db, id);
+ expand_simple_derive(tt, quote! { #krate::fmt::Debug }, |adt| {
+ let for_variant = |name: String, v: &VariantShape| match v {
+ VariantShape::Struct(fields) => {
+ let for_fields = fields.iter().map(|x| {
+ let x_string = x.to_string();
+ quote! {
+ .field(#x_string, & #x)
+ }
+ });
+ quote! {
+ f.debug_struct(#name) ##for_fields .finish()
+ }
+ }
+ VariantShape::Tuple(n) => {
+ let for_fields = tuple_field_iterator(*n).map(|x| {
+ quote! {
+ .field( & #x)
+ }
+ });
+ quote! {
+ f.debug_tuple(#name) ##for_fields .finish()
+ }
+ }
+ VariantShape::Unit => quote! {
+ f.write_str(#name)
+ },
+ };
+ if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
+ let star = tt::Punct {
+ char: '*',
+ spacing: ::tt::Spacing::Alone,
+ span: tt::TokenId::unspecified(),
+ };
+ return quote! {
+ fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
+ match #star self {}
+ }
+ };
+ }
+ let arms = match &adt.shape {
+ AdtShape::Struct(fields) => {
+ let fat_arrow = fat_arrow();
+ let name = &adt.name;
+ let pat = fields.as_pattern(quote!(#name));
+ let expr = for_variant(name.to_string(), fields);
+ vec![quote! { #pat #fat_arrow #expr }]
+ }
+ AdtShape::Enum { variants, .. } => variants
+ .iter()
+ .map(|(name, v)| {
+ let fat_arrow = fat_arrow();
+ let adt_name = &adt.name;
+ let pat = v.as_pattern(quote!(#adt_name :: #name));
+ let expr = for_variant(name.to_string(), v);
+ quote! {
+ #pat #fat_arrow #expr ,
+ }
+ })
+ .collect(),
+ AdtShape::Union => {
+ // FIXME: Return expand error here
+ vec![]
+ }
+ };
+ quote! {
+ fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
+ match self {
+ ##arms
+ }
+ }
+ }
+ })
}
fn hash_expand(
@@ -198,8 +600,47 @@ fn hash_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
- let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::hash::Hash })
+ let krate = &find_builtin_crate(db, id);
+ expand_simple_derive(tt, quote! { #krate::hash::Hash }, |adt| {
+ if matches!(adt.shape, AdtShape::Union) {
+ // FIXME: Return expand error here
+ return quote! {};
+ }
+ if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
+ let star = tt::Punct {
+ char: '*',
+ spacing: ::tt::Spacing::Alone,
+ span: tt::TokenId::unspecified(),
+ };
+ return quote! {
+ fn hash<H: #krate::hash::Hasher>(&self, ra_expand_state: &mut H) {
+ match #star self {}
+ }
+ };
+ }
+ let arms = adt.shape.as_pattern(&adt.name).into_iter().zip(adt.shape.field_names()).map(
+ |(pat, names)| {
+ let expr = {
+ let it = names.iter().map(|x| quote! { #x . hash(ra_expand_state); });
+ quote! { {
+ ##it
+ } }
+ };
+ let fat_arrow = fat_arrow();
+ quote! {
+ #pat #fat_arrow #expr ,
+ }
+ },
+ );
+ quote! {
+ fn hash<H: #krate::hash::Hasher>(&self, ra_expand_state: &mut H) {
+ #krate::mem::discriminant(self).hash(ra_expand_state);
+ match self {
+ ##arms
+ }
+ }
+ }
+ })
}
fn eq_expand(
@@ -208,7 +649,7 @@ fn eq_expand(
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::cmp::Eq })
+ expand_simple_derive(tt, quote! { #krate::cmp::Eq }, |_| quote! {})
}
fn partial_eq_expand(
@@ -217,7 +658,65 @@ fn partial_eq_expand(
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::cmp::PartialEq })
+ expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }, |adt| {
+ if matches!(adt.shape, AdtShape::Union) {
+ // FIXME: Return expand error here
+ return quote! {};
+ }
+ let name = &adt.name;
+
+ let (self_patterns, other_patterns) = self_and_other_patterns(adt, name);
+ let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
+ |(pat1, pat2, names)| {
+ let fat_arrow = fat_arrow();
+ let body = match &*names {
+ [] => {
+ quote!(true)
+ }
+ [first, rest @ ..] => {
+ let rest = rest.iter().map(|x| {
+ let t1 = Ident::new(format!("{}_self", x.text), x.span);
+ let t2 = Ident::new(format!("{}_other", x.text), x.span);
+ let and_and = and_and();
+ quote!(#and_and #t1 .eq( #t2 ))
+ });
+ let first = {
+ let t1 = Ident::new(format!("{}_self", first.text), first.span);
+ let t2 = Ident::new(format!("{}_other", first.text), first.span);
+ quote!(#t1 .eq( #t2 ))
+ };
+ quote!(#first ##rest)
+ }
+ };
+ quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
+ },
+ );
+
+ let fat_arrow = fat_arrow();
+ quote! {
+ fn eq(&self, other: &Self) -> bool {
+ match (self, other) {
+ ##arms
+ _unused #fat_arrow false
+ }
+ }
+ }
+ })
+}
+
+fn self_and_other_patterns(
+ adt: &BasicAdtInfo,
+ name: &tt::Ident,
+) -> (Vec<tt::Subtree>, Vec<tt::Subtree>) {
+ let self_patterns = adt.shape.as_pattern_map(name, |x| {
+ let t = Ident::new(format!("{}_self", x.text), x.span);
+ quote!(#t)
+ });
+ let other_patterns = adt.shape.as_pattern_map(name, |x| {
+ let t = Ident::new(format!("{}_other", x.text), x.span);
+ quote!(#t)
+ });
+ (self_patterns, other_patterns)
}
fn ord_expand(
@@ -225,8 +724,63 @@ fn ord_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
- let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::cmp::Ord })
+ let krate = &find_builtin_crate(db, id);
+ expand_simple_derive(tt, quote! { #krate::cmp::Ord }, |adt| {
+ fn compare(
+ krate: &tt::TokenTree,
+ left: tt::Subtree,
+ right: tt::Subtree,
+ rest: tt::Subtree,
+ ) -> tt::Subtree {
+ let fat_arrow1 = fat_arrow();
+ let fat_arrow2 = fat_arrow();
+ quote! {
+ match #left.cmp(&#right) {
+ #krate::cmp::Ordering::Equal #fat_arrow1 {
+ #rest
+ }
+ c #fat_arrow2 return c,
+ }
+ }
+ }
+ if matches!(adt.shape, AdtShape::Union) {
+ // FIXME: Return expand error here
+ return quote!();
+ }
+ let left = quote!(#krate::intrinsics::discriminant_value(self));
+ let right = quote!(#krate::intrinsics::discriminant_value(other));
+
+ let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name);
+ let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
+ |(pat1, pat2, fields)| {
+ let mut body = quote!(#krate::cmp::Ordering::Equal);
+ for f in fields.into_iter().rev() {
+ let t1 = Ident::new(format!("{}_self", f.text), f.span);
+ let t2 = Ident::new(format!("{}_other", f.text), f.span);
+ body = compare(krate, quote!(#t1), quote!(#t2), body);
+ }
+ let fat_arrow = fat_arrow();
+ quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
+ },
+ );
+ let fat_arrow = fat_arrow();
+ let body = compare(
+ krate,
+ left,
+ right,
+ quote! {
+ match (self, other) {
+ ##arms
+ _unused #fat_arrow #krate::cmp::Ordering::Equal
+ }
+ },
+ );
+ quote! {
+ fn cmp(&self, other: &Self) -> #krate::cmp::Ordering {
+ #body
+ }
+ }
+ })
}
fn partial_ord_expand(
@@ -234,6 +788,61 @@ fn partial_ord_expand(
id: MacroCallId,
tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
- let krate = find_builtin_crate(db, id);
- expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd })
+ let krate = &find_builtin_crate(db, id);
+ expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }, |adt| {
+ fn compare(
+ krate: &tt::TokenTree,
+ left: tt::Subtree,
+ right: tt::Subtree,
+ rest: tt::Subtree,
+ ) -> tt::Subtree {
+ let fat_arrow1 = fat_arrow();
+ let fat_arrow2 = fat_arrow();
+ quote! {
+ match #left.partial_cmp(&#right) {
+ #krate::option::Option::Some(#krate::cmp::Ordering::Equal) #fat_arrow1 {
+ #rest
+ }
+ c #fat_arrow2 return c,
+ }
+ }
+ }
+ if matches!(adt.shape, AdtShape::Union) {
+ // FIXME: Return expand error here
+ return quote!();
+ }
+ let left = quote!(#krate::intrinsics::discriminant_value(self));
+ let right = quote!(#krate::intrinsics::discriminant_value(other));
+
+ let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name);
+ let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map(
+ |(pat1, pat2, fields)| {
+ let mut body = quote!(#krate::option::Option::Some(#krate::cmp::Ordering::Equal));
+ for f in fields.into_iter().rev() {
+ let t1 = Ident::new(format!("{}_self", f.text), f.span);
+ let t2 = Ident::new(format!("{}_other", f.text), f.span);
+ body = compare(krate, quote!(#t1), quote!(#t2), body);
+ }
+ let fat_arrow = fat_arrow();
+ quote! { ( #pat1 , #pat2 ) #fat_arrow #body , }
+ },
+ );
+ let fat_arrow = fat_arrow();
+ let body = compare(
+ krate,
+ left,
+ right,
+ quote! {
+ match (self, other) {
+ ##arms
+ _unused #fat_arrow #krate::option::Option::Some(#krate::cmp::Ordering::Equal)
+ }
+ },
+ );
+ quote! {
+ fn partial_cmp(&self, other: &Self) -> #krate::option::Option::Option<#krate::cmp::Ordering> {
+ #body
+ }
+ }
+ })
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
index a9c5e1488..a9f0c154b 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
@@ -1,16 +1,21 @@
//! Builtin macro
+use std::mem;
+
+use ::tt::Ident;
use base_db::{AnchoredPath, Edition, FileId};
use cfg::CfgExpr;
use either::Either;
-use mbe::{parse_exprs_with_sep, parse_to_token_tree};
+use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap};
+use rustc_hash::FxHashMap;
use syntax::{
ast::{self, AstToken},
SmolStr,
};
use crate::{
- db::ExpandDatabase, name, quote, tt, ExpandError, ExpandResult, MacroCallId, MacroCallLoc,
+ db::ExpandDatabase, name, quote, tt, EagerCallInfo, ExpandError, ExpandResult, MacroCallId,
+ MacroCallLoc,
};
macro_rules! register_builtin {
@@ -45,7 +50,7 @@ macro_rules! register_builtin {
db: &dyn ExpandDatabase,
arg_id: MacroCallId,
tt: &tt::Subtree,
- ) -> ExpandResult<ExpandedEager> {
+ ) -> ExpandResult<tt::Subtree> {
let expander = match *self {
$( EagerExpander::$e_kind => $e_expand, )*
};
@@ -63,16 +68,9 @@ macro_rules! register_builtin {
};
}
-#[derive(Debug)]
-pub struct ExpandedEager {
- pub(crate) subtree: tt::Subtree,
- /// The included file ID of the include macro.
- pub(crate) included_file: Option<FileId>,
-}
-
-impl ExpandedEager {
- fn new(subtree: tt::Subtree) -> Self {
- ExpandedEager { subtree, included_file: None }
+impl EagerExpander {
+ pub fn is_include(&self) -> bool {
+ matches!(self, EagerExpander::Include)
}
}
@@ -90,11 +88,6 @@ register_builtin! {
(module_path, ModulePath) => module_path_expand,
(assert, Assert) => assert_expand,
(stringify, Stringify) => stringify_expand,
- (format_args, FormatArgs) => format_args_expand,
- (const_format_args, ConstFormatArgs) => format_args_expand,
- // format_args_nl only differs in that it adds a newline in the end,
- // so we use the same stub expansion for now
- (format_args_nl, FormatArgsNl) => format_args_expand,
(llvm_asm, LlvmAsm) => asm_expand,
(asm, Asm) => asm_expand,
(global_asm, GlobalAsm) => global_asm_expand,
@@ -106,6 +99,9 @@ register_builtin! {
(trace_macros, TraceMacros) => trace_macros_expand,
EAGER:
+ (format_args, FormatArgs) => format_args_expand,
+ (const_format_args, ConstFormatArgs) => format_args_expand,
+ (format_args_nl, FormatArgsNl) => format_args_nl_expand,
(compile_error, CompileError) => compile_error_expand,
(concat, Concat) => concat_expand,
(concat_idents, ConcatIdents) => concat_idents_expand,
@@ -135,9 +131,8 @@ fn line_expand(
_tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
// dummy implementation for type-checking purposes
- let line_num = 0;
let expanded = quote! {
- #line_num
+ 0 as u32
};
ExpandResult::ok(expanded)
@@ -179,9 +174,8 @@ fn column_expand(
_tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> {
// dummy implementation for type-checking purposes
- let col_num = 0;
let expanded = quote! {
- #col_num
+ 0 as u32
};
ExpandResult::ok(expanded)
@@ -234,45 +228,170 @@ fn file_expand(
}
fn format_args_expand(
+ db: &dyn ExpandDatabase,
+ id: MacroCallId,
+ tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+ format_args_expand_general(db, id, tt, "")
+}
+
+fn format_args_nl_expand(
+ db: &dyn ExpandDatabase,
+ id: MacroCallId,
+ tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+ format_args_expand_general(db, id, tt, "\\n")
+}
+
+fn format_args_expand_general(
_db: &dyn ExpandDatabase,
_id: MacroCallId,
tt: &tt::Subtree,
+ end_string: &str,
) -> ExpandResult<tt::Subtree> {
- // We expand `format_args!("", a1, a2)` to
- // ```
- // $crate::fmt::Arguments::new_v1(&[], &[
- // $crate::fmt::Argument::new(&arg1,$crate::fmt::Display::fmt),
- // $crate::fmt::Argument::new(&arg2,$crate::fmt::Display::fmt),
- // ])
- // ```,
- // which is still not really correct, but close enough for now
- let mut args = parse_exprs_with_sep(tt, ',');
-
- if args.is_empty() {
- return ExpandResult::with_err(
- tt::Subtree::empty(),
- mbe::ExpandError::NoMatchingRule.into(),
- );
- }
- for arg in &mut args {
+ let args = parse_exprs_with_sep(tt, ',');
+
+ let expand_error =
+ ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into());
+
+ let mut key_args = FxHashMap::default();
+ let mut args = args.into_iter().filter_map(|mut arg| {
// Remove `key =`.
if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
{
// but not with `==`
- if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' )
+ if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
{
- arg.token_trees.drain(..2);
+ let key = arg.token_trees.drain(..2).next().unwrap();
+ key_args.insert(key.to_string(), arg);
+ return None;
+ }
+ }
+ Some(arg)
+ }).collect::<Vec<_>>().into_iter();
+ // ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`)
+ let Some(format_subtree) = args.next() else {
+ return expand_error;
+ };
+ let format_string = (|| {
+ let token_tree = format_subtree.token_trees.get(0)?;
+ match token_tree {
+ tt::TokenTree::Leaf(l) => match l {
+ tt::Leaf::Literal(l) => {
+ if let Some(mut text) = l.text.strip_prefix('r') {
+ let mut raw_sharps = String::new();
+ while let Some(t) = text.strip_prefix('#') {
+ text = t;
+ raw_sharps.push('#');
+ }
+ text =
+ text.strip_suffix(&raw_sharps)?.strip_prefix('"')?.strip_suffix('"')?;
+ Some((text, l.span, Some(raw_sharps)))
+ } else {
+ let text = l.text.strip_prefix('"')?.strip_suffix('"')?;
+ let span = l.span;
+ Some((text, span, None))
+ }
+ }
+ _ => None,
+ },
+ tt::TokenTree::Subtree(_) => None,
+ }
+ })();
+ let Some((format_string, _format_string_span, raw_sharps)) = format_string else {
+ return expand_error;
+ };
+ let mut format_iter = format_string.chars().peekable();
+ let mut parts = vec![];
+ let mut last_part = String::new();
+ let mut arg_tts = vec![];
+ let mut err = None;
+ while let Some(c) = format_iter.next() {
+ // Parsing the format string. See https://doc.rust-lang.org/std/fmt/index.html#syntax for the grammar and more info
+ match c {
+ '{' => {
+ if format_iter.peek() == Some(&'{') {
+ format_iter.next();
+ last_part.push('{');
+ continue;
+ }
+ let mut argument = String::new();
+ while ![Some(&'}'), Some(&':')].contains(&format_iter.peek()) {
+ argument.push(match format_iter.next() {
+ Some(c) => c,
+ None => return expand_error,
+ });
+ }
+ let format_spec = match format_iter.next().unwrap() {
+ '}' => "".to_owned(),
+ ':' => {
+ let mut s = String::new();
+ while let Some(c) = format_iter.next() {
+ if c == '}' {
+ break;
+ }
+ s.push(c);
+ }
+ s
+ }
+ _ => unreachable!(),
+ };
+ parts.push(mem::take(&mut last_part));
+ let arg_tree = if argument.is_empty() {
+ match args.next() {
+ Some(x) => x,
+ None => {
+ err = Some(mbe::ExpandError::NoMatchingRule.into());
+ tt::Subtree::empty()
+ }
+ }
+ } else if let Some(tree) = key_args.get(&argument) {
+ tree.clone()
+ } else {
+ // FIXME: we should pick the related substring of the `_format_string_span` as the span. You
+ // can use `.char_indices()` instead of `.char()` for `format_iter` to find the substring interval.
+ let ident = Ident::new(argument, tt::TokenId::unspecified());
+ quote!(#ident)
+ };
+ let formatter = match &*format_spec {
+ "?" => quote!(::core::fmt::Debug::fmt),
+ "" => quote!(::core::fmt::Display::fmt),
+ _ => {
+ // FIXME: implement the rest and return expand error here
+ quote!(::core::fmt::Display::fmt)
+ }
+ };
+ arg_tts.push(quote! { ::core::fmt::Argument::new(&(#arg_tree), #formatter), });
}
+ '}' => {
+ if format_iter.peek() == Some(&'}') {
+ format_iter.next();
+ last_part.push('}');
+ } else {
+ return expand_error;
+ }
+ }
+ _ => last_part.push(c),
}
}
- let _format_string = args.remove(0);
- let arg_tts = args.into_iter().flat_map(|arg| {
- quote! { #DOLLAR_CRATE::fmt::Argument::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), }
- }.token_trees);
+ last_part += end_string;
+ if !last_part.is_empty() {
+ parts.push(last_part);
+ }
+ let part_tts = parts.into_iter().map(|x| {
+ let text = if let Some(raw) = &raw_sharps {
+ format!("r{raw}\"{}\"{raw}", x).into()
+ } else {
+ format!("\"{}\"", x).into()
+ };
+ let l = tt::Literal { span: tt::TokenId::unspecified(), text };
+ quote!(#l ,)
+ });
+ let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees);
let expanded = quote! {
- #DOLLAR_CRATE::fmt::Arguments::new_v1(&[], &[##arg_tts])
+ ::core::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts])
};
- ExpandResult::ok(expanded)
+ ExpandResult { value: expanded, err }
}
fn asm_expand(
@@ -382,23 +501,23 @@ fn compile_error_expand(
_db: &dyn ExpandDatabase,
_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let err = match &*tt.token_trees {
[tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) {
- Some(unquoted) => ExpandError::Other(unquoted.into()),
- None => ExpandError::Other("`compile_error!` argument must be a string".into()),
+ Some(unquoted) => ExpandError::other(unquoted),
+ None => ExpandError::other("`compile_error!` argument must be a string"),
},
- _ => ExpandError::Other("`compile_error!` argument must be a string".into()),
+ _ => ExpandError::other("`compile_error!` argument must be a string"),
};
- ExpandResult { value: ExpandedEager::new(quote! {}), err: Some(err) }
+ ExpandResult { value: quote! {}, err: Some(err) }
}
fn concat_expand(
_db: &dyn ExpandDatabase,
_arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let mut err = None;
let mut text = String::new();
for (i, mut t) in tt.token_trees.iter().enumerate() {
@@ -437,14 +556,14 @@ fn concat_expand(
}
}
}
- ExpandResult { value: ExpandedEager::new(quote!(#text)), err }
+ ExpandResult { value: quote!(#text), err }
}
fn concat_bytes_expand(
_db: &dyn ExpandDatabase,
_arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let mut bytes = Vec::new();
let mut err = None;
for (i, t) in tt.token_trees.iter().enumerate() {
@@ -477,7 +596,7 @@ fn concat_bytes_expand(
}
}
let ident = tt::Ident { text: bytes.join(", ").into(), span: tt::TokenId::unspecified() };
- ExpandResult { value: ExpandedEager::new(quote!([#ident])), err }
+ ExpandResult { value: quote!([#ident]), err }
}
fn concat_bytes_expand_subtree(
@@ -510,7 +629,7 @@ fn concat_idents_expand(
_db: &dyn ExpandDatabase,
_arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let mut err = None;
let mut ident = String::new();
for (i, t) in tt.token_trees.iter().enumerate() {
@@ -525,7 +644,7 @@ fn concat_idents_expand(
}
}
let ident = tt::Ident { text: ident.into(), span: tt::TokenId::unspecified() };
- ExpandResult { value: ExpandedEager::new(quote!(#ident)), err }
+ ExpandResult { value: quote!(#ident), err }
}
fn relative_file(
@@ -538,10 +657,10 @@ fn relative_file(
let path = AnchoredPath { anchor: call_site, path: path_str };
let res = db
.resolve_path(path)
- .ok_or_else(|| ExpandError::Other(format!("failed to load file `{path_str}`").into()))?;
+ .ok_or_else(|| ExpandError::other(format!("failed to load file `{path_str}`")))?;
// Prevent include itself
if res == call_site && !allow_recursion {
- Err(ExpandError::Other(format!("recursive inclusion of `{path_str}`").into()))
+ Err(ExpandError::other(format!("recursive inclusion of `{path_str}`")))
} else {
Ok(res)
}
@@ -560,38 +679,37 @@ fn parse_string(tt: &tt::Subtree) -> Result<String, ExpandError> {
fn include_expand(
db: &dyn ExpandDatabase,
arg_id: MacroCallId,
- tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
- let res = (|| {
- let path = parse_string(tt)?;
- let file_id = relative_file(db, arg_id, &path, false)?;
-
- let subtree =
- parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?.0;
- Ok((subtree, file_id))
- })();
-
- match res {
- Ok((subtree, file_id)) => {
- ExpandResult::ok(ExpandedEager { subtree, included_file: Some(file_id) })
- }
- Err(e) => ExpandResult::with_err(
- ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
- e,
- ),
+ _tt: &tt::Subtree,
+) -> ExpandResult<tt::Subtree> {
+ match db.include_expand(arg_id) {
+ Ok((res, _)) => ExpandResult::ok(res.0.clone()),
+ Err(e) => ExpandResult::new(tt::Subtree::empty(), e),
}
}
+pub(crate) fn include_arg_to_tt(
+ db: &dyn ExpandDatabase,
+ arg_id: MacroCallId,
+) -> Result<(triomphe::Arc<(::tt::Subtree<::tt::TokenId>, TokenMap)>, FileId), ExpandError> {
+ let loc = db.lookup_intern_macro_call(arg_id);
+ let Some(EagerCallInfo {arg, arg_id: Some(arg_id), .. }) = loc.eager.as_deref() else {
+ panic!("include_arg_to_tt called on non include macro call: {:?}", &loc.eager);
+ };
+ let path = parse_string(&arg.0)?;
+ let file_id = relative_file(db, *arg_id, &path, false)?;
+
+ let (subtree, map) =
+ parse_to_token_tree(&db.file_text(file_id)).ok_or(mbe::ExpandError::ConversionError)?;
+ Ok((triomphe::Arc::new((subtree, map)), file_id))
+}
+
fn include_bytes_expand(
_db: &dyn ExpandDatabase,
_arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
if let Err(e) = parse_string(tt) {
- return ExpandResult::with_err(
- ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
- e,
- );
+ return ExpandResult::new(tt::Subtree::empty(), e);
}
// FIXME: actually read the file here if the user asked for macro expansion
@@ -602,22 +720,17 @@ fn include_bytes_expand(
span: tt::TokenId::unspecified(),
}))],
};
- ExpandResult::ok(ExpandedEager::new(res))
+ ExpandResult::ok(res)
}
fn include_str_expand(
db: &dyn ExpandDatabase,
arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let path = match parse_string(tt) {
Ok(it) => it,
- Err(e) => {
- return ExpandResult::with_err(
- ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
- e,
- )
- }
+ Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
};
// FIXME: we're not able to read excluded files (which is most of them because
@@ -627,14 +740,14 @@ fn include_str_expand(
let file_id = match relative_file(db, arg_id, &path, true) {
Ok(file_id) => file_id,
Err(_) => {
- return ExpandResult::ok(ExpandedEager::new(quote!("")));
+ return ExpandResult::ok(quote!(""));
}
};
let text = db.file_text(file_id);
let text = &*text;
- ExpandResult::ok(ExpandedEager::new(quote!(#text)))
+ ExpandResult::ok(quote!(#text))
}
fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &str) -> Option<String> {
@@ -646,15 +759,10 @@ fn env_expand(
db: &dyn ExpandDatabase,
arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let key = match parse_string(tt) {
Ok(it) => it,
- Err(e) => {
- return ExpandResult::with_err(
- ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
- e,
- )
- }
+ Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
};
let mut err = None;
@@ -662,41 +770,34 @@ fn env_expand(
// The only variable rust-analyzer ever sets is `OUT_DIR`, so only diagnose that to avoid
// unnecessary diagnostics for eg. `CARGO_PKG_NAME`.
if key == "OUT_DIR" {
- err = Some(ExpandError::Other(
- r#"`OUT_DIR` not set, enable "build scripts" to fix"#.into(),
- ));
+ err = Some(ExpandError::other(r#"`OUT_DIR` not set, enable "build scripts" to fix"#));
}
// If the variable is unset, still return a dummy string to help type inference along.
// We cannot use an empty string here, because for
// `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
// `include!("foo.rs"), which might go to infinite loop
- "__RA_UNIMPLEMENTED__".to_string()
+ "UNRESOLVED_ENV_VAR".to_string()
});
let expanded = quote! { #s };
- ExpandResult { value: ExpandedEager::new(expanded), err }
+ ExpandResult { value: expanded, err }
}
fn option_env_expand(
db: &dyn ExpandDatabase,
arg_id: MacroCallId,
tt: &tt::Subtree,
-) -> ExpandResult<ExpandedEager> {
+) -> ExpandResult<tt::Subtree> {
let key = match parse_string(tt) {
Ok(it) => it,
- Err(e) => {
- return ExpandResult::with_err(
- ExpandedEager { subtree: tt::Subtree::empty(), included_file: None },
- e,
- )
- }
+ Err(e) => return ExpandResult::new(tt::Subtree::empty(), e),
};
-
+ // FIXME: Use `DOLLAR_CRATE` when that works in eager macros.
let expanded = match get_env_inner(db, arg_id, &key) {
- None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> },
- Some(s) => quote! { #DOLLAR_CRATE::option::Option::Some(#s) },
+ None => quote! { ::core::option::Option::None::<&str> },
+ Some(s) => quote! { ::core::option::Option::Some(#s) },
};
- ExpandResult::ok(ExpandedEager::new(expanded))
+ ExpandResult::ok(expanded)
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index 45572499e..78b2db730 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -1,22 +1,22 @@
//! Defines database & queries for macro expansion.
-use std::sync::Arc;
-
-use base_db::{salsa, SourceDatabase};
+use base_db::{salsa, Edition, SourceDatabase};
use either::Either;
use limit::Limit;
use mbe::syntax_node_to_token_tree;
use rustc_hash::FxHashSet;
use syntax::{
ast::{self, HasAttrs, HasDocComments},
- AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, T,
+ AstNode, GreenNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
};
+use triomphe::Arc;
use crate::{
- ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion, fixup,
- hygiene::HygieneFrame, tt, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
- ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind,
- MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
+ ast_id_map::AstIdMap, builtin_attr_macro::pseudo_derive_attr_expansion,
+ builtin_fn_macro::EagerExpander, fixup, hygiene::HygieneFrame, tt, BuiltinAttrExpander,
+ BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo, ExpandError, ExpandResult,
+ ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId,
+ MacroDefKind, MacroFile, ProcMacroExpander,
};
/// Total limit on the number of tokens produced by any macro invocation.
@@ -33,6 +33,8 @@ pub enum TokenExpander {
DeclarativeMacro { mac: mbe::DeclarativeMacro, def_site_token_map: mbe::TokenMap },
/// Stuff like `line!` and `file!`.
Builtin(BuiltinFnLikeExpander),
+ /// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.)
+ BuiltinEager(EagerExpander),
/// `global_allocator` and such.
BuiltinAttr(BuiltinAttrExpander),
/// `derive(Copy)` and such.
@@ -51,6 +53,7 @@ impl TokenExpander {
match self {
TokenExpander::DeclarativeMacro { mac, .. } => mac.expand(tt).map_err(Into::into),
TokenExpander::Builtin(it) => it.expand(db, id, tt).map_err(Into::into),
+ TokenExpander::BuiltinEager(it) => it.expand(db, id, tt).map_err(Into::into),
TokenExpander::BuiltinAttr(it) => it.expand(db, id, tt),
TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt),
TokenExpander::ProcMacro(_) => {
@@ -66,6 +69,7 @@ impl TokenExpander {
match self {
TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_down(id),
TokenExpander::Builtin(..)
+ | TokenExpander::BuiltinEager(..)
| TokenExpander::BuiltinAttr(..)
| TokenExpander::BuiltinDerive(..)
| TokenExpander::ProcMacro(..) => id,
@@ -76,6 +80,7 @@ impl TokenExpander {
match self {
TokenExpander::DeclarativeMacro { mac, .. } => mac.map_id_up(id),
TokenExpander::Builtin(..)
+ | TokenExpander::BuiltinEager(..)
| TokenExpander::BuiltinAttr(..)
| TokenExpander::BuiltinDerive(..)
| TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
@@ -90,12 +95,15 @@ pub trait ExpandDatabase: SourceDatabase {
/// Main public API -- parses a hir file, not caring whether it's a real
/// file or a macro expansion.
#[salsa::transparent]
- fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
+ fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode;
+ #[salsa::transparent]
+ fn parse_or_expand_with_err(&self, file_id: HirFileId) -> ExpandResult<Parse<SyntaxNode>>;
/// Implementation for the macro case.
+ // This query is LRU cached
fn parse_macro_expansion(
&self,
macro_file: MacroFile,
- ) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>>;
+ ) -> ExpandResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>;
/// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
/// reason why we use salsa at all.
@@ -119,15 +127,27 @@ pub trait ExpandDatabase: SourceDatabase {
/// just fetches procedural ones.
fn macro_def(&self, id: MacroDefId) -> Result<Arc<TokenExpander>, mbe::ParseError>;
- /// Expand macro call to a token tree. This query is LRUed (we keep 128 or so results in memory)
- fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Option<Arc<tt::Subtree>>>;
+ /// Expand macro call to a token tree.
+ // This query is LRU cached
+ fn macro_expand(&self, macro_call: MacroCallId) -> ExpandResult<Arc<tt::Subtree>>;
+ #[salsa::invoke(crate::builtin_fn_macro::include_arg_to_tt)]
+ fn include_expand(
+ &self,
+ arg_id: MacroCallId,
+ ) -> Result<
+ (triomphe::Arc<(::tt::Subtree<::tt::TokenId>, mbe::TokenMap)>, base_db::FileId),
+ ExpandError,
+ >;
/// Special case of the previous query for procedural macros. We can't LRU
/// proc macros, since they are not deterministic in general, and
- /// non-determinism breaks salsa in a very, very, very bad way. @edwin0cheng
- /// heroically debugged this once!
+ /// non-determinism breaks salsa in a very, very, very bad way.
+ /// @edwin0cheng heroically debugged this once!
fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<tt::Subtree>;
- /// Firewall query that returns the error from the `macro_expand` query.
- fn macro_expand_error(&self, macro_call: MacroCallId) -> Option<ExpandError>;
+ /// Firewall query that returns the errors from the `parse_macro_expansion` query.
+ fn parse_macro_expansion_error(
+ &self,
+ macro_call: MacroCallId,
+ ) -> ExpandResult<Box<[SyntaxError]>>;
fn hygiene_frame(&self, file_id: HirFileId) -> Arc<HygieneFrame>;
}
@@ -159,8 +179,8 @@ pub fn expand_speculative(
);
let (attr_arg, token_id) = match loc.kind {
- MacroCallKind::Attr { invoc_attr_index, is_derive, .. } => {
- let attr = if is_derive {
+ MacroCallKind::Attr { invoc_attr_index, .. } => {
+ let attr = if loc.def.is_attribute_derive() {
// for pseudo-derive expansion we actually pass the attribute itself only
ast::Attr::cast(speculative_args.clone())
} else {
@@ -236,17 +256,26 @@ pub fn expand_speculative(
}
fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
- let map = db.parse_or_expand(file_id).map(|it| AstIdMap::from_source(&it)).unwrap_or_default();
- Arc::new(map)
+ Arc::new(AstIdMap::from_source(&db.parse_or_expand(file_id)))
}
-fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option<SyntaxNode> {
+fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
+ match file_id.repr() {
+ HirFileIdRepr::FileId(file_id) => db.parse(file_id).tree().syntax().clone(),
+ HirFileIdRepr::MacroFile(macro_file) => {
+ db.parse_macro_expansion(macro_file).value.0.syntax_node()
+ }
+ }
+}
+
+fn parse_or_expand_with_err(
+ db: &dyn ExpandDatabase,
+ file_id: HirFileId,
+) -> ExpandResult<Parse<SyntaxNode>> {
match file_id.repr() {
- HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
+ HirFileIdRepr::FileId(file_id) => ExpandResult::ok(db.parse(file_id).to_syntax()),
HirFileIdRepr::MacroFile(macro_file) => {
- // FIXME: Note how we convert from `Parse` to `SyntaxNode` here,
- // forgetting about parse errors.
- db.parse_macro_expansion(macro_file).value.map(|(it, _)| it.syntax_node())
+ db.parse_macro_expansion(macro_file).map(|(it, _)| it)
}
}
}
@@ -254,35 +283,9 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> Option<Syntax
fn parse_macro_expansion(
db: &dyn ExpandDatabase,
macro_file: MacroFile,
-) -> ExpandResult<Option<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)>> {
+) -> ExpandResult<(Parse<SyntaxNode>, Arc<mbe::TokenMap>)> {
let _p = profile::span("parse_macro_expansion");
- let mbe::ValueResult { value, err } = db.macro_expand(macro_file.macro_call_id);
-
- if let Some(err) = &err {
- // Note:
- // The final goal we would like to make all parse_macro success,
- // such that the following log will not call anyway.
- let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
- let node = loc.kind.to_node(db);
-
- // collect parent information for warning log
- let parents =
- std::iter::successors(loc.kind.file_id().call_node(db), |it| it.file_id.call_node(db))
- .map(|n| format!("{:#}", n.value))
- .collect::<Vec<_>>()
- .join("\n");
-
- tracing::debug!(
- "fail on macro_parse: (reason: {:?} macro_call: {:#}) parents: {}",
- err,
- node.value,
- parents
- );
- }
- let tt = match value {
- Some(tt) => tt,
- None => return ExpandResult { value: None, err },
- };
+ let mbe::ValueResult { value: tt, err } = db.macro_expand(macro_file.macro_call_id);
let expand_to = macro_expand_to(db, macro_file.macro_call_id);
@@ -291,16 +294,21 @@ fn parse_macro_expansion(
let (parse, rev_token_map) = token_tree_to_syntax_node(&tt, expand_to);
- ExpandResult { value: Some((parse, Arc::new(rev_token_map))), err }
+ ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
}
fn macro_arg(
db: &dyn ExpandDatabase,
id: MacroCallId,
) -> Option<Arc<(tt::Subtree, mbe::TokenMap, fixup::SyntaxFixupUndoInfo)>> {
- let arg = db.macro_arg_text(id)?;
let loc = db.lookup_intern_macro_call(id);
+ if let Some(EagerCallInfo { arg, arg_id: Some(_), error: _ }) = loc.eager.as_deref() {
+ return Some(Arc::new((arg.0.clone(), arg.1.clone(), Default::default())));
+ }
+
+ let arg = db.macro_arg_text(id)?;
+
let node = SyntaxNode::new_root(arg);
let censor = censor_for_macro_input(&loc, &node);
let mut fixups = fixup::fixup_syntax(&node);
@@ -339,7 +347,7 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet<Sy
.map(|it| it.syntax().clone())
.collect()
}
- MacroCallKind::Attr { is_derive: true, .. } => return None,
+ MacroCallKind::Attr { .. } if loc.def.is_attribute_derive() => return None,
MacroCallKind::Attr { invoc_attr_index, .. } => {
cov_mark::hit!(attribute_macro_attr_censoring);
ast::Item::cast(node.clone())?
@@ -376,7 +384,17 @@ fn macro_arg_text(db: &dyn ExpandDatabase, id: MacroCallId) -> Option<GreenNode>
return None;
}
}
- Some(arg.green().into())
+ if let Some(EagerCallInfo { arg, .. }) = loc.eager.as_deref() {
+ Some(
+ mbe::token_tree_to_syntax_node(&arg.0, mbe::TopEntryPoint::Expr)
+ .0
+ .syntax_node()
+ .green()
+ .into(),
+ )
+ } else {
+ Some(arg.green().into())
+ }
}
fn macro_def(
@@ -385,13 +403,14 @@ fn macro_def(
) -> Result<Arc<TokenExpander>, mbe::ParseError> {
match id.kind {
MacroDefKind::Declarative(ast_id) => {
+ let is_2021 = db.crate_graph()[id.krate].edition >= Edition::Edition2021;
let (mac, def_site_token_map) = match ast_id.to_node(db) {
ast::Macro::MacroRules(macro_rules) => {
let arg = macro_rules
.token_tree()
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
- let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt)?;
+ let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021)?;
(mac, def_site_token_map)
}
ast::Macro::MacroDef(macro_def) => {
@@ -399,7 +418,7 @@ fn macro_def(
.body()
.ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?;
let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax());
- let mac = mbe::DeclarativeMacro::parse_macro2(&tt)?;
+ let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021)?;
(mac, def_site_token_map)
}
};
@@ -412,82 +431,98 @@ fn macro_def(
MacroDefKind::BuiltInDerive(expander, _) => {
Ok(Arc::new(TokenExpander::BuiltinDerive(expander)))
}
- MacroDefKind::BuiltInEager(..) => {
- // FIXME: Return a random error here just to make the types align.
- // This obviously should do something real instead.
- Err(mbe::ParseError::UnexpectedToken("unexpected eager macro".into()))
+ MacroDefKind::BuiltInEager(expander, ..) => {
+ Ok(Arc::new(TokenExpander::BuiltinEager(expander)))
}
MacroDefKind::ProcMacro(expander, ..) => Ok(Arc::new(TokenExpander::ProcMacro(expander))),
}
}
-fn macro_expand(
- db: &dyn ExpandDatabase,
- id: MacroCallId,
-) -> ExpandResult<Option<Arc<tt::Subtree>>> {
+fn macro_expand(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<Arc<tt::Subtree>> {
let _p = profile::span("macro_expand");
- let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
- if let Some(eager) = &loc.eager {
- return ExpandResult {
- value: Some(eager.arg_or_expansion.clone()),
- // FIXME: There could be errors here!
- err: None,
- };
+ let loc = db.lookup_intern_macro_call(id);
+ if let Some(EagerCallInfo { arg, arg_id: None, error }) = loc.eager.as_deref() {
+ // This is an input expansion for an eager macro. These are already pre-expanded
+ return ExpandResult { value: Arc::new(arg.0.clone()), err: error.clone() };
}
-
- let macro_arg = match db.macro_arg(id) {
- Some(it) => it,
- None => {
- return ExpandResult::only_err(ExpandError::Other(
- "Failed to lower macro args to token tree".into(),
- ))
- }
- };
-
let expander = match db.macro_def(loc.def) {
Ok(it) => it,
- // FIXME: This is weird -- we effectively report macro *definition*
- // errors lazily, when we try to expand the macro. Instead, they should
- // be reported at the definition site (when we construct a def map).
+ // FIXME: We should make sure to enforce a variant that invalid macro
+ // definitions do not get expanders that could reach this call path!
Err(err) => {
- return ExpandResult::only_err(ExpandError::Other(
- format!("invalid macro definition: {err}").into(),
- ))
+ return ExpandResult {
+ value: Arc::new(tt::Subtree {
+ delimiter: tt::Delimiter::UNSPECIFIED,
+ token_trees: vec![],
+ }),
+ err: Some(ExpandError::other(format!("invalid macro definition: {err}"))),
+ }
}
};
- let ExpandResult { value: mut tt, err } = expander.expand(db, id, &macro_arg.0);
+ let Some(macro_arg) = db.macro_arg(id) else {
+ return ExpandResult {
+ value: Arc::new(
+ tt::Subtree {
+ delimiter: tt::Delimiter::UNSPECIFIED,
+ token_trees: Vec::new(),
+ },
+ ),
+ // FIXME: We should make sure to enforce a variant that invalid macro
+ // calls do not reach this call path!
+ err: Some(ExpandError::other(
+ "invalid token tree"
+ )),
+ };
+ };
+ let (arg_tt, arg_tm, undo_info) = &*macro_arg;
+ let ExpandResult { value: mut tt, mut err } = expander.expand(db, id, arg_tt);
+
+ if let Some(EagerCallInfo { error, .. }) = loc.eager.as_deref() {
+ // FIXME: We should report both errors!
+ err = error.clone().or(err);
+ }
+
// Set a hard limit for the expanded tt
let count = tt.count();
if TOKEN_LIMIT.check(count).is_err() {
- return ExpandResult::only_err(ExpandError::Other(
- format!(
+ return ExpandResult {
+ value: Arc::new(tt::Subtree {
+ delimiter: tt::Delimiter::UNSPECIFIED,
+ token_trees: vec![],
+ }),
+ err: Some(ExpandError::other(format!(
"macro invocation exceeds token limit: produced {} tokens, limit is {}",
count,
TOKEN_LIMIT.inner(),
- )
- .into(),
- ));
+ ))),
+ };
}
- fixup::reverse_fixups(&mut tt, &macro_arg.1, &macro_arg.2);
+ fixup::reverse_fixups(&mut tt, arg_tm, undo_info);
- ExpandResult { value: Some(Arc::new(tt)), err }
+ ExpandResult { value: Arc::new(tt), err }
}
-fn macro_expand_error(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> Option<ExpandError> {
- db.macro_expand(macro_call).err
+fn parse_macro_expansion_error(
+ db: &dyn ExpandDatabase,
+ macro_call_id: MacroCallId,
+) -> ExpandResult<Box<[SyntaxError]>> {
+ db.parse_macro_expansion(MacroFile { macro_call_id })
+ .map(|it| it.0.errors().to_vec().into_boxed_slice())
}
fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::Subtree> {
- let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
- let macro_arg = match db.macro_arg(id) {
- Some(it) => it,
- None => {
- return ExpandResult::with_err(
- tt::Subtree::empty(),
- ExpandError::Other("No arguments for proc-macro".into()),
- )
- }
+ let loc = db.lookup_intern_macro_call(id);
+ let Some(macro_arg) = db.macro_arg(id) else {
+ return ExpandResult {
+ value: tt::Subtree {
+ delimiter: tt::Delimiter::UNSPECIFIED,
+ token_trees: Vec::new(),
+ },
+ err: Some(ExpandError::other(
+ "invalid token tree"
+ )),
+ };
};
let expander = match loc.def.kind {
@@ -512,8 +547,7 @@ fn hygiene_frame(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<HygieneFram
}
fn macro_expand_to(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandTo {
- let loc: MacroCallLoc = db.lookup_intern_macro_call(id);
- loc.kind.expand_to()
+ db.lookup_intern_macro_call(id).expand_to()
}
fn token_tree_to_syntax_node(
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
index aca41b11f..7ee3fd375 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/eager.rs
@@ -18,10 +18,9 @@
//!
//!
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
-use std::sync::Arc;
-
use base_db::CrateId;
-use syntax::{ted, SyntaxNode};
+use syntax::{ted, Parse, SyntaxNode};
+use triomphe::Arc;
use crate::{
ast::{self, AstNode},
@@ -32,143 +31,77 @@ use crate::{
MacroCallLoc, MacroDefId, MacroDefKind, UnresolvedMacro,
};
-#[derive(Debug)]
-pub struct ErrorEmitted {
- _private: (),
-}
-
-pub trait ErrorSink {
- fn emit(&mut self, err: ExpandError);
-
- fn option<T>(
- &mut self,
- opt: Option<T>,
- error: impl FnOnce() -> ExpandError,
- ) -> Result<T, ErrorEmitted> {
- match opt {
- Some(it) => Ok(it),
- None => {
- self.emit(error());
- Err(ErrorEmitted { _private: () })
- }
- }
- }
-
- fn option_with<T>(
- &mut self,
- opt: impl FnOnce() -> Option<T>,
- error: impl FnOnce() -> ExpandError,
- ) -> Result<T, ErrorEmitted> {
- self.option(opt(), error)
- }
-
- fn result<T>(&mut self, res: Result<T, ExpandError>) -> Result<T, ErrorEmitted> {
- match res {
- Ok(it) => Ok(it),
- Err(e) => {
- self.emit(e);
- Err(ErrorEmitted { _private: () })
- }
- }
- }
-
- fn expand_result_option<T>(&mut self, res: ExpandResult<Option<T>>) -> Result<T, ErrorEmitted> {
- match (res.value, res.err) {
- (None, Some(err)) => {
- self.emit(err);
- Err(ErrorEmitted { _private: () })
- }
- (Some(value), opt_err) => {
- if let Some(err) = opt_err {
- self.emit(err);
- }
- Ok(value)
- }
- (None, None) => unreachable!("`ExpandResult` without value or error"),
- }
- }
-}
-
-impl ErrorSink for &'_ mut dyn FnMut(ExpandError) {
- fn emit(&mut self, err: ExpandError) {
- self(err);
- }
-}
-
-pub fn expand_eager_macro(
+pub fn expand_eager_macro_input(
db: &dyn ExpandDatabase,
krate: CrateId,
macro_call: InFile<ast::MacroCall>,
def: MacroDefId,
resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
- diagnostic_sink: &mut dyn FnMut(ExpandError),
-) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
- let hygiene = Hygiene::new(db, macro_call.file_id);
- let parsed_args = macro_call
- .value
- .token_tree()
- .map(|tt| mbe::syntax_node_to_token_tree(tt.syntax()).0)
- .unwrap_or_else(tt::Subtree::empty);
+) -> Result<ExpandResult<Option<MacroCallId>>, UnresolvedMacro> {
+ assert!(matches!(def.kind, MacroDefKind::BuiltInEager(..)));
+ let token_tree = macro_call.value.token_tree();
+
+ let Some(token_tree) = token_tree else {
+ return Ok(ExpandResult { value: None, err:
+ Some(ExpandError::other(
+ "invalid token tree"
+ )),
+ });
+ };
+ let (parsed_args, arg_token_map) = mbe::syntax_node_to_token_tree(token_tree.syntax());
let ast_map = db.ast_id_map(macro_call.file_id);
let call_id = InFile::new(macro_call.file_id, ast_map.ast_id(&macro_call.value));
let expand_to = ExpandTo::from_call_site(&macro_call.value);
// Note:
- // When `lazy_expand` is called, its *parent* file must be already exists.
- // Here we store an eager macro id for the argument expanded subtree here
+ // When `lazy_expand` is called, its *parent* file must already exist.
+ // Here we store an eager macro id for the argument expanded subtree
// for that purpose.
let arg_id = db.intern_macro_call(MacroCallLoc {
def,
krate,
- eager: Some(EagerCallInfo {
- arg_or_expansion: Arc::new(parsed_args.clone()),
- included_file: None,
- }),
+ eager: Some(Box::new(EagerCallInfo {
+ arg: Arc::new((parsed_args, arg_token_map)),
+ arg_id: None,
+ error: None,
+ })),
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
});
-
- let parsed_args = mbe::token_tree_to_syntax_node(&parsed_args, mbe::TopEntryPoint::Expr).0;
- let result = match eager_macro_recur(
+ let arg_as_expr = match db.macro_arg_text(arg_id) {
+ Some(it) => it,
+ None => {
+ return Ok(ExpandResult {
+ value: None,
+ err: Some(ExpandError::other("invalid token tree")),
+ })
+ }
+ };
+ let ExpandResult { value: expanded_eager_input, err } = eager_macro_recur(
db,
- &hygiene,
- InFile::new(arg_id.as_file(), parsed_args.syntax_node()),
+ &Hygiene::new(db, macro_call.file_id),
+ InFile::new(arg_id.as_file(), SyntaxNode::new_root(arg_as_expr)),
krate,
resolver,
- diagnostic_sink,
- ) {
- Ok(Ok(it)) => it,
- Ok(Err(err)) => return Ok(Err(err)),
- Err(err) => return Err(err),
+ )?;
+ let Some(expanded_eager_input) = expanded_eager_input else {
+ return Ok(ExpandResult { value: None, err })
};
- let subtree = to_subtree(&result);
-
- if let MacroDefKind::BuiltInEager(eager, _) = def.kind {
- let res = eager.expand(db, arg_id, &subtree);
- if let Some(err) = res.err {
- diagnostic_sink(err);
- }
-
- let loc = MacroCallLoc {
- def,
- krate,
- eager: Some(EagerCallInfo {
- arg_or_expansion: Arc::new(res.value.subtree),
- included_file: res.value.included_file,
- }),
- kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
- };
+ let (mut subtree, token_map) = mbe::syntax_node_to_token_tree(&expanded_eager_input);
+ subtree.delimiter = crate::tt::Delimiter::unspecified();
- Ok(Ok(db.intern_macro_call(loc)))
- } else {
- panic!("called `expand_eager_macro` on non-eager macro def {def:?}");
- }
-}
+ let loc = MacroCallLoc {
+ def,
+ krate,
+ eager: Some(Box::new(EagerCallInfo {
+ arg: Arc::new((subtree, token_map)),
+ arg_id: Some(arg_id),
+ error: err.clone(),
+ })),
+ kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
+ };
-fn to_subtree(node: &SyntaxNode) -> crate::tt::Subtree {
- let mut subtree = mbe::syntax_node_to_token_tree(node).0;
- subtree.delimiter = crate::tt::Delimiter::unspecified();
- subtree
+ Ok(ExpandResult { value: Some(db.intern_macro_call(loc)), err })
}
fn lazy_expand(
@@ -176,7 +109,7 @@ fn lazy_expand(
def: &MacroDefId,
macro_call: InFile<ast::MacroCall>,
krate: CrateId,
-) -> ExpandResult<Option<InFile<SyntaxNode>>> {
+) -> ExpandResult<InFile<Parse<SyntaxNode>>> {
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
let expand_to = ExpandTo::from_call_site(&macro_call.value);
@@ -186,10 +119,9 @@ fn lazy_expand(
MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), expand_to },
);
- let err = db.macro_expand_error(id);
- let value = db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node));
+ let macro_file = id.as_macro_file();
- ExpandResult { value, err }
+ db.parse_macro_expansion(macro_file).map(|parse| InFile::new(macro_file.into(), parse.0))
}
fn eager_macro_recur(
@@ -198,69 +130,83 @@ fn eager_macro_recur(
curr: InFile<SyntaxNode>,
krate: CrateId,
macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
- mut diagnostic_sink: &mut dyn FnMut(ExpandError),
-) -> Result<Result<SyntaxNode, ErrorEmitted>, UnresolvedMacro> {
+) -> Result<ExpandResult<Option<SyntaxNode>>, UnresolvedMacro> {
let original = curr.value.clone_for_update();
let children = original.descendants().filter_map(ast::MacroCall::cast);
let mut replacements = Vec::new();
+ // Note: We only report a single error inside of eager expansions
+ let mut error = None;
+
// Collect replacement
for child in children {
let def = match child.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
Some(path) => macro_resolver(path.clone()).ok_or(UnresolvedMacro { path })?,
None => {
- diagnostic_sink(ExpandError::Other("malformed macro invocation".into()));
+ error = Some(ExpandError::other("malformed macro invocation"));
continue;
}
};
- let insert = match def.kind {
+ let ExpandResult { value, err } = match def.kind {
MacroDefKind::BuiltInEager(..) => {
- let id = match expand_eager_macro(
+ let ExpandResult { value, err } = match expand_eager_macro_input(
db,
krate,
curr.with_value(child.clone()),
def,
macro_resolver,
- diagnostic_sink,
) {
- Ok(Ok(it)) => it,
- Ok(Err(err)) => return Ok(Err(err)),
+ Ok(it) => it,
Err(err) => return Err(err),
};
- db.parse_or_expand(id.as_file())
- .expect("successful macro expansion should be parseable")
- .clone_for_update()
+ match value {
+ Some(call) => {
+ let ExpandResult { value, err: err2 } =
+ db.parse_macro_expansion(call.as_macro_file());
+ ExpandResult {
+ value: Some(value.0.syntax_node().clone_for_update()),
+ err: err.or(err2),
+ }
+ }
+ None => ExpandResult { value: None, err },
+ }
}
MacroDefKind::Declarative(_)
| MacroDefKind::BuiltIn(..)
| MacroDefKind::BuiltInAttr(..)
| MacroDefKind::BuiltInDerive(..)
| MacroDefKind::ProcMacro(..) => {
- let res = lazy_expand(db, &def, curr.with_value(child.clone()), krate);
- let val = match diagnostic_sink.expand_result_option(res) {
- Ok(it) => it,
- Err(err) => return Ok(Err(err)),
- };
+ let ExpandResult { value, err } =
+ lazy_expand(db, &def, curr.with_value(child.clone()), krate);
// replace macro inside
- let hygiene = Hygiene::new(db, val.file_id);
- match eager_macro_recur(db, &hygiene, val, krate, macro_resolver, diagnostic_sink) {
- Ok(Ok(it)) => it,
- Ok(Err(err)) => return Ok(Err(err)),
- Err(err) => return Err(err),
- }
+ let hygiene = Hygiene::new(db, value.file_id);
+ let ExpandResult { value, err: error } = eager_macro_recur(
+ db,
+ &hygiene,
+ // FIXME: We discard parse errors here
+ value.map(|it| it.syntax_node()),
+ krate,
+ macro_resolver,
+ )?;
+ let err = err.or(error);
+ ExpandResult { value, err }
}
};
-
+ if err.is_some() {
+ error = err;
+ }
// check if the whole original syntax is replaced
if child.syntax() == &original {
- return Ok(Ok(insert));
+ return Ok(ExpandResult { value, err: error });
}
- replacements.push((child, insert));
+ if let Some(insert) = value {
+ replacements.push((child, insert));
+ }
}
replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
- Ok(Ok(original))
+ Ok(ExpandResult { value: Some(original), err: error })
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
index b273f2176..00796e7c0 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/fixup.rs
@@ -14,7 +14,7 @@ use tt::token_id::Subtree;
/// The result of calculating fixes for a syntax node -- a bunch of changes
/// (appending to and replacing nodes), the information that is needed to
/// reverse those changes afterwards, and a token map.
-#[derive(Debug)]
+#[derive(Debug, Default)]
pub(crate) struct SyntaxFixups {
pub(crate) append: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
pub(crate) replace: FxHashMap<SyntaxElement, Vec<SyntheticToken>>,
@@ -24,7 +24,7 @@ pub(crate) struct SyntaxFixups {
}
/// This is the information needed to reverse the fixups.
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, Default, PartialEq, Eq)]
pub struct SyntaxFixupUndoInfo {
original: Vec<Subtree>,
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
index 2eb56fc9e..10f8fe9ce 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
@@ -2,8 +2,6 @@
//!
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
//! this moment, this is horribly incomplete and handles only `$crate`.
-use std::sync::Arc;
-
use base_db::CrateId;
use db::TokenExpander;
use either::Either;
@@ -12,6 +10,7 @@ use syntax::{
ast::{self, HasDocComments},
AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize,
};
+use triomphe::Arc;
use crate::{
db::{self, ExpandDatabase},
@@ -200,8 +199,14 @@ fn make_hygiene_info(
});
let macro_def = db.macro_def(loc.def).ok()?;
- let (_, exp_map) = db.parse_macro_expansion(macro_file).value?;
- let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
+ let (_, exp_map) = db.parse_macro_expansion(macro_file).value;
+ let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| {
+ Arc::new((
+ tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
+ Default::default(),
+ Default::default(),
+ ))
+ });
Some(HygieneInfo {
file: macro_file,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index 5e99eacc1..e0c199328 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -20,11 +20,13 @@ pub mod mod_path;
pub mod attrs;
mod fixup;
+use mbe::TokenMap;
pub use mbe::{Origin, ValueResult};
use ::tt::token_id as tt;
+use triomphe::Arc;
-use std::{fmt, hash::Hash, iter, sync::Arc};
+use std::{fmt, hash::Hash, iter};
use base_db::{
impl_intern_key,
@@ -51,12 +53,18 @@ use crate::{
pub type ExpandResult<T> = ValueResult<T, ExpandError>;
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ExpandError {
UnresolvedProcMacro(CrateId),
Mbe(mbe::ExpandError),
- RecursionOverflowPosioned,
- Other(Box<str>),
+ RecursionOverflowPoisoned,
+ Other(Box<Box<str>>),
+}
+
+impl ExpandError {
+ pub fn other(msg: impl Into<Box<str>>) -> Self {
+ ExpandError::Other(Box::new(msg.into()))
+ }
}
impl From<mbe::ExpandError> for ExpandError {
@@ -70,7 +78,7 @@ impl fmt::Display for ExpandError {
match self {
ExpandError::UnresolvedProcMacro(_) => f.write_str("unresolved proc-macro"),
ExpandError::Mbe(it) => it.fmt(f),
- ExpandError::RecursionOverflowPosioned => {
+ ExpandError::RecursionOverflowPoisoned => {
f.write_str("overflow expanding the original macro")
}
ExpandError::Other(it) => f.write_str(it),
@@ -95,9 +103,15 @@ impl fmt::Display for ExpandError {
/// The two variants are encoded in a single u32 which are differentiated by the MSB.
/// If the MSB is 0, the value represents a `FileId`, otherwise the remaining 31 bits represent a
/// `MacroCallId`.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct HirFileId(u32);
+impl fmt::Debug for HirFileId {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.repr().fmt(f)
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MacroFile {
pub macro_call_id: MacroCallId,
@@ -113,7 +127,8 @@ impl_intern_key!(MacroCallId);
pub struct MacroCallLoc {
pub def: MacroDefId,
pub(crate) krate: CrateId,
- eager: Option<EagerCallInfo>,
+ /// Some if `def` is a builtin eager macro.
+ eager: Option<Box<EagerCallInfo>>,
pub kind: MacroCallKind,
}
@@ -138,8 +153,11 @@ pub enum MacroDefKind {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct EagerCallInfo {
/// NOTE: This can be *either* the expansion result, *or* the argument to the eager macro!
- arg_or_expansion: Arc<tt::Subtree>,
- included_file: Option<FileId>,
+ arg: Arc<(tt::Subtree, TokenMap)>,
+ /// call id of the eager macro's input file. If this is none, macro call containing this call info
+ /// is an eager macro's input, otherwise it is its output.
+ arg_id: Option<MacroCallId>,
+ error: Option<ExpandError>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -166,8 +184,6 @@ pub enum MacroCallKind {
/// Outer attributes are counted first, then inner attributes. This does not support
/// out-of-line modules, which may have attributes spread across 2 files!
invoc_attr_index: AttrId,
- /// Whether this attribute is the `#[derive]` attribute.
- is_derive: bool,
},
}
@@ -205,10 +221,15 @@ impl HirFileId {
HirFileIdRepr::FileId(id) => break id,
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_call_id);
- file_id = match loc.eager {
- Some(EagerCallInfo { included_file: Some(file), .. }) => file.into(),
+ let is_include_expansion = loc.def.is_include()
+ && matches!(
+ loc.eager.as_deref(),
+ Some(EagerCallInfo { arg_id: Some(_), .. })
+ );
+ file_id = match is_include_expansion.then(|| db.include_expand(macro_call_id)) {
+ Some(Ok((_, file))) => file.into(),
_ => loc.kind.file_id(),
- };
+ }
}
}
}
@@ -230,18 +251,17 @@ impl HirFileId {
pub fn call_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> {
let macro_file = self.macro_file()?;
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
- Some(loc.kind.to_node(db))
+ Some(loc.to_node(db))
}
/// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
pub fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option<(FileId, SyntaxNode)> {
- let mut call =
- db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).kind.to_node(db);
+ let mut call = db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).to_node(db);
loop {
match call.file_id.repr() {
HirFileIdRepr::FileId(file_id) => break Some((file_id, call.value)),
HirFileIdRepr::MacroFile(MacroFile { macro_call_id }) => {
- call = db.lookup_intern_macro_call(macro_call_id).kind.to_node(db);
+ call = db.lookup_intern_macro_call(macro_call_id).to_node(db);
}
}
}
@@ -255,8 +275,14 @@ impl HirFileId {
let arg_tt = loc.kind.arg(db)?;
let macro_def = db.macro_def(loc.def).ok()?;
- let (parse, exp_map) = db.parse_macro_expansion(macro_file).value?;
- let macro_arg = db.macro_arg(macro_file.macro_call_id)?;
+ let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
+ let macro_arg = db.macro_arg(macro_file.macro_call_id).unwrap_or_else(|| {
+ Arc::new((
+ tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: Vec::new() },
+ Default::default(),
+ Default::default(),
+ ))
+ });
let def = loc.def.ast_id().left().and_then(|id| {
let def_tt = match id.to_node(db) {
@@ -298,7 +324,7 @@ impl HirFileId {
let macro_file = self.macro_file()?;
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
let attr = match loc.def.kind {
- MacroDefKind::BuiltInDerive(..) => loc.kind.to_node(db),
+ MacroDefKind::BuiltInDerive(..) => loc.to_node(db),
_ => return None,
};
Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
@@ -319,7 +345,17 @@ impl HirFileId {
match self.macro_file() {
Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
- matches!(loc.eager, Some(EagerCallInfo { included_file: Some(_), .. }))
+ loc.def.is_include()
+ }
+ _ => false,
+ }
+ }
+
+ pub fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool {
+ match self.macro_file() {
+ Some(macro_file) => {
+ let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
+ matches!(loc.eager.as_deref(), Some(EagerCallInfo { .. }))
}
_ => false,
}
@@ -342,7 +378,7 @@ impl HirFileId {
match self.macro_file() {
Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
- matches!(loc.kind, MacroCallKind::Attr { is_derive: true, .. })
+ loc.def.is_attribute_derive()
}
None => false,
}
@@ -413,22 +449,19 @@ impl MacroDefId {
MacroDefKind::BuiltInAttr(..) | MacroDefKind::ProcMacro(_, ProcMacroKind::Attr, _)
)
}
-}
-// FIXME: attribute indices do not account for nested `cfg_attr`
+ pub fn is_attribute_derive(&self) -> bool {
+ matches!(self.kind, MacroDefKind::BuiltInAttr(expander, ..) if expander.is_derive())
+ }
-impl MacroCallKind {
- /// Returns the file containing the macro invocation.
- fn file_id(&self) -> HirFileId {
- match *self {
- MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
- | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
- | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
- }
+ pub fn is_include(&self) -> bool {
+ matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include())
}
+}
+impl MacroCallLoc {
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> InFile<SyntaxNode> {
- match self {
+ match self.kind {
MacroCallKind::FnLike { ast_id, .. } => {
ast_id.with_value(ast_id.to_node(db).syntax().clone())
}
@@ -444,23 +477,49 @@ impl MacroCallKind {
.unwrap_or_else(|| it.syntax().clone())
})
}
- MacroCallKind::Attr { ast_id, is_derive: true, invoc_attr_index, .. } => {
- // FIXME: handle `cfg_attr`
- ast_id.with_value(ast_id.to_node(db)).map(|it| {
- it.doc_comments_and_attrs()
- .nth(invoc_attr_index.ast_index())
- .and_then(|it| match it {
- Either::Left(attr) => Some(attr.syntax().clone()),
- Either::Right(_) => None,
- })
- .unwrap_or_else(|| it.syntax().clone())
- })
+ MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
+ if self.def.is_attribute_derive() {
+ // FIXME: handle `cfg_attr`
+ ast_id.with_value(ast_id.to_node(db)).map(|it| {
+ it.doc_comments_and_attrs()
+ .nth(invoc_attr_index.ast_index())
+ .and_then(|it| match it {
+ Either::Left(attr) => Some(attr.syntax().clone()),
+ Either::Right(_) => None,
+ })
+ .unwrap_or_else(|| it.syntax().clone())
+ })
+ } else {
+ ast_id.with_value(ast_id.to_node(db).syntax().clone())
+ }
}
- MacroCallKind::Attr { ast_id, .. } => {
- ast_id.with_value(ast_id.to_node(db).syntax().clone())
+ }
+ }
+
+ fn expand_to(&self) -> ExpandTo {
+ match self.kind {
+ MacroCallKind::FnLike { expand_to, .. } => expand_to,
+ MacroCallKind::Derive { .. } => ExpandTo::Items,
+ MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Statements,
+ MacroCallKind::Attr { .. } => {
+ // is this always correct?
+ ExpandTo::Items
}
}
}
+}
+
+// FIXME: attribute indices do not account for nested `cfg_attr`
+
+impl MacroCallKind {
+ /// Returns the file containing the macro invocation.
+ fn file_id(&self) -> HirFileId {
+ match *self {
+ MacroCallKind::FnLike { ast_id: InFile { file_id, .. }, .. }
+ | MacroCallKind::Derive { ast_id: InFile { file_id, .. }, .. }
+ | MacroCallKind::Attr { ast_id: InFile { file_id, .. }, .. } => file_id,
+ }
+ }
/// Returns the original file range that best describes the location of this macro call.
///
@@ -538,21 +597,16 @@ impl MacroCallKind {
MacroCallKind::Attr { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
}
}
-
- fn expand_to(&self) -> ExpandTo {
- match self {
- MacroCallKind::FnLike { expand_to, .. } => *expand_to,
- MacroCallKind::Derive { .. } => ExpandTo::Items,
- MacroCallKind::Attr { is_derive: true, .. } => ExpandTo::Statements,
- MacroCallKind::Attr { .. } => ExpandTo::Items, // is this always correct?
- }
- }
}
impl MacroCallId {
pub fn as_file(self) -> HirFileId {
MacroFile { macro_call_id: self }.into()
}
+
+ pub fn as_macro_file(self) -> MacroFile {
+ MacroFile { macro_call_id: self }
+ }
}
/// ExpansionInfo mainly describes how to map text range between src and expanded macro
@@ -610,7 +664,7 @@ impl ExpansionInfo {
let token_range = token.value.text_range();
match &loc.kind {
- MacroCallKind::Attr { attr_args, invoc_attr_index, is_derive, .. } => {
+ MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => {
// FIXME: handle `cfg_attr`
let attr = item
.doc_comments_and_attrs()
@@ -626,7 +680,8 @@ impl ExpansionInfo {
token.value.text_range().checked_sub(attr_input_start)?;
// shift by the item's tree's max id
let token_id = attr_args.1.token_by_range(relative_range)?;
- let token_id = if *is_derive {
+
+ let token_id = if loc.def.is_attribute_derive() {
// we do not shift for `#[derive]`, as we only need to downmap the derive attribute tokens
token_id
} else {
@@ -645,7 +700,7 @@ impl ExpansionInfo {
let token_id = match token_id_in_attr_input {
Some(token_id) => token_id,
- // the token is not inside an attribute's input so do the lookup in the macro_arg as usual
+ // the token is not inside `an attribute's input so do the lookup in the macro_arg as usual
None => {
let relative_range =
token.value.text_range().checked_sub(self.arg.value.text_range().start())?;
@@ -677,20 +732,35 @@ impl ExpansionInfo {
let call_id = self.expanded.file_id.macro_file()?.macro_call_id;
let loc = db.lookup_intern_macro_call(call_id);
+ // Special case: map tokens from `include!` expansions to the included file
+ if loc.def.is_include()
+ && matches!(loc.eager.as_deref(), Some(EagerCallInfo { arg_id: Some(_), .. }))
+ {
+ if let Ok((tt_and_map, file_id)) = db.include_expand(call_id) {
+ let range = tt_and_map.1.first_range_by_token(token_id, token.value.kind())?;
+ let source = db.parse(file_id);
+
+ let token = source.syntax_node().covering_element(range).into_token()?;
+
+ return Some((InFile::new(file_id.into(), token), Origin::Call));
+ }
+ }
+
// Attributes are a bit special for us, they have two inputs, the input tokentree and the annotated item.
let (token_map, tt) = match &loc.kind {
- MacroCallKind::Attr { attr_args, is_derive: true, .. } => {
- (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
- }
MacroCallKind::Attr { attr_args, .. } => {
- // try unshifting the the token id, if unshifting fails, the token resides in the non-item attribute input
- // note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
- match self.macro_arg_shift.unshift(token_id) {
- Some(unshifted) => {
- token_id = unshifted;
- (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
+ if loc.def.is_attribute_derive() {
+ (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
+ } else {
+ // try unshifting the token id, if unshifting fails, the token resides in the non-item attribute input
+ // note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
+ match self.macro_arg_shift.unshift(token_id) {
+ Some(unshifted) => {
+ token_id = unshifted;
+ (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
+ }
+ None => (&self.macro_arg.1, self.arg.clone()),
}
- None => (&self.macro_arg.1, self.arg.clone()),
}
}
_ => match origin {
@@ -718,7 +788,7 @@ pub type AstId<N> = InFile<FileAstId<N>>;
impl<N: AstNode> AstId<N> {
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N {
- let root = db.parse_or_expand(self.file_id).unwrap();
+ let root = db.parse_or_expand(self.file_id);
db.ast_id_map(self.file_id).get(self.value).to_node(&root)
}
}
@@ -754,7 +824,7 @@ impl<T> InFile<T> {
}
pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
- db.parse_or_expand(self.file_id).expect("source created from invalid file")
+ db.parse_or_expand(self.file_id)
}
}
@@ -950,6 +1020,7 @@ fn ascend_node_border_tokens(
let first_token = |node: &SyntaxNode| skip_trivia_token(node.first_token()?, Direction::Next);
let last_token = |node: &SyntaxNode| skip_trivia_token(node.last_token()?, Direction::Prev);
+ // FIXME: Once the token map rewrite is done, this shouldnt need to rely on syntax nodes and tokens anymore
let first = first_token(node)?;
let last = last_token(node)?;
let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?;
@@ -977,6 +1048,7 @@ impl<N: AstNode> InFile<N> {
self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n))
}
+ // FIXME: this should return `Option<InFileNotHirFile<N>>`
pub fn original_ast_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<N>> {
// This kind of upmapping can only be achieved in attribute expanded files,
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
index e9393cc89..47a8ab7de 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
@@ -1,7 +1,7 @@
//! A lowering for `use`-paths (more generally, paths without angle-bracketed segments).
use std::{
- fmt::{self, Display},
+ fmt::{self, Display as _},
iter,
};
@@ -24,6 +24,12 @@ pub struct ModPath {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnescapedModPath<'a>(&'a ModPath);
+impl<'a> UnescapedModPath<'a> {
+ pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
+ UnescapedDisplay { db, path: self }
+ }
+}
+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PathKind {
Plain,
@@ -110,52 +116,30 @@ impl ModPath {
UnescapedModPath(self)
}
- fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result {
- let mut first_segment = true;
- let mut add_segment = |s| -> fmt::Result {
- if !first_segment {
- f.write_str("::")?;
- }
- first_segment = false;
- f.write_str(s)?;
- Ok(())
- };
- match self.kind {
- PathKind::Plain => {}
- PathKind::Super(0) => add_segment("self")?,
- PathKind::Super(n) => {
- for _ in 0..n {
- add_segment("super")?;
- }
- }
- PathKind::Crate => add_segment("crate")?,
- PathKind::Abs => add_segment("")?,
- PathKind::DollarCrate(_) => add_segment("$crate")?,
- }
- for segment in &self.segments {
- if !first_segment {
- f.write_str("::")?;
- }
- first_segment = false;
- if escaped {
- segment.fmt(f)?
- } else {
- segment.unescaped().fmt(f)?
- };
- }
- Ok(())
+ pub fn display<'a>(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
+ Display { db, path: self }
}
}
-impl Display for ModPath {
+struct Display<'a> {
+ db: &'a dyn ExpandDatabase,
+ path: &'a ModPath,
+}
+
+impl<'a> fmt::Display for Display<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self._fmt(f, true)
+ display_fmt_path(self.db, self.path, f, true)
}
}
-impl<'a> Display for UnescapedModPath<'a> {
+struct UnescapedDisplay<'a> {
+ db: &'a dyn ExpandDatabase,
+ path: &'a UnescapedModPath<'a>,
+}
+
+impl<'a> fmt::Display for UnescapedDisplay<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0._fmt(f, false)
+ display_fmt_path(self.db, self.path.0, f, false)
}
}
@@ -164,6 +148,46 @@ impl From<Name> for ModPath {
ModPath::from_segments(PathKind::Plain, iter::once(name))
}
}
+fn display_fmt_path(
+ db: &dyn ExpandDatabase,
+ path: &ModPath,
+ f: &mut fmt::Formatter<'_>,
+ escaped: bool,
+) -> fmt::Result {
+ let mut first_segment = true;
+ let mut add_segment = |s| -> fmt::Result {
+ if !first_segment {
+ f.write_str("::")?;
+ }
+ first_segment = false;
+ f.write_str(s)?;
+ Ok(())
+ };
+ match path.kind {
+ PathKind::Plain => {}
+ PathKind::Super(0) => add_segment("self")?,
+ PathKind::Super(n) => {
+ for _ in 0..n {
+ add_segment("super")?;
+ }
+ }
+ PathKind::Crate => add_segment("crate")?,
+ PathKind::Abs => add_segment("")?,
+ PathKind::DollarCrate(_) => add_segment("$crate")?,
+ }
+ for segment in &path.segments {
+ if !first_segment {
+ f.write_str("::")?;
+ }
+ first_segment = false;
+ if escaped {
+ segment.display(db).fmt(f)?;
+ } else {
+ segment.unescaped().display(db).fmt(f)?;
+ }
+ }
+ Ok(())
+}
fn convert_path(
db: &dyn ExpandDatabase,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index c3462beac..f8dbb8427 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -24,27 +24,6 @@ enum Repr {
TupleField(usize),
}
-impl fmt::Display for Name {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match &self.0 {
- Repr::Text(text) => fmt::Display::fmt(&text, f),
- Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
- }
- }
-}
-
-impl<'a> fmt::Display for UnescapedName<'a> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match &self.0 .0 {
- Repr::Text(text) => {
- let text = text.strip_prefix("r#").unwrap_or(text);
- fmt::Display::fmt(&text, f)
- }
- Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
- }
- }
-}
-
impl<'a> UnescapedName<'a> {
/// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over
/// [`ToString::to_string`] if possible as this conversion is cheaper in the general case.
@@ -60,6 +39,11 @@ impl<'a> UnescapedName<'a> {
Repr::TupleField(it) => SmolStr::new(it.to_string()),
}
}
+
+ pub fn display(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
+ _ = db;
+ UnescapedDisplay { name: self }
+ }
}
impl Name {
@@ -78,7 +62,7 @@ impl Name {
Self::new_text(lt.text().into())
}
- /// Shortcut to create inline plain text name
+ /// Shortcut to create inline plain text name. Panics if `text.len() > 22`
const fn new_inline(text: &str) -> Name {
Name::new_text(SmolStr::new_inline(text))
}
@@ -112,6 +96,17 @@ impl Name {
Name::new_inline("[missing name]")
}
+ /// Generates a new name which is only equal to itself, by incrementing a counter. Due
+ /// its implementation, it should not be used in things that salsa considers, like
+ /// type names or field names, and it should be only used in names of local variables
+ /// and labels and similar things.
+ pub fn generate_new_name() -> Name {
+ use std::sync::atomic::{AtomicUsize, Ordering};
+ static CNT: AtomicUsize = AtomicUsize::new(0);
+ let c = CNT.fetch_add(1, Ordering::Relaxed);
+ Name::new_text(format!("<ra@gennew>{c}").into())
+ }
+
/// Returns the tuple index this name represents if it is a tuple field.
pub fn as_tuple_index(&self) -> Option<usize> {
match self.0 {
@@ -156,6 +151,40 @@ impl Name {
Repr::TupleField(_) => false,
}
}
+
+ pub fn display<'a>(&'a self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
+ _ = db;
+ Display { name: self }
+ }
+}
+
+struct Display<'a> {
+ name: &'a Name,
+}
+
+impl<'a> fmt::Display for Display<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match &self.name.0 {
+ Repr::Text(text) => fmt::Display::fmt(&text, f),
+ Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
+ }
+ }
+}
+
+struct UnescapedDisplay<'a> {
+ name: &'a UnescapedName<'a>,
+}
+
+impl<'a> fmt::Display for UnescapedDisplay<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match &self.name.0 .0 {
+ Repr::Text(text) => {
+ let text = text.strip_prefix("r#").unwrap_or(text);
+ fmt::Display::fmt(&text, f)
+ }
+ Repr::TupleField(idx) => fmt::Display::fmt(&idx, f),
+ }
+ }
}
pub trait AsName {
@@ -337,18 +366,24 @@ pub mod known {
crate_type,
derive,
global_allocator,
+ no_core,
+ no_std,
test,
test_case,
recursion_limit,
feature,
// known methods of lang items
call_once,
+ call_mut,
+ call,
eq,
ne,
ge,
gt,
le,
lt,
+ // known fields of lang items
+ pieces,
// lang items
add_assign,
add,
@@ -363,6 +398,7 @@ pub mod known {
deref,
div_assign,
div,
+ drop,
fn_mut,
fn_once,
future_trait,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
index d758e9302..41675c630 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
@@ -7,20 +7,23 @@ use crate::{db::ExpandDatabase, tt, ExpandError, ExpandResult};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct ProcMacroExpander {
- proc_macro_id: Option<ProcMacroId>,
+ proc_macro_id: ProcMacroId,
}
+const DUMMY_ID: u32 = !0;
+
impl ProcMacroExpander {
pub fn new(proc_macro_id: ProcMacroId) -> Self {
- Self { proc_macro_id: Some(proc_macro_id) }
+ assert_ne!(proc_macro_id.0, DUMMY_ID);
+ Self { proc_macro_id }
}
pub fn dummy() -> Self {
- Self { proc_macro_id: None }
+ Self { proc_macro_id: ProcMacroId(DUMMY_ID) }
}
pub fn is_dummy(&self) -> bool {
- self.proc_macro_id.is_none()
+ self.proc_macro_id.0 == DUMMY_ID
}
pub fn expand(
@@ -32,33 +35,37 @@ impl ProcMacroExpander {
attr_arg: Option<&tt::Subtree>,
) -> ExpandResult<tt::Subtree> {
match self.proc_macro_id {
- Some(id) => {
- let krate_graph = db.crate_graph();
- let proc_macros = match &krate_graph[def_crate].proc_macro {
- Ok(proc_macros) => proc_macros,
- Err(_) => {
+ ProcMacroId(DUMMY_ID) => {
+ ExpandResult::new(tt::Subtree::empty(), ExpandError::UnresolvedProcMacro(def_crate))
+ }
+ ProcMacroId(id) => {
+ let proc_macros = db.proc_macros();
+ let proc_macros = match proc_macros.get(&def_crate) {
+ Some(Ok(proc_macros)) => proc_macros,
+ Some(Err(_)) | None => {
never!("Non-dummy expander even though there are no proc macros");
- return ExpandResult::with_err(
+ return ExpandResult::new(
tt::Subtree::empty(),
- ExpandError::Other("Internal error".into()),
+ ExpandError::other("Internal error"),
);
}
};
- let proc_macro = match proc_macros.get(id.0 as usize) {
+ let proc_macro = match proc_macros.get(id as usize) {
Some(proc_macro) => proc_macro,
None => {
never!(
"Proc macro index out of bounds: the length is {} but the index is {}",
proc_macros.len(),
- id.0
+ id
);
- return ExpandResult::with_err(
+ return ExpandResult::new(
tt::Subtree::empty(),
- ExpandError::Other("Internal error".into()),
+ ExpandError::other("Internal error"),
);
}
};
+ let krate_graph = db.crate_graph();
// Proc macros have access to the environment variables of the invoking crate.
let env = &krate_graph[calling_crate].env;
match proc_macro.expander.expand(tt, attr_arg, env) {
@@ -68,23 +75,15 @@ impl ProcMacroExpander {
ProcMacroExpansionError::System(text)
if proc_macro.kind == ProcMacroKind::Attr =>
{
- ExpandResult {
- value: tt.clone(),
- err: Some(ExpandError::Other(text.into())),
- }
+ ExpandResult { value: tt.clone(), err: Some(ExpandError::other(text)) }
}
ProcMacroExpansionError::System(text)
- | ProcMacroExpansionError::Panic(text) => ExpandResult::with_err(
- tt::Subtree::empty(),
- ExpandError::Other(text.into()),
- ),
+ | ProcMacroExpansionError::Panic(text) => {
+ ExpandResult::new(tt::Subtree::empty(), ExpandError::other(text))
+ }
},
}
}
- None => ExpandResult::with_err(
- tt::Subtree::empty(),
- ExpandError::UnresolvedProcMacro(def_crate),
- ),
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs
index 63586f9da..ab3809abc 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/quote.rs
@@ -162,6 +162,12 @@ impl ToTokenTree for crate::tt::TokenTree {
}
}
+impl ToTokenTree for &crate::tt::TokenTree {
+ fn to_token(self) -> crate::tt::TokenTree {
+ self.clone()
+ }
+}
+
impl ToTokenTree for crate::tt::Subtree {
fn to_token(self) -> crate::tt::TokenTree {
self.into()
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index 9b3296df2..c8bea3450 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -15,19 +15,21 @@ doctest = false
cov-mark = "2.0.0-pre.1"
itertools = "0.10.5"
arrayvec = "0.7.2"
-bitflags = "1.3.2"
+bitflags = "2.1.0"
smallvec.workspace = true
ena = "0.14.0"
either = "1.7.0"
tracing = "0.1.35"
rustc-hash = "1.1.0"
scoped-tls = "1.0.0"
-chalk-solve = { version = "0.89.0", default-features = false }
-chalk-ir = "0.89.0"
-chalk-recursive = { version = "0.89.0", default-features = false }
-chalk-derive = "0.89.0"
+chalk-solve = { version = "0.91.0", default-features = false }
+chalk-ir = "0.91.0"
+chalk-recursive = { version = "0.91.0", default-features = false }
+chalk-derive = "0.91.0"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
once_cell = "1.17.0"
+triomphe.workspace = true
+nohash-hasher.workspace = true
typed-arena = "2.0.1"
rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
index 58744dd0c..3860bccec 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
@@ -3,12 +3,11 @@
//! reference to a type with the field `bar`. This is an approximation of the
//! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs).
-use std::sync::Arc;
-
use chalk_ir::cast::Cast;
use hir_def::lang_item::LangItem;
use hir_expand::name::name;
use limit::Limit;
+use triomphe::Arc;
use crate::{
db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt,
@@ -23,6 +22,41 @@ pub(crate) enum AutoderefKind {
Overloaded,
}
+/// Returns types that `ty` transitively dereferences to. This function is only meant to be used
+/// outside `hir-ty`.
+///
+/// It is guaranteed that:
+/// - the yielded types don't contain inference variables (but may contain `TyKind::Error`).
+/// - a type won't be yielded more than once; in other words, the returned iterator will stop if it
+/// detects a cycle in the deref chain.
+pub fn autoderef(
+ db: &dyn HirDatabase,
+ env: Arc<TraitEnvironment>,
+ ty: Canonical<Ty>,
+) -> impl Iterator<Item = Ty> {
+ let mut table = InferenceTable::new(db, env);
+ let ty = table.instantiate_canonical(ty);
+ let mut autoderef = Autoderef::new(&mut table, ty);
+ let mut v = Vec::new();
+ while let Some((ty, _steps)) = autoderef.next() {
+ // `ty` may contain unresolved inference variables. Since there's no chance they would be
+ // resolved, just replace with fallback type.
+ let resolved = autoderef.table.resolve_completely(ty);
+
+ // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we
+ // would revisit some already visited types. Stop here to avoid duplication.
+ //
+ // XXX: The recursion limit for `Autoderef` is currently 10, so `Vec::contains()` shouldn't
+ // be too expensive. Replace this duplicate check with `FxHashSet` if it proves to be more
+ // performant.
+ if v.contains(&resolved) {
+ break;
+ }
+ v.push(resolved);
+ }
+ v.into_iter()
+}
+
#[derive(Debug)]
pub(crate) struct Autoderef<'a, 'db> {
pub(crate) table: &'a mut InferenceTable<'db>,
@@ -76,49 +110,43 @@ pub(crate) fn autoderef_step(
table: &mut InferenceTable<'_>,
ty: Ty,
) -> Option<(AutoderefKind, Ty)> {
- if let Some(derefed) = builtin_deref(&ty) {
+ if let Some(derefed) = builtin_deref(table, &ty, false) {
Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
} else {
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
}
}
-// FIXME: replace uses of this with Autoderef above
-pub fn autoderef(
- db: &dyn HirDatabase,
- env: Arc<TraitEnvironment>,
- ty: Canonical<Ty>,
-) -> impl Iterator<Item = Canonical<Ty>> + '_ {
- let mut table = InferenceTable::new(db, env);
- let ty = table.instantiate_canonical(ty);
- let mut autoderef = Autoderef::new(&mut table, ty);
- let mut v = Vec::new();
- while let Some((ty, _steps)) = autoderef.next() {
- v.push(autoderef.table.canonicalize(ty).value);
- }
- v.into_iter()
-}
-
-pub(crate) fn deref(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
- let _p = profile::span("deref");
- autoderef_step(table, ty).map(|(_, ty)| ty)
-}
-
-fn builtin_deref(ty: &Ty) -> Option<&Ty> {
+pub(crate) fn builtin_deref<'ty>(
+ table: &mut InferenceTable<'_>,
+ ty: &'ty Ty,
+ explicit: bool,
+) -> Option<&'ty Ty> {
match ty.kind(Interner) {
- TyKind::Ref(.., ty) | TyKind::Raw(.., ty) => Some(ty),
+ TyKind::Ref(.., ty) => Some(ty),
+ // FIXME: Maybe accept this but diagnose if its not explicit?
+ TyKind::Raw(.., ty) if explicit => Some(ty),
+ &TyKind::Adt(chalk_ir::AdtId(adt), ref substs) => {
+ if crate::lang_items::is_box(table.db, adt) {
+ substs.at(Interner, 0).ty(Interner)
+ } else {
+ None
+ }
+ }
_ => None,
}
}
-fn deref_by_trait(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
+pub(crate) fn deref_by_trait(
+ table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>,
+ ty: Ty,
+) -> Option<Ty> {
let _p = profile::span("deref_by_trait");
if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() {
// don't try to deref unknown variables
return None;
}
- let db = table.db;
let deref_trait =
db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?;
let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
index 03e944359..eec57ba3f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
@@ -18,7 +18,6 @@ use crate::{
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
- ValueTyDefId,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -195,6 +194,19 @@ impl TyBuilder<()> {
params.placeholder_subst(db)
}
+ pub fn unknown_subst(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> Substitution {
+ let params = generics(db.upcast(), def.into());
+ Substitution::from_iter(
+ Interner,
+ params.iter_id().map(|id| match id {
+ either::Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner),
+ either::Either::Right(id) => {
+ unknown_const_as_generic(db.const_param_ty(id)).cast(Interner)
+ }
+ }),
+ )
+ }
+
pub fn subst_for_def(
db: &dyn HirDatabase,
def: impl Into<GenericDefId>,
@@ -233,6 +245,25 @@ impl TyBuilder<()> {
TyBuilder::new((), params, parent_subst)
}
+ pub fn subst_for_closure(
+ db: &dyn HirDatabase,
+ parent: DefWithBodyId,
+ sig_ty: Ty,
+ ) -> Substitution {
+ let sig_ty = sig_ty.cast(Interner);
+ let self_subst = iter::once(&sig_ty);
+ let Some(parent) = parent.as_generic_def_id() else {
+ return Substitution::from_iter(Interner, self_subst);
+ };
+ Substitution::from_iter(
+ Interner,
+ self_subst
+ .chain(generics(db.upcast(), parent).placeholder_subst(db).iter(Interner))
+ .cloned()
+ .collect::<Vec<_>>(),
+ )
+ }
+
pub fn build(self) -> Substitution {
let ((), subst) = self.build_internal();
subst
@@ -362,21 +393,4 @@ impl TyBuilder<Binders<Ty>> {
pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> {
TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def))
}
-
- pub fn value_ty(
- db: &dyn HirDatabase,
- def: ValueTyDefId,
- parent_subst: Option<Substitution>,
- ) -> TyBuilder<Binders<Ty>> {
- let poly_value_ty = db.value_ty(def);
- let id = match def.to_generic_def_id() {
- Some(id) => id,
- None => {
- // static items
- assert!(parent_subst.is_none());
- return TyBuilder::new_empty(poly_value_ty);
- }
- };
- TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_value_ty)
- }
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
index 28ae4c349..5dd8e2719 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
@@ -1,8 +1,8 @@
//! The implementation of `RustIrDatabase` for Chalk, which provides information
//! about the code that Chalk needs.
-use std::sync::Arc;
+use core::ops;
+use std::{iter, sync::Arc};
-use cov_mark::hit;
use tracing::debug;
use chalk_ir::{cast::Cast, fold::shift::Shift, CanonicalVarKinds};
@@ -10,9 +10,9 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
use base_db::CrateId;
use hir_def::{
- expr::Movability,
+ hir::Movability,
lang_item::{lang_attr, LangItem, LangItemTarget},
- AssocItemId, GenericDefId, HasModule, ItemContainerId, Lookup, ModuleId, TypeAliasId,
+ AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId,
};
use hir_expand::name::name;
@@ -25,7 +25,7 @@ use crate::{
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
to_assoc_type_id, to_chalk_trait_id,
traits::ChalkContext,
- utils::generics,
+ utils::{generics, ClosureSubst},
wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId,
Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef,
TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause,
@@ -108,17 +108,6 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
_ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
};
- fn local_impls(db: &dyn HirDatabase, module: ModuleId) -> Option<Arc<TraitImpls>> {
- let block = module.containing_block()?;
- hit!(block_local_impls);
- db.trait_impls_in_block(block)
- }
-
- // Note: Since we're using impls_for_trait, only impls where the trait
- // can be resolved should ever reach Chalk. impl_datum relies on that
- // and will panic if the trait can't be resolved.
- let in_deps = self.db.trait_impls_in_deps(self.krate);
- let in_self = self.db.trait_impls_in_crate(self.krate);
let trait_module = trait_.module(self.db.upcast());
let type_module = match self_ty_fp {
Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())),
@@ -128,33 +117,62 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())),
_ => None,
};
- let impl_maps = [
- Some(in_deps),
- Some(in_self),
- local_impls(self.db, trait_module),
- type_module.and_then(|m| local_impls(self.db, m)),
- ];
- let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
+ let mut def_blocks =
+ [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())];
- let result: Vec<_> = if fps.is_empty() {
- debug!("Unrestricted search for {:?} impls...", trait_);
- impl_maps
- .iter()
- .filter_map(|o| o.as_ref())
- .flat_map(|impls| impls.for_trait(trait_).map(id_to_chalk))
- .collect()
- } else {
- impl_maps
- .iter()
- .filter_map(|o| o.as_ref())
- .flat_map(|impls| {
- fps.iter().flat_map(move |fp| {
- impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
- })
- })
- .collect()
- };
+ // Note: Since we're using impls_for_trait, only impls where the trait
+ // can be resolved should ever reach Chalk. impl_datum relies on that
+ // and will panic if the trait can't be resolved.
+ let in_deps = self.db.trait_impls_in_deps(self.krate);
+ let in_self = self.db.trait_impls_in_crate(self.krate);
+
+ let block_impls = iter::successors(self.block, |&block_id| {
+ cov_mark::hit!(block_local_impls);
+ self.db.block_def_map(block_id).parent().and_then(|module| module.containing_block())
+ })
+ .inspect(|&block_id| {
+ // make sure we don't search the same block twice
+ def_blocks.iter_mut().for_each(|block| {
+ if *block == Some(block_id) {
+ *block = None;
+ }
+ });
+ })
+ .map(|block_id| self.db.trait_impls_in_block(block_id));
+
+ let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
+ let mut result = vec![];
+ match fps {
+ [] => {
+ debug!("Unrestricted search for {:?} impls...", trait_);
+ let mut f = |impls: &TraitImpls| {
+ result.extend(impls.for_trait(trait_).map(id_to_chalk));
+ };
+ f(&in_self);
+ in_deps.iter().map(ops::Deref::deref).for_each(&mut f);
+ block_impls.for_each(|it| f(&it));
+ def_blocks
+ .into_iter()
+ .flatten()
+ .for_each(|it| f(&self.db.trait_impls_in_block(it)));
+ }
+ fps => {
+ let mut f =
+ |impls: &TraitImpls| {
+ result.extend(fps.iter().flat_map(|fp| {
+ impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
+ }));
+ };
+ f(&in_self);
+ in_deps.iter().map(ops::Deref::deref).for_each(&mut f);
+ block_impls.for_each(|it| f(&it));
+ def_blocks
+ .into_iter()
+ .flatten()
+ .for_each(|it| f(&self.db.trait_impls_in_block(it)));
+ }
+ }
debug!("impls_for_trait returned {} impls", result.len());
result
@@ -193,7 +211,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
&self,
environment: &chalk_ir::Environment<Interner>,
) -> chalk_ir::ProgramClauses<Interner> {
- self.db.program_clauses_for_chalk_env(self.krate, environment.clone())
+ self.db.program_clauses_for_chalk_env(self.krate, self.block, environment.clone())
}
fn opaque_ty_data(&self, id: chalk_ir::OpaqueTyId<Interner>) -> Arc<OpaqueTyDatum> {
@@ -321,7 +339,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
_closure_id: chalk_ir::ClosureId<Interner>,
substs: &chalk_ir::Substitution<Interner>,
) -> chalk_ir::Binders<rust_ir::FnDefInputsAndOutputDatum<Interner>> {
- let sig_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
+ let sig_ty = ClosureSubst(substs).sig_ty();
let sig = &sig_ty.callable_sig(self.db).expect("first closure param should be fn ptr");
let io = rust_ir::FnDefInputsAndOutputDatum {
argument_types: sig.params().to_vec(),
@@ -347,13 +365,19 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
fn trait_name(&self, trait_id: chalk_ir::TraitId<Interner>) -> String {
let id = from_chalk_trait_id(trait_id);
- self.db.trait_data(id).name.to_string()
+ self.db.trait_data(id).name.display(self.db.upcast()).to_string()
}
fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String {
match adt_id {
- hir_def::AdtId::StructId(id) => self.db.struct_data(id).name.to_string(),
- hir_def::AdtId::EnumId(id) => self.db.enum_data(id).name.to_string(),
- hir_def::AdtId::UnionId(id) => self.db.union_data(id).name.to_string(),
+ hir_def::AdtId::StructId(id) => {
+ self.db.struct_data(id).name.display(self.db.upcast()).to_string()
+ }
+ hir_def::AdtId::EnumId(id) => {
+ self.db.enum_data(id).name.display(self.db.upcast()).to_string()
+ }
+ hir_def::AdtId::UnionId(id) => {
+ self.db.union_data(id).name.display(self.db.upcast()).to_string()
+ }
}
}
fn adt_size_align(&self, _id: chalk_ir::AdtId<Interner>) -> Arc<rust_ir::AdtSizeAlign> {
@@ -362,7 +386,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
}
fn assoc_type_name(&self, assoc_ty_id: chalk_ir::AssocTypeId<Interner>) -> String {
let id = self.db.associated_ty_data(assoc_ty_id).name;
- self.db.type_alias_data(id).name.to_string()
+ self.db.type_alias_data(id).name.display(self.db.upcast()).to_string()
}
fn opaque_type_name(&self, opaque_ty_id: chalk_ir::OpaqueTyId<Interner>) -> String {
format!("Opaque_{}", opaque_ty_id.0)
@@ -373,7 +397,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
fn generator_datum(
&self,
id: chalk_ir::GeneratorId<Interner>,
- ) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorDatum<Interner>> {
+ ) -> Arc<chalk_solve::rust_ir::GeneratorDatum<Interner>> {
let (parent, expr) = self.db.lookup_intern_generator(id.into());
// We fill substitution with unknown type, because we only need to know whether the generic
@@ -398,8 +422,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
let input_output = crate::make_type_and_const_binders(it, input_output);
let movability = match self.db.body(parent)[expr] {
- hir_def::expr::Expr::Closure {
- closure_kind: hir_def::expr::ClosureKind::Generator(movability),
+ hir_def::hir::Expr::Closure {
+ closure_kind: hir_def::hir::ClosureKind::Generator(movability),
..
} => movability,
_ => unreachable!("non generator expression interned as generator"),
@@ -414,7 +438,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
fn generator_witness_datum(
&self,
id: chalk_ir::GeneratorId<Interner>,
- ) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<Interner>> {
+ ) -> Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<Interner>> {
// FIXME: calculate inner types
let inner_types =
rust_ir::GeneratorWitnessExistential { types: wrap_empty_binders(vec![]) };
@@ -435,7 +459,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
}
}
-impl<'a> chalk_ir::UnificationDatabase<Interner> for &'a dyn HirDatabase {
+impl chalk_ir::UnificationDatabase<Interner> for &dyn HirDatabase {
fn fn_def_variance(
&self,
fn_def_id: chalk_ir::FnDefId<Interner>,
@@ -451,9 +475,10 @@ impl<'a> chalk_ir::UnificationDatabase<Interner> for &'a dyn HirDatabase {
pub(crate) fn program_clauses_for_chalk_env_query(
db: &dyn HirDatabase,
krate: CrateId,
+ block: Option<BlockId>,
environment: chalk_ir::Environment<Interner>,
) -> chalk_ir::ProgramClauses<Interner> {
- chalk_solve::program_clauses_for_env(&ChalkContext { db, krate }, &environment)
+ chalk_solve::program_clauses_for_env(&ChalkContext { db, krate, block }, &environment)
}
pub(crate) fn associated_ty_data_query(
@@ -472,7 +497,7 @@ pub(crate) fn associated_ty_data_query(
let generic_params = generics(db.upcast(), type_alias.into());
// let bound_vars = generic_params.bound_vars_subst(DebruijnIndex::INNERMOST);
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
- let ctx = crate::TyLoweringContext::new(db, &resolver)
+ let ctx = crate::TyLoweringContext::new(db, &resolver, type_alias.into())
.with_type_param_mode(crate::lower::ParamLoweringMode::Variable);
let trait_subst = TyBuilder::subst_for_def(db, trait_, None)
@@ -567,6 +592,7 @@ fn well_known_trait_from_lang_item(item: LangItem) -> Option<WellKnownTrait> {
LangItem::Unpin => WellKnownTrait::Unpin,
LangItem::Unsize => WellKnownTrait::Unsize,
LangItem::Tuple => WellKnownTrait::Tuple,
+ LangItem::PointeeTrait => WellKnownTrait::Pointee,
_ => return None,
})
}
@@ -587,6 +613,7 @@ fn lang_item_from_well_known_trait(trait_: WellKnownTrait) -> LangItem {
WellKnownTrait::Tuple => LangItem::Tuple,
WellKnownTrait::Unpin => LangItem::Unpin,
WellKnownTrait::Unsize => LangItem::Unsize,
+ WellKnownTrait::Pointee => LangItem::PointeeTrait,
}
}
@@ -786,17 +813,17 @@ pub(crate) fn adt_variance_query(
)
}
+/// Returns instantiated predicates.
pub(super) fn convert_where_clauses(
db: &dyn HirDatabase,
def: GenericDefId,
substs: &Substitution,
) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
- let generic_predicates = db.generic_predicates(def);
- let mut result = Vec::with_capacity(generic_predicates.len());
- for pred in generic_predicates.iter() {
- result.push(pred.clone().substitute(Interner, substs));
- }
- result
+ db.generic_predicates(def)
+ .iter()
+ .cloned()
+ .map(|pred| pred.substitute(Interner, substs))
+ .collect()
}
pub(super) fn generic_predicate_to_inline_bound(
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
index 214189492..a8071591a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
@@ -1,24 +1,28 @@
//! Various extensions traits for Chalk types.
-use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy};
+use chalk_ir::{cast::Cast, FloatTy, IntTy, Mutability, Scalar, TyVariableKind, UintTy};
use hir_def::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
generics::TypeOrConstParamData,
lang_item::LangItem,
type_ref::Rawness,
- FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
+ DefWithBodyId, FunctionId, GenericDefId, HasModule, ItemContainerId, Lookup, TraitId,
};
use crate::{
- db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
- from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders,
- CallableDefId, CallableSig, DynTy, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy,
+ db::HirDatabase,
+ from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
+ to_chalk_trait_id,
+ utils::{generics, ClosureSubst},
+ AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Canonical, CanonicalVarKinds,
+ ClosureId, DynTy, FnPointer, ImplTraitId, InEnvironment, Interner, Lifetime, ProjectionTy,
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, TypeFlags, WhereClause,
};
pub trait TyExt {
fn is_unit(&self) -> bool;
fn is_integral(&self) -> bool;
+ fn is_scalar(&self) -> bool;
fn is_floating_point(&self) -> bool;
fn is_never(&self) -> bool;
fn is_unknown(&self) -> bool;
@@ -28,8 +32,10 @@ pub trait TyExt {
fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
fn as_builtin(&self) -> Option<BuiltinType>;
fn as_tuple(&self) -> Option<&Substitution>;
+ fn as_closure(&self) -> Option<ClosureId>;
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
+ fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)>;
fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>;
fn as_generic_def(&self, db: &dyn HirDatabase) -> Option<GenericDefId>;
@@ -44,6 +50,7 @@ pub trait TyExt {
fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option<Vec<QuantifiedWhereClause>>;
fn associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<TraitId>;
+ fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool;
/// FIXME: Get rid of this, it's not a good abstraction
fn equals_ctor(&self, other: &Ty) -> bool;
@@ -62,6 +69,10 @@ impl TyExt for Ty {
)
}
+ fn is_scalar(&self) -> bool {
+ matches!(self.kind(Interner), TyKind::Scalar(_))
+ }
+
fn is_floating_point(&self) -> bool {
matches!(
self.kind(Interner),
@@ -128,12 +139,20 @@ impl TyExt for Ty {
}
}
+ fn as_closure(&self) -> Option<ClosureId> {
+ match self.kind(Interner) {
+ TyKind::Closure(id, _) => Some(*id),
+ _ => None,
+ }
+ }
+
fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId> {
match self.callable_def(db) {
Some(CallableDefId::FunctionId(func)) => Some(func),
Some(CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_)) | None => None,
}
}
+
fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> {
match self.kind(Interner) {
TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)),
@@ -141,6 +160,13 @@ impl TyExt for Ty {
}
}
+ fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)> {
+ match self.kind(Interner) {
+ TyKind::Raw(mutability, ty) => Some((ty, *mutability)),
+ _ => None,
+ }
+ }
+
fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> {
match self.kind(Interner) {
TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)),
@@ -176,10 +202,7 @@ impl TyExt for Ty {
let sig = db.callable_item_signature(callable_def);
Some(sig.substitute(Interner, parameters))
}
- TyKind::Closure(.., substs) => {
- let sig_param = substs.at(Interner, 0).assert_ty_ref(Interner);
- sig_param.callable_sig(db)
- }
+ TyKind::Closure(.., substs) => ClosureSubst(substs).sig_ty().callable_sig(db),
_ => None,
}
}
@@ -318,6 +341,20 @@ impl TyExt for Ty {
}
}
+ fn is_copy(self, db: &dyn HirDatabase, owner: DefWithBodyId) -> bool {
+ let crate_id = owner.module(db.upcast()).krate();
+ let Some(copy_trait) = db.lang_item(crate_id, LangItem::Copy).and_then(|x| x.as_trait()) else {
+ return false;
+ };
+ let trait_ref = TyBuilder::trait_ref(db, copy_trait).push(self).build();
+ let env = db.trait_environment_for_body(owner);
+ let goal = Canonical {
+ value: InEnvironment::new(&env.env, trait_ref.cast(Interner)),
+ binders: CanonicalVarKinds::empty(Interner),
+ };
+ db.trait_solve(crate_id, None, goal).is_some()
+ }
+
fn equals_ctor(&self, other: &Ty) -> bool {
match (self.kind(Interner), other.kind(Interner)) {
(TyKind::Adt(adt, ..), TyKind::Adt(adt2, ..)) => adt == adt2,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
index 5830c4898..262341c6e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -3,19 +3,20 @@
use base_db::CrateId;
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
use hir_def::{
- expr::Expr,
- path::ModPath,
+ hir::Expr,
+ path::Path,
resolver::{Resolver, ValueNs},
- type_ref::ConstRef,
- ConstId, EnumVariantId,
+ type_ref::LiteralConstRef,
+ ConstBlockLoc, EnumVariantId, GeneralConstId, StaticId,
};
use la_arena::{Idx, RawIdx};
use stdx::never;
+use triomphe::Arc;
use crate::{
- db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode,
- to_placeholder_idx, utils::Generics, Const, ConstData, ConstScalar, ConstValue, GenericArg,
- Interner, MemoryMap, Ty, TyBuilder,
+ db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode,
+ mir::monomorphize_mir_body_bad, to_placeholder_idx, utils::Generics, Const, ConstData,
+ ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution, Ty, TyBuilder,
};
use super::mir::{interpret_mir, lower_to_mir, pad16, MirEvalError, MirLowerError};
@@ -57,7 +58,7 @@ pub enum ConstEvalError {
impl From<MirLowerError> for ConstEvalError {
fn from(value: MirLowerError) -> Self {
match value {
- MirLowerError::ConstEvalError(e) => *e,
+ MirLowerError::ConstEvalError(_, e) => *e,
_ => ConstEvalError::MirLowerError(value),
}
}
@@ -72,10 +73,11 @@ impl From<MirEvalError> for ConstEvalError {
pub(crate) fn path_to_const(
db: &dyn HirDatabase,
resolver: &Resolver,
- path: &ModPath,
+ path: &Path,
mode: ParamLoweringMode,
args_lazy: impl FnOnce() -> Generics,
debruijn: DebruijnIndex,
+ expected_ty: Ty,
) -> Option<Const> {
match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) {
Some(ValueNs::GenericParam(p)) => {
@@ -89,7 +91,7 @@ pub(crate) fn path_to_const(
Some(x) => ConstValue::BoundVar(BoundVar::new(debruijn, x)),
None => {
never!(
- "Generic list doesn't contain this param: {:?}, {}, {:?}",
+ "Generic list doesn't contain this param: {:?}, {:?}, {:?}",
args,
path,
p
@@ -100,6 +102,10 @@ pub(crate) fn path_to_const(
};
Some(ConstData { ty, value }.intern(Interner))
}
+ Some(ValueNs::ConstId(c)) => Some(intern_const_scalar(
+ ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)),
+ expected_ty,
+ )),
_ => None,
}
}
@@ -123,22 +129,28 @@ pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const {
}
/// Interns a constant scalar with the given type
-pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: CrateId) -> Const {
+pub fn intern_const_ref(
+ db: &dyn HirDatabase,
+ value: &LiteralConstRef,
+ ty: Ty,
+ krate: CrateId,
+) -> Const {
+ let layout = db.layout_of_ty(ty.clone(), krate);
let bytes = match value {
- ConstRef::Int(i) => {
+ LiteralConstRef::Int(i) => {
// FIXME: We should handle failure of layout better.
- let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
+ let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
}
- ConstRef::UInt(i) => {
- let size = layout_of_ty(db, &ty, krate).map(|x| x.size.bytes_usize()).unwrap_or(16);
+ LiteralConstRef::UInt(i) => {
+ let size = layout.map(|x| x.size.bytes_usize()).unwrap_or(16);
ConstScalar::Bytes(i.to_le_bytes()[0..size].to_vec(), MemoryMap::default())
}
- ConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
- ConstRef::Char(c) => {
+ LiteralConstRef::Bool(b) => ConstScalar::Bytes(vec![*b as u8], MemoryMap::default()),
+ LiteralConstRef::Char(c) => {
ConstScalar::Bytes((*c as u32).to_le_bytes().to_vec(), MemoryMap::default())
}
- ConstRef::Unknown => ConstScalar::Unknown,
+ LiteralConstRef::Unknown => ConstScalar::Unknown,
};
intern_const_scalar(bytes, ty)
}
@@ -147,19 +159,23 @@ pub fn intern_const_ref(db: &dyn HirDatabase, value: &ConstRef, ty: Ty, krate: C
pub fn usize_const(db: &dyn HirDatabase, value: Option<u128>, krate: CrateId) -> Const {
intern_const_ref(
db,
- &value.map_or(ConstRef::Unknown, ConstRef::UInt),
+ &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt),
TyBuilder::usize(),
krate,
)
}
-pub fn try_const_usize(c: &Const) -> Option<u128> {
+pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option<u128> {
match &c.data(Interner).value {
chalk_ir::ConstValue::BoundVar(_) => None,
chalk_ir::ConstValue::InferenceVar(_) => None,
chalk_ir::ConstValue::Placeholder(_) => None,
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(x, _) => Some(u128::from_le_bytes(pad16(&x, false))),
+ ConstScalar::UnevaluatedConst(c, subst) => {
+ let ec = db.const_eval(*c, subst.clone()).ok()?;
+ try_const_usize(db, &ec)
+ }
_ => None,
},
}
@@ -168,7 +184,16 @@ pub fn try_const_usize(c: &Const) -> Option<u128> {
pub(crate) fn const_eval_recover(
_: &dyn HirDatabase,
_: &[String],
- _: &ConstId,
+ _: &GeneralConstId,
+ _: &Substitution,
+) -> Result<Const, ConstEvalError> {
+ Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
+}
+
+pub(crate) fn const_eval_static_recover(
+ _: &dyn HirDatabase,
+ _: &[String],
+ _: &StaticId,
) -> Result<Const, ConstEvalError> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
@@ -183,11 +208,40 @@ pub(crate) fn const_eval_discriminant_recover(
pub(crate) fn const_eval_query(
db: &dyn HirDatabase,
- const_id: ConstId,
+ def: GeneralConstId,
+ subst: Substitution,
) -> Result<Const, ConstEvalError> {
- let def = const_id.into();
- let body = db.mir_body(def)?;
- let c = interpret_mir(db, &body, false)?;
+ let body = match def {
+ GeneralConstId::ConstId(c) => {
+ db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))?
+ }
+ GeneralConstId::ConstBlockId(c) => {
+ let ConstBlockLoc { parent, root } = db.lookup_intern_anonymous_const(c);
+ let body = db.body(parent);
+ let infer = db.infer(parent);
+ Arc::new(monomorphize_mir_body_bad(
+ db,
+ lower_to_mir(db, parent, &body, &infer, root)?,
+ subst,
+ db.trait_environment_for_body(parent),
+ )?)
+ }
+ GeneralConstId::InTypeConstId(c) => db.mir_body(c.into())?,
+ };
+ let c = interpret_mir(db, &body, false).0?;
+ Ok(c)
+}
+
+pub(crate) fn const_eval_static_query(
+ db: &dyn HirDatabase,
+ def: StaticId,
+) -> Result<Const, ConstEvalError> {
+ let body = db.monomorphized_mir_body(
+ def.into(),
+ Substitution::empty(Interner),
+ db.trait_environment_for_body(def.into()),
+ )?;
+ let c = interpret_mir(db, &body, false).0?;
Ok(c)
}
@@ -209,9 +263,13 @@ pub(crate) fn const_eval_discriminant_variant(
};
return Ok(value);
}
- let mir_body = db.mir_body(def)?;
- let c = interpret_mir(db, &mir_body, false)?;
- let c = try_const_usize(&c).unwrap() as i128;
+ let mir_body = db.monomorphized_mir_body(
+ def,
+ Substitution::empty(Interner),
+ db.trait_environment_for_body(def),
+ )?;
+ let c = interpret_mir(db, &mir_body, false).0?;
+ let c = try_const_usize(db, &c).unwrap() as i128;
Ok(c)
}
@@ -226,15 +284,16 @@ pub(crate) fn eval_to_const(
debruijn: DebruijnIndex,
) -> Const {
let db = ctx.db;
+ let infer = ctx.clone().resolve_all();
if let Expr::Path(p) = &ctx.body.exprs[expr] {
let resolver = &ctx.resolver;
- if let Some(c) = path_to_const(db, resolver, p.mod_path(), mode, args, debruijn) {
+ if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn, infer[expr].clone()) {
return c;
}
}
let infer = ctx.clone().resolve_all();
if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
- if let Ok(result) = interpret_mir(db, &mir_body, true) {
+ if let Ok(result) = interpret_mir(db, &mir_body, true).0 {
return result;
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
index 6a29e8ce5..0db1fefbf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
@@ -1,8 +1,10 @@
-use base_db::fixture::WithFixture;
+use base_db::{fixture::WithFixture, FileId};
+use chalk_ir::Substitution;
use hir_def::db::DefDatabase;
use crate::{
- consteval::try_const_usize, db::HirDatabase, test_db::TestDB, Const, ConstScalar, Interner,
+ consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar,
+ Interner,
};
use super::{
@@ -10,9 +12,11 @@ use super::{
ConstEvalError,
};
+mod intrinsics;
+
fn simplify(e: ConstEvalError) -> ConstEvalError {
match e {
- ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e)) => {
+ ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e, _, _)) => {
simplify(ConstEvalError::MirEvalError(*e))
}
_ => e,
@@ -20,17 +24,35 @@ fn simplify(e: ConstEvalError) -> ConstEvalError {
}
#[track_caller]
-fn check_fail(ra_fixture: &str, error: ConstEvalError) {
- assert_eq!(eval_goal(ra_fixture).map_err(simplify), Err(error));
+fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
+ let (db, file_id) = TestDB::with_single_file(ra_fixture);
+ match eval_goal(&db, file_id) {
+ Ok(_) => panic!("Expected fail, but it succeeded"),
+ Err(e) => {
+ assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, db))
+ }
+ }
}
#[track_caller]
fn check_number(ra_fixture: &str, answer: i128) {
- let r = eval_goal(ra_fixture).unwrap();
+ let (db, file_id) = TestDB::with_single_file(ra_fixture);
+ let r = match eval_goal(&db, file_id) {
+ Ok(t) => t,
+ Err(e) => {
+ let err = pretty_print_err(e, db);
+ panic!("Error in evaluating goal: {}", err);
+ }
+ };
match &r.data(Interner).value {
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(b, _) => {
- assert_eq!(b, &answer.to_le_bytes()[0..b.len()]);
+ assert_eq!(
+ b,
+ &answer.to_le_bytes()[0..b.len()],
+ "Bytes differ. In decimal form: actual = {}, expected = {answer}",
+ i128::from_le_bytes(pad16(b, true))
+ );
}
x => panic!("Expected number but found {:?}", x),
},
@@ -38,16 +60,26 @@ fn check_number(ra_fixture: &str, answer: i128) {
}
}
-fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
- let (db, file_id) = TestDB::with_single_file(ra_fixture);
+fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
+ let mut err = String::new();
+ let span_formatter = |file, range| format!("{:?} {:?}", file, range);
+ match e {
+ ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter),
+ ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter),
+ }
+ .unwrap();
+ err
+}
+
+fn eval_goal(db: &TestDB, file_id: FileId) -> Result<Const, ConstEvalError> {
let module_id = db.module_for_file(file_id);
- let def_map = module_id.def_map(&db);
+ let def_map = module_id.def_map(db);
let scope = &def_map[module_id.local_id].scope;
let const_id = scope
.declarations()
.find_map(|x| match x {
hir_def::ModuleDefId::ConstId(x) => {
- if db.const_data(x).name.as_ref()?.to_string() == "GOAL" {
+ if db.const_data(x).name.as_ref()?.display(db).to_string() == "GOAL" {
Some(x)
} else {
None
@@ -56,7 +88,7 @@ fn eval_goal(ra_fixture: &str) -> Result<Const, ConstEvalError> {
_ => None,
})
.unwrap();
- db.const_eval(const_id)
+ db.const_eval(const_id.into(), Substitution::empty(Interner))
}
#[test]
@@ -72,8 +104,98 @@ fn bit_op() {
check_number(r#"const GOAL: u8 = !0 & !(!0 >> 1)"#, 128);
check_number(r#"const GOAL: i8 = !0 & !(!0 >> 1)"#, 0);
check_number(r#"const GOAL: i8 = 1 << 7"#, (1i8 << 7) as i128);
- // FIXME: report panic here
- check_number(r#"const GOAL: i8 = 1 << 8"#, 0);
+ check_number(r#"const GOAL: i8 = -1 << 2"#, (-1i8 << 2) as i128);
+ check_fail(r#"const GOAL: i8 = 1 << 8"#, |e| {
+ e == ConstEvalError::MirEvalError(MirEvalError::Panic("Overflow in Shl".to_string()))
+ });
+}
+
+#[test]
+fn floating_point() {
+ check_number(
+ r#"const GOAL: f64 = 2.0 + 3.0 * 5.5 - 8.;"#,
+ i128::from_le_bytes(pad16(&f64::to_le_bytes(10.5), true)),
+ );
+ check_number(
+ r#"const GOAL: f32 = 2.0 + 3.0 * 5.5 - 8.;"#,
+ i128::from_le_bytes(pad16(&f32::to_le_bytes(10.5), true)),
+ );
+ check_number(
+ r#"const GOAL: f32 = -90.0 + 36.0;"#,
+ i128::from_le_bytes(pad16(&f32::to_le_bytes(-54.0), true)),
+ );
+}
+
+#[test]
+fn casts() {
+ check_number(r#"const GOAL: usize = 12 as *const i32 as usize"#, 12);
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: i32 = {
+ let a = [10, 20, 3, 15];
+ let x: &[i32] = &a;
+ let y: *const [i32] = x;
+ let z = y as *const i32;
+ unsafe { *z }
+ };
+ "#,
+ 10,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: i16 = {
+ let a = &mut 5;
+ let z = a as *mut _;
+ unsafe { *z }
+ };
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: usize = {
+ let a = &[10, 20, 30, 40] as &[i32];
+ a.len()
+ };
+ "#,
+ 4,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: usize = {
+ let a = [10, 20, 3, 15];
+ let x: &[i32] = &a;
+ let y: *const [i32] = x;
+ let z = y as *const [u8]; // slice fat pointer cast don't touch metadata
+ let q = z as *const str;
+ let p = q as *const [u8];
+ let w = unsafe { &*z };
+ w.len()
+ };
+ "#,
+ 4,
+ );
+ check_number(r#"const GOAL: i32 = -12i8 as i32"#, -12);
+}
+
+#[test]
+fn raw_pointer_equality() {
+ check_number(
+ r#"
+ //- minicore: copy, eq
+ const GOAL: bool = {
+ let a = 2;
+ let p1 = a as *const i32;
+ let p2 = a as *const i32;
+ p1 == p2
+ };
+ "#,
+ 1,
+ );
}
#[test]
@@ -166,8 +288,7 @@ fn reference_autoderef() {
#[test]
fn overloaded_deref() {
- // FIXME: We should support this.
- check_fail(
+ check_number(
r#"
//- minicore: deref_mut
struct Foo;
@@ -185,9 +306,7 @@ fn overloaded_deref() {
*y + *x
};
"#,
- ConstEvalError::MirLowerError(MirLowerError::NotSupported(
- "explicit overloaded deref".into(),
- )),
+ 10,
);
}
@@ -219,6 +338,117 @@ fn overloaded_deref_autoref() {
}
#[test]
+fn overloaded_index() {
+ check_number(
+ r#"
+ //- minicore: index
+ struct Foo;
+
+ impl core::ops::Index<usize> for Foo {
+ type Output = i32;
+ fn index(&self, index: usize) -> &i32 {
+ if index == 7 {
+ &700
+ } else {
+ &1000
+ }
+ }
+ }
+
+ impl core::ops::IndexMut<usize> for Foo {
+ fn index_mut(&mut self, index: usize) -> &mut i32 {
+ if index == 7 {
+ &mut 7
+ } else {
+ &mut 10
+ }
+ }
+ }
+
+ const GOAL: i32 = {
+ (Foo[2]) + (Foo[7]) + (*&Foo[2]) + (*&Foo[7]) + (*&mut Foo[2]) + (*&mut Foo[7])
+ };
+ "#,
+ 3417,
+ );
+}
+
+#[test]
+fn overloaded_binop() {
+ check_number(
+ r#"
+ //- minicore: add
+ enum Color {
+ Red,
+ Green,
+ Yellow,
+ }
+
+ use Color::*;
+
+ impl core::ops::Add for Color {
+ type Output = Color;
+ fn add(self, rhs: Color) -> Self::Output {
+ Yellow
+ }
+ }
+
+ impl core::ops::AddAssign for Color {
+ fn add_assign(&mut self, rhs: Color) {
+ *self = Red;
+ }
+ }
+
+ const GOAL: bool = {
+ let x = Red + Green;
+ let mut y = Green;
+ y += x;
+ x == Yellow && y == Red && Red + Green == Yellow && Red + Red == Yellow && Yellow + Green == Yellow
+ };
+ "#,
+ 1,
+ );
+ check_number(
+ r#"
+ //- minicore: add
+ impl core::ops::Add for usize {
+ type Output = usize;
+ fn add(self, rhs: usize) -> Self::Output {
+ self + rhs
+ }
+ }
+
+ impl core::ops::AddAssign for usize {
+ fn add_assign(&mut self, rhs: usize) {
+ *self += rhs;
+ }
+ }
+
+ #[lang = "shl"]
+ pub trait Shl<Rhs = Self> {
+ type Output;
+
+ fn shl(self, rhs: Rhs) -> Self::Output;
+ }
+
+ impl Shl<u8> for usize {
+ type Output = usize;
+
+ fn shl(self, rhs: u8) -> Self::Output {
+ self << rhs
+ }
+ }
+
+ const GOAL: usize = {
+ let mut x = 10;
+ x += 20;
+ 2 + 2 + (x << 1u8)
+ };"#,
+ 64,
+ );
+}
+
+#[test]
fn function_call() {
check_number(
r#"
@@ -241,20 +471,6 @@ fn function_call() {
}
#[test]
-fn intrinsics() {
- check_number(
- r#"
- extern "rust-intrinsic" {
- pub fn size_of<T>() -> usize;
- }
-
- const GOAL: usize = size_of::<i32>();
- "#,
- 4,
- );
-}
-
-#[test]
fn trait_basic() {
check_number(
r#"
@@ -301,6 +517,35 @@ fn trait_method() {
}
#[test]
+fn trait_method_inside_block() {
+ check_number(
+ r#"
+trait Twait {
+ fn a(&self) -> i32;
+}
+
+fn outer() -> impl Twait {
+ struct Stwuct;
+
+ impl Twait for Stwuct {
+ fn a(&self) -> i32 {
+ 5
+ }
+ }
+ fn f() -> impl Twait {
+ let s = Stwuct;
+ s
+ }
+ f()
+}
+
+const GOAL: i32 = outer().a();
+ "#,
+ 5,
+ );
+}
+
+#[test]
fn generic_fn() {
check_number(
r#"
@@ -357,6 +602,16 @@ fn generic_fn() {
);
check_number(
r#"
+ const fn y<T>(b: T) -> (T, ) {
+ let alloc = b;
+ (alloc, )
+ }
+ const GOAL: u8 = y(2).0;
+ "#,
+ 2,
+ );
+ check_number(
+ r#"
//- minicore: coerce_unsized, index, slice
fn bar<A, B>(a: A, b: B) -> B {
b
@@ -483,6 +738,66 @@ fn loops() {
"#,
4,
);
+ check_number(
+ r#"
+ const GOAL: u8 = {
+ let mut x = 0;
+ loop {
+ x = x + 1;
+ if x == 5 {
+ break x + 2;
+ }
+ }
+ };
+ "#,
+ 7,
+ );
+ check_number(
+ r#"
+ const GOAL: u8 = {
+ 'a: loop {
+ let x = 'b: loop {
+ let x = 'c: loop {
+ let x = 'd: loop {
+ let x = 'e: loop {
+ break 'd 1;
+ };
+ break 2 + x;
+ };
+ break 3 + x;
+ };
+ break 'a 4 + x;
+ };
+ break 5 + x;
+ }
+ };
+ "#,
+ 8,
+ );
+ check_number(
+ r#"
+ //- minicore: add
+ const GOAL: u8 = {
+ let mut x = 0;
+ 'a: loop {
+ 'b: loop {
+ 'c: while x < 20 {
+ 'd: while x < 5 {
+ 'e: loop {
+ x += 1;
+ continue 'c;
+ };
+ };
+ x += 1;
+ };
+ break 'a;
+ };
+ }
+ x
+ };
+ "#,
+ 20,
+ );
}
#[test]
@@ -523,6 +838,18 @@ fn for_loops() {
}
#[test]
+fn ranges() {
+ check_number(
+ r#"
+ //- minicore: range
+ const GOAL: i32 = (1..2).start + (20..10).end + (100..=200).start + (2000..=1000).end
+ + (10000..).start + (..100000).end + (..=1000000).end;
+ "#,
+ 1111111,
+ );
+}
+
+#[test]
fn recursion() {
check_number(
r#"
@@ -555,6 +882,38 @@ fn structs() {
"#,
17,
);
+ check_number(
+ r#"
+ struct Point {
+ x: i32,
+ y: i32,
+ }
+
+ const GOAL: i32 = {
+ let p = Point { x: 5, y: 2 };
+ let p2 = Point { x: 3, ..p };
+ p.x * 1000 + p.y * 100 + p2.x * 10 + p2.y
+ };
+ "#,
+ 5232,
+ );
+ check_number(
+ r#"
+ struct Point {
+ x: i32,
+ y: i32,
+ }
+
+ const GOAL: i32 = {
+ let p = Point { x: 5, y: 2 };
+ let Point { x, y } = p;
+ let Point { x: x2, .. } = p;
+ let Point { y: y2, .. } = p;
+ x * 1000 + y * 100 + x2 * 10 + y2
+ };
+ "#,
+ 5252,
+ );
}
#[test]
@@ -599,13 +958,14 @@ fn tuples() {
);
check_number(
r#"
- struct TupleLike(i32, u8, i64, u16);
- const GOAL: u8 = {
+ struct TupleLike(i32, i64, u8, u16);
+ const GOAL: i64 = {
let a = TupleLike(10, 20, 3, 15);
- a.1
+ let TupleLike(b, .., c) = a;
+ a.1 * 100 + b as i64 + c as i64
};
"#,
- 20,
+ 2025,
);
check_number(
r#"
@@ -638,11 +998,17 @@ fn path_pattern_matching() {
use Season::*;
+ const MY_SEASON: Season = Summer;
+
+ impl Season {
+ const FALL: Season = Fall;
+ }
+
const fn f(x: Season) -> i32 {
match x {
Spring => 1,
- Summer => 2,
- Fall => 3,
+ MY_SEASON => 2,
+ Season::FALL => 3,
Winter => 4,
}
}
@@ -653,6 +1019,91 @@ fn path_pattern_matching() {
}
#[test]
+fn pattern_matching_literal() {
+ check_number(
+ r#"
+ const fn f(x: i32) -> i32 {
+ match x {
+ -1 => 1,
+ 1 => 10,
+ _ => 100,
+ }
+ }
+ const GOAL: i32 = f(-1) + f(1) + f(0) + f(-5);
+ "#,
+ 211,
+ );
+ check_number(
+ r#"
+ const fn f(x: &str) -> i32 {
+ match x {
+ "f" => 1,
+ "foo" => 10,
+ "" => 100,
+ "bar" => 1000,
+ _ => 10000,
+ }
+ }
+ const GOAL: i32 = f("f") + f("foo") * 2 + f("") * 3 + f("bar") * 4;
+ "#,
+ 4321,
+ );
+}
+
+#[test]
+fn pattern_matching_range() {
+ check_number(
+ r#"
+ pub const L: i32 = 6;
+ mod x {
+ pub const R: i32 = 100;
+ }
+ const fn f(x: i32) -> i32 {
+ match x {
+ -1..=5 => x * 10,
+ L..=x::R => x * 100,
+ _ => x,
+ }
+ }
+ const GOAL: i32 = f(-1) + f(2) + f(100) + f(-2) + f(1000);
+ "#,
+ 11008,
+ );
+}
+
+#[test]
+fn pattern_matching_slice() {
+ check_number(
+ r#"
+ //- minicore: slice, index, coerce_unsized, copy
+ const fn f(x: &[usize]) -> usize {
+ match x {
+ [a, b @ .., c, d] => *a + b.len() + *c + *d,
+ }
+ }
+ const GOAL: usize = f(&[10, 20, 3, 15, 1000, 60, 16]);
+ "#,
+ 10 + 4 + 60 + 16,
+ );
+ check_number(
+ r#"
+ //- minicore: slice, index, coerce_unsized, copy
+ const fn f(x: &[usize]) -> usize {
+ match x {
+ [] => 0,
+ [a] => *a,
+ &[a, b] => a + b,
+ [a, b @ .., c, d] => *a + b.len() + *c + *d,
+ }
+ }
+ const GOAL: usize = f(&[]) + f(&[10]) + f(&[100, 100])
+ + f(&[1000, 1000, 1000]) + f(&[10000, 57, 34, 46, 10000, 10000]);
+ "#,
+ 33213,
+ );
+}
+
+#[test]
fn pattern_matching_ergonomics() {
check_number(
r#"
@@ -665,6 +1116,16 @@ fn pattern_matching_ergonomics() {
"#,
5,
);
+ check_number(
+ r#"
+ const GOAL: u8 = {
+ let a = &(2, 3);
+ let &(x, y) = a;
+ x + y
+ };
+ "#,
+ 5,
+ );
}
#[test]
@@ -749,6 +1210,77 @@ fn function_param_patterns() {
}
#[test]
+fn match_guards() {
+ check_number(
+ r#"
+ //- minicore: option
+ fn f(x: Option<i32>) -> i32 {
+ match x {
+ y if let Some(42) = y => 42000,
+ Some(y) => y,
+ None => 10
+ }
+ }
+ const GOAL: i32 = f(Some(42)) + f(Some(2)) + f(None);
+ "#,
+ 42012,
+ );
+}
+
+#[test]
+fn result_layout_niche_optimization() {
+ check_number(
+ r#"
+ //- minicore: option, result
+ const GOAL: i32 = match Some(2).ok_or(Some(2)) {
+ Ok(x) => x,
+ Err(_) => 1000,
+ };
+ "#,
+ 2,
+ );
+ check_number(
+ r#"
+ //- minicore: result
+ pub enum AlignmentEnum64 {
+ _Align1Shl0 = 1 << 0,
+ _Align1Shl1 = 1 << 1,
+ _Align1Shl2 = 1 << 2,
+ _Align1Shl3 = 1 << 3,
+ _Align1Shl4 = 1 << 4,
+ _Align1Shl5 = 1 << 5,
+ }
+ const GOAL: Result<AlignmentEnum64, ()> = {
+ let align = Err(());
+ align
+ };
+ "#,
+ 0, // It is 0 since result is niche encoded and 1 is valid for `AlignmentEnum64`
+ );
+ check_number(
+ r#"
+ //- minicore: result
+ pub enum AlignmentEnum64 {
+ _Align1Shl0 = 1 << 0,
+ _Align1Shl1 = 1 << 1,
+ _Align1Shl2 = 1 << 2,
+ _Align1Shl3 = 1 << 3,
+ _Align1Shl4 = 1 << 4,
+ _Align1Shl5 = 1 << 5,
+ }
+ const GOAL: i32 = {
+ let align = Ok::<_, ()>(AlignmentEnum64::_Align1Shl0);
+ match align {
+ Ok(_) => 2,
+ Err(_) => 1,
+ }
+ };
+ "#,
+ 2,
+ );
+}
+
+#[test]
fn options() {
check_number(
r#"
@@ -802,6 +1334,253 @@ fn options() {
}
#[test]
+fn from_trait() {
+ check_number(
+ r#"
+ //- minicore: from
+ struct E1(i32);
+ struct E2(i32);
+
+ impl From<E1> for E2 {
+ fn from(E1(x): E1) -> Self {
+ E2(1000 * x)
+ }
+ }
+ const GOAL: i32 = {
+ let x: E2 = E1(2).into();
+ x.0
+ };
+ "#,
+ 2000,
+ );
+}
+
+#[test]
+fn builtin_derive_macro() {
+ check_number(
+ r#"
+ //- minicore: clone, derive, builtin_impls
+ #[derive(Clone)]
+ enum Z {
+ Foo(Y),
+ Bar,
+ }
+ #[derive(Clone)]
+ struct X(i32, Z, i64)
+ #[derive(Clone)]
+ struct Y {
+ field1: i32,
+ field2: u8,
+ }
+
+ const GOAL: u8 = {
+ let x = X(2, Z::Foo(Y { field1: 4, field2: 5 }), 8);
+ let x = x.clone();
+ let Z::Foo(t) = x.1;
+ t.field2
+ };
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
+ //- minicore: default, derive, builtin_impls
+ #[derive(Default)]
+ struct X(i32, Y, i64)
+ #[derive(Default)]
+ struct Y {
+ field1: i32,
+ field2: u8,
+ }
+
+ const GOAL: u8 = {
+ let x = X::default();
+ x.1.field2
+ };
+ "#,
+ 0,
+ );
+}
+
+#[test]
+fn try_operator() {
+ check_number(
+ r#"
+ //- minicore: option, try
+ const fn f(x: Option<i32>, y: Option<i32>) -> Option<i32> {
+ Some(x? * y?)
+ }
+ const fn g(x: Option<i32>, y: Option<i32>) -> i32 {
+ match f(x, y) {
+ Some(k) => k,
+ None => 5,
+ }
+ }
+ const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None);
+ "#,
+ 215,
+ );
+ check_number(
+ r#"
+ //- minicore: result, try, from
+ struct E1(i32);
+ struct E2(i32);
+
+ impl From<E1> for E2 {
+ fn from(E1(x): E1) -> Self {
+ E2(1000 * x)
+ }
+ }
+
+ const fn f(x: Result<i32, E1>) -> Result<i32, E2> {
+ Ok(x? * 10)
+ }
+ const fn g(x: Result<i32, E1>) -> i32 {
+ match f(x) {
+ Ok(k) => 7 * k,
+ Err(E2(k)) => 5 * k,
+ }
+ }
+ const GOAL: i32 = g(Ok(2)) + g(Err(E1(3)));
+ "#,
+ 15140,
+ );
+}
+
+#[test]
+fn try_block() {
+ check_number(
+ r#"
+ //- minicore: option, try
+ const fn g(x: Option<i32>, y: Option<i32>) -> i32 {
+ let r = try { x? * y? };
+ match r {
+ Some(k) => k,
+ None => 5,
+ }
+ }
+ const GOAL: i32 = g(Some(10), Some(20)) + g(Some(30), None) + g(None, Some(40)) + g(None, None);
+ "#,
+ 215,
+ );
+}
+
+#[test]
+fn closures() {
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ const GOAL: i32 = {
+ let y = 5;
+ let c = |x| x + y;
+ c(2)
+ };
+ "#,
+ 7,
+ );
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ const GOAL: i32 = {
+ let y = 5;
+ let c = |(a, b): &(i32, i32)| *a + *b + y;
+ c(&(2, 3))
+ };
+ "#,
+ 10,
+ );
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ const GOAL: i32 = {
+ let mut y = 5;
+ let c = |x| {
+ y = y + x;
+ };
+ c(2);
+ c(3);
+ y
+ };
+ "#,
+ 10,
+ );
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ const GOAL: i32 = {
+ let c: fn(i32) -> i32 = |x| 2 * x;
+ c(2) + c(10)
+ };
+ "#,
+ 24,
+ );
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ struct X(i32);
+ impl X {
+ fn mult(&mut self, n: i32) {
+ self.0 = self.0 * n
+ }
+ }
+ const GOAL: i32 = {
+ let x = X(1);
+ let c = || {
+ x.mult(2);
+ || {
+ x.mult(3);
+ || {
+ || {
+ x.mult(4);
+ || {
+ x.mult(x.0);
+ || {
+ x.0
+ }
+ }
+ }
+ }
+ }
+ };
+ let r = c()()()()()();
+ r + x.0
+ };
+ "#,
+ 24 * 24 * 2,
+ );
+}
+
+#[test]
+fn closure_and_impl_fn() {
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ fn closure_wrapper<F: FnOnce() -> i32>(c: F) -> impl FnOnce() -> F {
+ || c
+ }
+
+ const GOAL: i32 = {
+ let y = 5;
+ let c = closure_wrapper(|| y);
+ c()()
+ };
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
+ //- minicore: fn, copy
+ fn f<T, F: Fn() -> T>(t: F) -> impl Fn() -> T {
+ move || t()
+ }
+
+ const GOAL: i32 = f(|| 2)();
+ "#,
+ 2,
+ );
+}
+
+#[test]
fn or_pattern() {
check_number(
r#"
@@ -840,6 +1619,282 @@ fn or_pattern() {
}
#[test]
+fn function_pointer_in_constants() {
+ check_number(
+ r#"
+ struct Foo {
+ f: fn(u8) -> u8,
+ }
+ const FOO: Foo = Foo { f: add2 };
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ const GOAL: u8 = (FOO.f)(3);
+ "#,
+ 5,
+ );
+}
+
+#[test]
+fn function_pointer() {
+ check_number(
+ r#"
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ const GOAL: u8 = {
+ let plus2 = add2;
+ plus2(3)
+ };
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ const GOAL: u8 = {
+ let plus2: fn(u8) -> u8 = add2;
+ plus2(3)
+ };
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ fn mult3(x: u8) -> u8 {
+ x * 3
+ }
+ const GOAL: u8 = {
+ let x = [add2, mult3];
+ x[0](1) + x[1](5)
+ };
+ "#,
+ 18,
+ );
+}
+
+#[test]
+fn enum_variant_as_function() {
+ check_number(
+ r#"
+ //- minicore: option
+ const GOAL: u8 = {
+ let f = Some;
+ f(3).unwrap_or(2)
+ };
+ "#,
+ 3,
+ );
+ check_number(
+ r#"
+ //- minicore: option
+ const GOAL: u8 = {
+ let f: fn(u8) -> Option<u8> = Some;
+ f(3).unwrap_or(2)
+ };
+ "#,
+ 3,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ enum Foo {
+ Add2(u8),
+ Mult3(u8),
+ }
+ use Foo::*;
+ const fn f(x: Foo) -> u8 {
+ match x {
+ Add2(x) => x + 2,
+ Mult3(x) => x * 3,
+ }
+ }
+ const GOAL: u8 = {
+ let x = [Add2, Mult3];
+ f(x[0](1)) + f(x[1](5))
+ };
+ "#,
+ 18,
+ );
+}
+
+#[test]
+fn function_traits() {
+ check_number(
+ r#"
+ //- minicore: fn
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ const GOAL: u8 = call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3);
+ "#,
+ 15,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, fn
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ fn call(f: &dyn Fn(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ fn call_mut(f: &mut dyn FnMut(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ const GOAL: u8 = call(&add2, 3) + call_mut(&mut add2, 3);
+ "#,
+ 10,
+ );
+ check_number(
+ r#"
+ //- minicore: fn
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ fn call(f: impl Fn(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ fn call_mut(mut f: impl FnMut(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ fn call_once(f: impl FnOnce(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ const GOAL: u8 = {
+ let add2: fn(u8) -> u8 = add2;
+ call(add2, 3) + call_mut(add2, 3) + call_once(add2, 3)
+ };
+ "#,
+ 15,
+ );
+ check_number(
+ r#"
+ //- minicore: fn
+ fn add2(x: u8) -> u8 {
+ x + 2
+ }
+ fn call(f: &&&&&impl Fn(u8) -> u8, x: u8) -> u8 {
+ f(x)
+ }
+ const GOAL: u8 = call(&&&&&add2, 3);
+ "#,
+ 5,
+ );
+}
+
+#[test]
+fn dyn_trait() {
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ trait Foo {
+ fn foo(&self) -> u8 { 10 }
+ }
+ struct S1;
+ struct S2;
+ struct S3;
+ impl Foo for S1 {
+ fn foo(&self) -> u8 { 1 }
+ }
+ impl Foo for S2 {
+ fn foo(&self) -> u8 { 2 }
+ }
+ impl Foo for S3 {}
+ const GOAL: u8 = {
+ let x: &[&dyn Foo] = &[&S1, &S2, &S3];
+ x[0].foo() + x[1].foo() + x[2].foo()
+ };
+ "#,
+ 13,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ trait Foo {
+ fn foo(&self) -> i32 { 10 }
+ }
+ trait Bar {
+ fn bar(&self) -> i32 { 20 }
+ }
+
+ struct S;
+ impl Foo for S {
+ fn foo(&self) -> i32 { 200 }
+ }
+ impl Bar for dyn Foo {
+ fn bar(&self) -> i32 { 700 }
+ }
+ const GOAL: i32 = {
+ let x: &dyn Foo = &S;
+ x.bar() + x.foo()
+ };
+ "#,
+ 900,
+ );
+}
+
+#[test]
+fn boxes() {
+ check_number(
+ r#"
+//- minicore: coerce_unsized, deref_mut, slice
+use core::ops::{Deref, DerefMut};
+use core::{marker::Unsize, ops::CoerceUnsized};
+
+#[lang = "owned_box"]
+pub struct Box<T: ?Sized> {
+ inner: *mut T,
+}
+impl<T> Box<T> {
+ fn new(t: T) -> Self {
+ #[rustc_box]
+ Box::new(t)
+ }
+}
+
+impl<T: ?Sized> Deref for Box<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &**self
+ }
+}
+
+impl<T: ?Sized> DerefMut for Box<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut **self
+ }
+}
+
+impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
+
+const GOAL: usize = {
+ let x = Box::new(5);
+ let y: Box<[i32]> = Box::new([1, 2, 3]);
+ *x + y.len()
+};
+"#,
+ 8,
+ );
+}
+
+#[test]
fn array_and_index() {
check_number(
r#"
@@ -867,9 +1922,42 @@ fn array_and_index() {
check_number(
r#"
//- minicore: coerce_unsized, index, slice
+ const GOAL: usize = {
+ let a = [1, 2, 3];
+ let x: &[i32] = &a;
+ let y = &*x;
+ y.len()
+ };"#,
+ 3,
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
const GOAL: usize = [1, 2, 3, 4, 5].len();"#,
5,
);
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: [u16; 5] = [1, 2, 3, 4, 5];"#,
+ 1 + (2 << 16) + (3 << 32) + (4 << 48) + (5 << 64),
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: [u16; 5] = [12; 5];"#,
+ 12 + (12 << 16) + (12 << 32) + (12 << 48) + (12 << 64),
+ );
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const LEN: usize = 4;
+ const GOAL: u16 = {
+ let x = [7; LEN];
+ x[2]
+ }"#,
+ 7,
+ );
}
#[test]
@@ -888,6 +1976,38 @@ fn byte_string() {
}
#[test]
+fn c_string() {
+ check_number(
+ r#"
+//- minicore: index, slice
+#[lang = "CStr"]
+pub struct CStr {
+ inner: [u8]
+}
+const GOAL: u8 = {
+ let a = c"hello";
+ a.inner[0]
+};
+ "#,
+ 104,
+ );
+ check_number(
+ r#"
+//- minicore: index, slice
+#[lang = "CStr"]
+pub struct CStr {
+ inner: [u8]
+}
+const GOAL: u8 = {
+ let a = c"hello";
+ a.inner[6]
+};
+ "#,
+ 0,
+ );
+}
+
+#[test]
fn consts() {
check_number(
r#"
@@ -901,6 +2021,48 @@ fn consts() {
}
#[test]
+fn statics() {
+ check_number(
+ r#"
+ //- minicore: cell
+ use core::cell::Cell;
+ fn f() -> i32 {
+ static S: Cell<i32> = Cell::new(10);
+ S.set(S.get() + 1);
+ S.get()
+ }
+ const GOAL: i32 = f() + f() + f();
+ "#,
+ 36,
+ );
+}
+
+#[test]
+fn extern_weak_statics() {
+ check_number(
+ r#"
+ extern "C" {
+ #[linkage = "extern_weak"]
+ static __dso_handle: *mut u8;
+ }
+ const GOAL: usize = __dso_handle as usize;
+ "#,
+ 0,
+ );
+}
+
+#[test]
+fn from_ne_bytes() {
+ check_number(
+ r#"
+//- minicore: int_impl
+const GOAL: u32 = u32::from_ne_bytes([44, 1, 0, 0]);
+ "#,
+ 300,
+ );
+}
+
+#[test]
fn enums() {
check_number(
r#"
@@ -927,14 +2089,14 @@ fn enums() {
"#,
0,
);
- let r = eval_goal(
+ let (db, file_id) = TestDB::with_single_file(
r#"
enum E { A = 1, B }
const GOAL: E = E::A;
"#,
- )
- .unwrap();
- assert_eq!(try_const_usize(&r), Some(1));
+ );
+ let r = eval_goal(&db, file_id).unwrap();
+ assert_eq!(try_const_usize(&db, &r), Some(1));
}
#[test]
@@ -946,7 +2108,7 @@ fn const_loop() {
const F2: i32 = 2 * F1;
const GOAL: i32 = F3;
"#,
- ConstEvalError::MirLowerError(MirLowerError::Loop),
+ |e| e == ConstEvalError::MirLowerError(MirLowerError::Loop),
);
}
@@ -963,6 +2125,29 @@ fn const_transfer_memory() {
}
#[test]
+fn anonymous_const_block() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn size_of<T>() -> usize;
+ }
+
+ const fn f<T>() -> usize {
+ let r = const { size_of::<T>() };
+ r
+ }
+
+ const GOAL: usize = {
+ let x = const { 2 + const { 3 } };
+ let y = f::<i32>();
+ x + y
+ };
+ "#,
+ 9,
+ );
+}
+
+#[test]
fn const_impl_assoc() {
check_number(
r#"
@@ -970,9 +2155,9 @@ fn const_impl_assoc() {
impl U5 {
const VAL: usize = 5;
}
- const GOAL: usize = U5::VAL;
+ const GOAL: usize = U5::VAL + <U5>::VAL;
"#,
- 5,
+ 10,
);
}
@@ -987,12 +2172,61 @@ fn const_generic_subst_fn() {
"#,
11,
);
+ check_number(
+ r#"
+ fn f<const N: usize>(x: [i32; N]) -> usize {
+ N
+ }
+
+ trait ArrayExt {
+ fn f(self) -> usize;
+ }
+
+ impl<T, const N: usize> ArrayExt for [T; N] {
+ fn g(self) -> usize {
+ f(self)
+ }
+ }
+
+ const GOAL: usize = f([1, 2, 5]);
+ "#,
+ 3,
+ );
+}
+
+#[test]
+fn layout_of_type_with_associated_type_field_defined_inside_body() {
+ check_number(
+ r#"
+trait Tr {
+ type Ty;
+}
+
+struct St<T: Tr>(T::Ty);
+
+const GOAL: i64 = {
+ // if we move `St2` out of body, the test will fail, as we don't see the impl anymore. That
+ // case will probably be rejected by rustc in some later edition, but we should support this
+ // case.
+ struct St2;
+
+ impl Tr for St2 {
+ type Ty = i64;
+ }
+
+ struct Goal(St<St2>);
+
+ let x = Goal(St(5));
+ x.0.0
+};
+"#,
+ 5,
+ );
}
#[test]
fn const_generic_subst_assoc_const_impl() {
- // FIXME: this should evaluate to 5
- check_fail(
+ check_number(
r#"
struct Adder<const N: usize, const M: usize>;
impl<const N: usize, const M: usize> Adder<N, M> {
@@ -1000,14 +2234,42 @@ fn const_generic_subst_assoc_const_impl() {
}
const GOAL: usize = Adder::<2, 3>::VAL;
"#,
- ConstEvalError::MirEvalError(MirEvalError::TypeError("missing generic arg")),
+ 5,
+ );
+}
+
+#[test]
+fn associated_types() {
+ check_number(
+ r#"
+ trait Tr {
+ type Item;
+ fn get_item(&self) -> Self::Item;
+ }
+
+ struct X(i32);
+ struct Y(i32);
+
+ impl Tr for X {
+ type Item = Y;
+ fn get_item(&self) -> Self::Item {
+ Y(self.0 + 2)
+ }
+ }
+
+ fn my_get_item<T: Tr>(x: T) -> <T as Tr>::Item {
+ x.get_item()
+ }
+
+ const GOAL: i32 = my_get_item(X(3)).0;
+ "#,
+ 5,
);
}
#[test]
fn const_trait_assoc() {
- // FIXME: this should evaluate to 0
- check_fail(
+ check_number(
r#"
struct U0;
trait ToConst {
@@ -1016,9 +2278,49 @@ fn const_trait_assoc() {
impl ToConst for U0 {
const VAL: usize = 0;
}
- const GOAL: usize = U0::VAL;
+ impl ToConst for i32 {
+ const VAL: usize = 32;
+ }
+ const GOAL: usize = U0::VAL + i32::VAL;
"#,
- ConstEvalError::MirLowerError(MirLowerError::IncompleteExpr),
+ 32,
+ );
+ check_number(
+ r#"
+ struct S<T>(*mut T);
+
+ trait MySized: Sized {
+ const SIZE: S<Self> = S(1 as *mut Self);
+ }
+
+ impl MySized for i32 {
+ const SIZE: S<i32> = S(10 as *mut i32);
+ }
+
+ impl MySized for i64 {
+ }
+
+ const fn f<T: MySized>() -> usize {
+ T::SIZE.0 as usize
+ }
+
+ const GOAL: usize = f::<i32>() + f::<i64>() * 2;
+ "#,
+ 12,
+ );
+}
+
+#[test]
+fn panic_messages() {
+ check_fail(
+ r#"
+ //- minicore: panic
+ const GOAL: u8 = {
+ let x: u16 = 2;
+ panic!("hello");
+ };
+ "#,
+ |e| e == ConstEvalError::MirEvalError(MirEvalError::Panic("hello".to_string())),
);
}
@@ -1028,7 +2330,7 @@ fn exec_limits() {
r#"
const GOAL: usize = loop {};
"#,
- ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
+ |e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
);
check_fail(
r#"
@@ -1037,7 +2339,7 @@ fn exec_limits() {
}
const GOAL: i32 = f(0);
"#,
- ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
+ |e| e == ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
);
// Reasonable code should still work
check_number(
@@ -1062,7 +2364,7 @@ fn exec_limits() {
#[test]
fn type_error() {
- let e = eval_goal(
+ check_fail(
r#"
const GOAL: u8 = {
let x: u16 = 2;
@@ -1070,6 +2372,25 @@ fn type_error() {
y.0
};
"#,
+ |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))),
+ );
+}
+
+#[test]
+fn unsized_local() {
+ check_fail(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const fn x() -> SomeUnknownTypeThatDereferenceToSlice {
+ SomeUnknownTypeThatDereferenceToSlice
+ }
+
+ const GOAL: u16 = {
+ let y = x();
+ let z: &[u16] = &y;
+ z[1]
+ };
+ "#,
+ |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::UnsizedTemporary(_))),
);
- assert!(matches!(e, Err(ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_)))));
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
new file mode 100644
index 000000000..e05d824db
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -0,0 +1,377 @@
+use super::*;
+
+#[test]
+fn size_of() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn size_of<T>() -> usize;
+ }
+
+ const GOAL: usize = size_of::<i32>();
+ "#,
+ 4,
+ );
+}
+
+#[test]
+fn transmute() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn transmute<T, U>(e: T) -> U;
+ }
+
+ const GOAL: i32 = transmute((1i16, 1i16));
+ "#,
+ 0x00010001,
+ );
+}
+
+#[test]
+fn const_eval_select() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET
+ where
+ G: FnOnce<ARG, Output = RET>,
+ F: FnOnce<ARG, Output = RET>;
+ }
+
+ const fn in_const(x: i32, y: i32) -> i32 {
+ x + y
+ }
+
+ fn in_rt(x: i32, y: i32) -> i32 {
+ x + y
+ }
+
+ const GOAL: i32 = const_eval_select((2, 3), in_const, in_rt);
+ "#,
+ 5,
+ );
+}
+
+#[test]
+fn wrapping_add() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn wrapping_add<T>(a: T, b: T) -> T;
+ }
+
+ const GOAL: u8 = wrapping_add(10, 250);
+ "#,
+ 4,
+ );
+}
+
+#[test]
+fn saturating_add() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn saturating_add<T>(a: T, b: T) -> T;
+ }
+
+ const GOAL: u8 = saturating_add(10, 250);
+ "#,
+ 255,
+ );
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn saturating_add<T>(a: T, b: T) -> T;
+ }
+
+ const GOAL: i8 = saturating_add(5, 8);
+ "#,
+ 13,
+ );
+}
+
+#[test]
+fn allocator() {
+ check_number(
+ r#"
+ extern "Rust" {
+ #[rustc_allocator]
+ fn __rust_alloc(size: usize, align: usize) -> *mut u8;
+ #[rustc_deallocator]
+ fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize);
+ #[rustc_reallocator]
+ fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
+ #[rustc_allocator_zeroed]
+ fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
+ }
+
+ const GOAL: u8 = unsafe {
+ let ptr = __rust_alloc(4, 1);
+ let ptr2 = ((ptr as usize) + 1) as *mut u8;
+ *ptr = 23;
+ *ptr2 = 32;
+ let ptr = __rust_realloc(ptr, 4, 1, 8);
+ let ptr2 = ((ptr as usize) + 1) as *mut u8;
+ *ptr + *ptr2
+ };
+ "#,
+ 55,
+ );
+}
+
+#[test]
+fn overflowing_add() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
+ }
+
+ const GOAL: u8 = add_with_overflow(1, 2).0;
+ "#,
+ 3,
+ );
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
+ }
+
+ const GOAL: u8 = add_with_overflow(1, 2).1 as u8;
+ "#,
+ 0,
+ );
+}
+
+#[test]
+fn needs_drop() {
+ check_number(
+ r#"
+ //- minicore: copy, sized
+ extern "rust-intrinsic" {
+ pub fn needs_drop<T: ?Sized>() -> bool;
+ }
+ struct X;
+ const GOAL: bool = !needs_drop::<i32>() && needs_drop::<X>();
+ "#,
+ 1,
+ );
+}
+
+#[test]
+fn likely() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn likely(b: bool) -> bool;
+ pub fn unlikely(b: bool) -> bool;
+ }
+
+ const GOAL: bool = likely(true) && unlikely(true) && !likely(false) && !unlikely(false);
+ "#,
+ 1,
+ );
+}
+
+#[test]
+fn floating_point() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn sqrtf32(x: f32) -> f32;
+ pub fn powf32(a: f32, x: f32) -> f32;
+ pub fn fmaf32(a: f32, b: f32, c: f32) -> f32;
+ }
+
+ const GOAL: f32 = sqrtf32(1.2) + powf32(3.4, 5.6) + fmaf32(-7.8, 1.3, 2.4);
+ "#,
+ i128::from_le_bytes(pad16(
+ &f32::to_le_bytes(1.2f32.sqrt() + 3.4f32.powf(5.6) + (-7.8f32).mul_add(1.3, 2.4)),
+ true,
+ )),
+ );
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn powif64(a: f64, x: i32) -> f64;
+ pub fn sinf64(x: f64) -> f64;
+ pub fn minnumf64(x: f64, y: f64) -> f64;
+ }
+
+ const GOAL: f64 = powif64(1.2, 5) + sinf64(3.4) + minnumf64(-7.8, 1.3);
+ "#,
+ i128::from_le_bytes(pad16(
+ &f64::to_le_bytes(1.2f64.powi(5) + 3.4f64.sin() + (-7.8f64).min(1.3)),
+ true,
+ )),
+ );
+}
+
+#[test]
+fn atomic() {
+ check_number(
+ r#"
+ //- minicore: copy
+ extern "rust-intrinsic" {
+ pub fn atomic_load_seqcst<T: Copy>(src: *const T) -> T;
+ pub fn atomic_xchg_acquire<T: Copy>(dst: *mut T, src: T) -> T;
+ pub fn atomic_cxchg_release_seqcst<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool);
+ pub fn atomic_cxchgweak_acquire_acquire<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool);
+ pub fn atomic_store_release<T: Copy>(dst: *mut T, val: T);
+ pub fn atomic_xadd_acqrel<T: Copy>(dst: *mut T, src: T) -> T;
+ pub fn atomic_xsub_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
+ pub fn atomic_and_acquire<T: Copy>(dst: *mut T, src: T) -> T;
+ pub fn atomic_nand_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
+ pub fn atomic_or_release<T: Copy>(dst: *mut T, src: T) -> T;
+ pub fn atomic_xor_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
+ }
+
+ fn should_not_reach() {
+ _ // fails the test if executed
+ }
+
+ const GOAL: i32 = {
+ let mut x = 5;
+ atomic_store_release(&mut x, 10);
+ let mut y = atomic_xchg_acquire(&mut x, 100);
+ atomic_xadd_acqrel(&mut y, 20);
+ if (30, true) != atomic_cxchg_release_seqcst(&mut y, 30, 40) {
+ should_not_reach();
+ }
+ if (40, false) != atomic_cxchg_release_seqcst(&mut y, 30, 50) {
+ should_not_reach();
+ }
+ if (40, true) != atomic_cxchgweak_acquire_acquire(&mut y, 40, 30) {
+ should_not_reach();
+ }
+ let mut z = atomic_xsub_seqcst(&mut x, -200);
+ atomic_xor_seqcst(&mut x, 1024);
+ atomic_load_seqcst(&x) + z * 3 + atomic_load_seqcst(&y) * 2
+ };
+ "#,
+ 660 + 1024,
+ );
+}
+
+#[test]
+fn offset() {
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ extern "rust-intrinsic" {
+ pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
+ }
+
+ const GOAL: u8 = unsafe {
+ let ar: &[(u8, u8, u8)] = &[
+ (10, 11, 12),
+ (20, 21, 22),
+ (30, 31, 32),
+ (40, 41, 42),
+ (50, 51, 52),
+ ];
+ let ar: *const [(u8, u8, u8)] = ar;
+ let ar = ar as *const (u8, u8, u8);
+ let element = *offset(ar, 2);
+ element.1
+ };
+ "#,
+ 31,
+ );
+}
+
+#[test]
+fn arith_offset() {
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ extern "rust-intrinsic" {
+ pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
+ }
+
+ const GOAL: u8 = unsafe {
+ let ar: &[(u8, u8, u8)] = &[
+ (10, 11, 12),
+ (20, 21, 22),
+ (30, 31, 32),
+ (40, 41, 42),
+ (50, 51, 52),
+ ];
+ let ar: *const [(u8, u8, u8)] = ar;
+ let ar = ar as *const (u8, u8, u8);
+ let element = *arith_offset(arith_offset(ar, 102), -100);
+ element.1
+ };
+ "#,
+ 31,
+ );
+}
+
+#[test]
+fn copy_nonoverlapping() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
+ }
+
+ const GOAL: u8 = unsafe {
+ let mut x = 2;
+ let y = 5;
+ copy_nonoverlapping(&y, &mut x, 1);
+ x
+ };
+ "#,
+ 5,
+ );
+}
+
+#[test]
+fn copy() {
+ check_number(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ extern "rust-intrinsic" {
+ pub fn copy<T>(src: *const T, dst: *mut T, count: usize);
+ }
+
+ const GOAL: i32 = unsafe {
+ let mut x = [1i32, 2, 3, 4, 5];
+ let y = (&mut x as *mut _) as *mut i32;
+ let z = (y as usize + 4) as *const i32;
+ copy(z, y, 4);
+ x[0] + x[1] + x[2] + x[3] + x[4]
+ };
+ "#,
+ 19,
+ );
+}
+
+#[test]
+fn ctpop() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn ctpop<T: Copy>(x: T) -> T;
+ }
+
+ const GOAL: i64 = ctpop(-29);
+ "#,
+ 61,
+ );
+}
+
+#[test]
+fn cttz() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn cttz<T: Copy>(x: T) -> T;
+ }
+
+ const GOAL: i64 = cttz(-24);
+ "#,
+ 3,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
index 304c78767..9dd810f84 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -1,27 +1,27 @@
//! The home of `HirDatabase`, which is the Salsa database containing all the
//! type inference-related queries.
-use std::sync::Arc;
+use std::sync;
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{
- db::DefDatabase,
- expr::ExprId,
- layout::{Layout, LayoutError, TargetDataLayout},
- AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId,
- ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
+ db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId,
+ DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
+ LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId,
};
use la_arena::ArenaMap;
use smallvec::SmallVec;
+use triomphe::Arc;
use crate::{
chalk_db,
consteval::ConstEvalError,
+ layout::{Layout, LayoutError},
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
mir::{BorrowckResult, MirBody, MirLowerError},
- Binders, CallableDefId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner,
- PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId,
- ValueTyDefId,
+ Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult,
+ Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty,
+ TyDefId, ValueTyDefId,
};
use hir_expand::name::Name;
@@ -38,8 +38,28 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::cycle(crate::mir::mir_body_recover)]
fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>;
+ #[salsa::invoke(crate::mir::mir_body_for_closure_query)]
+ fn mir_body_for_closure(&self, def: ClosureId) -> Result<Arc<MirBody>, MirLowerError>;
+
+ #[salsa::invoke(crate::mir::monomorphized_mir_body_query)]
+ #[salsa::cycle(crate::mir::monomorphized_mir_body_recover)]
+ fn monomorphized_mir_body(
+ &self,
+ def: DefWithBodyId,
+ subst: Substitution,
+ env: Arc<crate::TraitEnvironment>,
+ ) -> Result<Arc<MirBody>, MirLowerError>;
+
+ #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)]
+ fn monomorphized_mir_body_for_closure(
+ &self,
+ def: ClosureId,
+ subst: Substitution,
+ env: Arc<crate::TraitEnvironment>,
+ ) -> Result<Arc<MirBody>, MirLowerError>;
+
#[salsa::invoke(crate::mir::borrowck_query)]
- fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<BorrowckResult>, MirLowerError>;
+ fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>;
#[salsa::invoke(crate::lower::ty_query)]
#[salsa::cycle(crate::lower::ty_recover)]
@@ -57,7 +77,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::consteval::const_eval_query)]
#[salsa::cycle(crate::consteval::const_eval_recover)]
- fn const_eval(&self, def: ConstId) -> Result<Const, ConstEvalError>;
+ fn const_eval(&self, def: GeneralConstId, subst: Substitution)
+ -> Result<Const, ConstEvalError>;
+
+ #[salsa::invoke(crate::consteval::const_eval_static_query)]
+ #[salsa::cycle(crate::consteval::const_eval_static_recover)]
+ fn const_eval_static(&self, def: StaticId) -> Result<Const, ConstEvalError>;
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
#[salsa::cycle(crate::consteval::const_eval_discriminant_recover)]
@@ -71,7 +96,16 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::layout::layout_of_adt_query)]
#[salsa::cycle(crate::layout::layout_of_adt_recover)]
- fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result<Layout, LayoutError>;
+ fn layout_of_adt(
+ &self,
+ def: AdtId,
+ subst: Substitution,
+ krate: CrateId,
+ ) -> Result<Arc<Layout>, LayoutError>;
+
+ #[salsa::invoke(crate::layout::layout_of_ty_query)]
+ #[salsa::cycle(crate::layout::layout_of_ty_recover)]
+ fn layout_of_ty(&self, ty: Ty, krate: CrateId) -> Result<Arc<Layout>, LayoutError>;
#[salsa::invoke(crate::layout::target_data_layout_query)]
fn target_data_layout(&self, krate: CrateId) -> Option<Arc<TargetDataLayout>>;
@@ -97,6 +131,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::lower::generic_predicates_query)]
fn generic_predicates(&self, def: GenericDefId) -> Arc<[Binders<QuantifiedWhereClause>]>;
+ #[salsa::invoke(crate::lower::trait_environment_for_body_query)]
+ #[salsa::transparent]
+ fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<crate::TraitEnvironment>;
+
#[salsa::invoke(crate::lower::trait_environment_query)]
fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>;
@@ -108,7 +146,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn inherent_impls_in_crate(&self, krate: CrateId) -> Arc<InherentImpls>;
#[salsa::invoke(InherentImpls::inherent_impls_in_block_query)]
- fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>;
+ fn inherent_impls_in_block(&self, block: BlockId) -> Arc<InherentImpls>;
/// Collects all crates in the dependency graph that have impls for the
/// given fingerprint. This is only used for primitive types and types
@@ -125,10 +163,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
#[salsa::invoke(TraitImpls::trait_impls_in_block_query)]
- fn trait_impls_in_block(&self, krate: BlockId) -> Option<Arc<TraitImpls>>;
+ fn trait_impls_in_block(&self, block: BlockId) -> Arc<TraitImpls>;
#[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
- fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>;
+ fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<[Arc<TraitImpls>]>;
// Interned IDs for Chalk integration
#[salsa::interned]
@@ -148,24 +186,34 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn intern_generator(&self, id: (DefWithBodyId, ExprId)) -> InternedGeneratorId;
#[salsa::invoke(chalk_db::associated_ty_data_query)]
- fn associated_ty_data(&self, id: chalk_db::AssocTypeId) -> Arc<chalk_db::AssociatedTyDatum>;
+ fn associated_ty_data(
+ &self,
+ id: chalk_db::AssocTypeId,
+ ) -> sync::Arc<chalk_db::AssociatedTyDatum>;
#[salsa::invoke(chalk_db::trait_datum_query)]
- fn trait_datum(&self, krate: CrateId, trait_id: chalk_db::TraitId)
- -> Arc<chalk_db::TraitDatum>;
+ fn trait_datum(
+ &self,
+ krate: CrateId,
+ trait_id: chalk_db::TraitId,
+ ) -> sync::Arc<chalk_db::TraitDatum>;
#[salsa::invoke(chalk_db::struct_datum_query)]
fn struct_datum(
&self,
krate: CrateId,
struct_id: chalk_db::AdtId,
- ) -> Arc<chalk_db::StructDatum>;
+ ) -> sync::Arc<chalk_db::StructDatum>;
#[salsa::invoke(chalk_db::impl_datum_query)]
- fn impl_datum(&self, krate: CrateId, impl_id: chalk_db::ImplId) -> Arc<chalk_db::ImplDatum>;
+ fn impl_datum(
+ &self,
+ krate: CrateId,
+ impl_id: chalk_db::ImplId,
+ ) -> sync::Arc<chalk_db::ImplDatum>;
#[salsa::invoke(chalk_db::fn_def_datum_query)]
- fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> Arc<chalk_db::FnDefDatum>;
+ fn fn_def_datum(&self, krate: CrateId, fn_def_id: FnDefId) -> sync::Arc<chalk_db::FnDefDatum>;
#[salsa::invoke(chalk_db::fn_def_variance_query)]
fn fn_def_variance(&self, fn_def_id: FnDefId) -> chalk_db::Variances;
@@ -178,7 +226,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
&self,
krate: CrateId,
id: chalk_db::AssociatedTyValueId,
- ) -> Arc<chalk_db::AssociatedTyValue>;
+ ) -> sync::Arc<chalk_db::AssociatedTyValue>;
#[salsa::invoke(crate::traits::normalize_projection_query)]
#[salsa::transparent]
@@ -193,6 +241,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn trait_solve(
&self,
krate: CrateId,
+ block: Option<BlockId>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution>;
@@ -200,6 +249,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn trait_solve_query(
&self,
krate: CrateId,
+ block: Option<BlockId>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution>;
@@ -207,20 +257,28 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn program_clauses_for_chalk_env(
&self,
krate: CrateId,
+ block: Option<BlockId>,
env: chalk_ir::Environment<Interner>,
) -> chalk_ir::ProgramClauses<Interner>;
}
fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
let _p = profile::span("infer:wait").detail(|| match def {
- DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(),
- DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(),
- DefWithBodyId::ConstId(it) => {
- db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
+ DefWithBodyId::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(),
+ DefWithBodyId::StaticId(it) => {
+ db.static_data(it).name.clone().display(db.upcast()).to_string()
}
+ DefWithBodyId::ConstId(it) => db
+ .const_data(it)
+ .name
+ .clone()
+ .unwrap_or_else(Name::missing)
+ .display(db.upcast())
+ .to_string(),
DefWithBodyId::VariantId(it) => {
- db.enum_data(it.parent).variants[it.local_id].name.to_string()
+ db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string()
}
+ DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"),
});
db.infer_query(def)
}
@@ -228,10 +286,11 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
fn trait_solve_wait(
db: &dyn HirDatabase,
krate: CrateId,
+ block: Option<BlockId>,
goal: crate::Canonical<crate::InEnvironment<crate::Goal>>,
) -> Option<crate::Solution> {
let _p = profile::span("trait_solve::wait");
- db.trait_solve_query(krate, goal)
+ db.trait_solve_query(krate, block, goal)
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
index d36b93e3b..1233469b9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -16,8 +16,8 @@ use std::fmt;
use base_db::CrateId;
use hir_def::{
- adt::VariantData,
- expr::{Pat, PatId},
+ data::adt::VariantData,
+ hir::{Pat, PatId},
src::HasSource,
AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, StaticId,
StructId,
@@ -223,7 +223,7 @@ impl<'a> DeclValidator<'a> {
}
// Check the function name.
- let function_name = data.name.to_string();
+ let function_name = data.name.display(self.db.upcast()).to_string();
let fn_name_replacement = to_lower_snake_case(&function_name).map(|new_name| Replacement {
current_name: data.name.clone(),
suggested_text: new_name,
@@ -244,7 +244,9 @@ impl<'a> DeclValidator<'a> {
id,
Replacement {
current_name: bind_name.clone(),
- suggested_text: to_lower_snake_case(&bind_name.to_string())?,
+ suggested_text: to_lower_snake_case(
+ &bind_name.display(self.db.upcast()).to_string(),
+ )?,
expected_case: CaseType::LowerSnakeCase,
},
))
@@ -287,7 +289,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Function,
ident: AstPtr::new(&ast_ptr),
expected_case: fn_name_replacement.expected_case,
- ident_text: fn_name_replacement.current_name.to_string(),
+ ident_text: fn_name_replacement.current_name.display(self.db.upcast()).to_string(),
suggested_text: fn_name_replacement.suggested_text,
};
@@ -343,7 +345,10 @@ impl<'a> DeclValidator<'a> {
ident_type,
ident: AstPtr::new(&name_ast),
expected_case: replacement.expected_case,
- ident_text: replacement.current_name.to_string(),
+ ident_text: replacement
+ .current_name
+ .display(self.db.upcast())
+ .to_string(),
suggested_text: replacement.suggested_text,
};
@@ -362,7 +367,7 @@ impl<'a> DeclValidator<'a> {
let non_snake_case_allowed = self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false);
// Check the structure name.
- let struct_name = data.name.to_string();
+ let struct_name = data.name.display(self.db.upcast()).to_string();
let struct_name_replacement = if !non_camel_case_allowed {
to_camel_case(&struct_name).map(|new_name| Replacement {
current_name: data.name.clone(),
@@ -379,7 +384,7 @@ impl<'a> DeclValidator<'a> {
if !non_snake_case_allowed {
if let VariantData::Record(fields) = data.variant_data.as_ref() {
for (_, field) in fields.iter() {
- let field_name = field.name.to_string();
+ let field_name = field.name.display(self.db.upcast()).to_string();
if let Some(new_name) = to_lower_snake_case(&field_name) {
let replacement = Replacement {
current_name: field.name.clone(),
@@ -434,7 +439,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Structure,
ident: AstPtr::new(&ast_ptr),
expected_case: replacement.expected_case,
- ident_text: replacement.current_name.to_string(),
+ ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
suggested_text: replacement.suggested_text,
};
@@ -479,7 +484,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Field,
ident: AstPtr::new(&ast_ptr),
expected_case: field_to_rename.expected_case,
- ident_text: field_to_rename.current_name.to_string(),
+ ident_text: field_to_rename.current_name.display(self.db.upcast()).to_string(),
suggested_text: field_to_rename.suggested_text,
};
@@ -496,7 +501,7 @@ impl<'a> DeclValidator<'a> {
}
// Check the enum name.
- let enum_name = data.name.to_string();
+ let enum_name = data.name.display(self.db.upcast()).to_string();
let enum_name_replacement = to_camel_case(&enum_name).map(|new_name| Replacement {
current_name: data.name.clone(),
suggested_text: new_name,
@@ -510,7 +515,9 @@ impl<'a> DeclValidator<'a> {
.filter_map(|(_, variant)| {
Some(Replacement {
current_name: variant.name.clone(),
- suggested_text: to_camel_case(&variant.name.to_string())?,
+ suggested_text: to_camel_case(
+ &variant.name.display(self.db.upcast()).to_string(),
+ )?,
expected_case: CaseType::UpperCamelCase,
})
})
@@ -558,7 +565,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Enum,
ident: AstPtr::new(&ast_ptr),
expected_case: replacement.expected_case,
- ident_text: replacement.current_name.to_string(),
+ ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
suggested_text: replacement.suggested_text,
};
@@ -603,7 +610,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Variant,
ident: AstPtr::new(&ast_ptr),
expected_case: variant_to_rename.expected_case,
- ident_text: variant_to_rename.current_name.to_string(),
+ ident_text: variant_to_rename.current_name.display(self.db.upcast()).to_string(),
suggested_text: variant_to_rename.suggested_text,
};
@@ -623,7 +630,7 @@ impl<'a> DeclValidator<'a> {
None => return,
};
- let const_name = name.to_string();
+ let const_name = name.display(self.db.upcast()).to_string();
let replacement = if let Some(new_name) = to_upper_snake_case(&const_name) {
Replacement {
current_name: name.clone(),
@@ -648,7 +655,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::Constant,
ident: AstPtr::new(&ast_ptr),
expected_case: replacement.expected_case,
- ident_text: replacement.current_name.to_string(),
+ ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
suggested_text: replacement.suggested_text,
};
@@ -668,7 +675,7 @@ impl<'a> DeclValidator<'a> {
let name = &data.name;
- let static_name = name.to_string();
+ let static_name = name.display(self.db.upcast()).to_string();
let replacement = if let Some(new_name) = to_upper_snake_case(&static_name) {
Replacement {
current_name: name.clone(),
@@ -693,7 +700,7 @@ impl<'a> DeclValidator<'a> {
ident_type: IdentType::StaticVariable,
ident: AstPtr::new(&ast_ptr),
expected_case: replacement.expected_case,
- ident_text: replacement.current_name.to_string(),
+ ident_text: replacement.current_name.display(self.db.upcast()).to_string(),
suggested_text: replacement.suggested_text,
};
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index 2e9066788..ab34dc88d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -3,7 +3,6 @@
//! fields, etc.
use std::fmt;
-use std::sync::Arc;
use either::Either;
use hir_def::lang_item::LangItem;
@@ -12,6 +11,7 @@ use hir_def::{ItemContainerId, Lookup};
use hir_expand::name;
use itertools::Itertools;
use rustc_hash::FxHashSet;
+use triomphe::Arc;
use typed_arena::Arena;
use crate::{
@@ -27,7 +27,7 @@ use crate::{
pub(crate) use hir_def::{
body::Body,
- expr::{Expr, ExprId, MatchArm, Pat, PatId},
+ hir::{Expr, ExprId, MatchArm, Pat, PatId},
LocalFieldId, VariantId,
};
@@ -207,7 +207,7 @@ impl ExprValidator {
let report = compute_match_usefulness(&cx, &m_arms, scrut_ty);
- // FIXME Report unreacheble arms
+ // FIXME Report unreachable arms
// https://github.com/rust-lang/rust/blob/f31622a50/compiler/rustc_mir_build/src/thir/pattern/check_match.rs#L200
let witnesses = report.non_exhaustiveness_witnesses;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
index 859a37804..f8cdeaa5e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
@@ -1,6 +1,6 @@
//! Validation of matches.
//!
-//! This module provides lowering from [hir_def::expr::Pat] to [self::Pat] and match
+//! This module provides lowering from [hir_def::hir::Pat] to [self::Pat] and match
//! checking algorithm.
//!
//! It is modeled on the rustc module `rustc_mir_build::thir::pattern`.
@@ -12,7 +12,7 @@ pub(crate) mod usefulness;
use chalk_ir::Mutability;
use hir_def::{
- adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
+ body::Body, data::adt::VariantData, hir::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId,
};
use hir_expand::name::Name;
use stdx::{always, never};
@@ -125,15 +125,15 @@ impl<'a> PatCtxt<'a> {
let variant = self.infer.variant_resolution_for_pat(pat);
let kind = match self.body[pat] {
- hir_def::expr::Pat::Wild => PatKind::Wild,
+ hir_def::hir::Pat::Wild => PatKind::Wild,
- hir_def::expr::Pat::Lit(expr) => self.lower_lit(expr),
+ hir_def::hir::Pat::Lit(expr) => self.lower_lit(expr),
- hir_def::expr::Pat::Path(ref path) => {
+ hir_def::hir::Pat::Path(ref path) => {
return self.lower_path(pat, path);
}
- hir_def::expr::Pat::Tuple { ref args, ellipsis } => {
+ hir_def::hir::Pat::Tuple { ref args, ellipsis } => {
let arity = match *ty.kind(Interner) {
TyKind::Tuple(arity, _) => arity,
_ => {
@@ -146,13 +146,14 @@ impl<'a> PatCtxt<'a> {
PatKind::Leaf { subpatterns }
}
- hir_def::expr::Pat::Bind { id, subpat, .. } => {
- let bm = self.infer.pat_binding_modes[&pat];
+ hir_def::hir::Pat::Bind { id, subpat, .. } => {
+ let bm = self.infer.binding_modes[id];
+ ty = &self.infer[id];
let name = &self.body.bindings[id].name;
match (bm, ty.kind(Interner)) {
(BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty,
(BindingMode::Ref(_), _) => {
- never!("`ref {}` has wrong type {:?}", name, ty);
+ never!("`ref {}` has wrong type {:?}", name.display(self.db.upcast()), ty);
self.errors.push(PatternError::UnexpectedType);
return Pat { ty: ty.clone(), kind: PatKind::Wild.into() };
}
@@ -161,13 +162,13 @@ impl<'a> PatCtxt<'a> {
PatKind::Binding { name: name.clone(), subpattern: self.lower_opt_pattern(subpat) }
}
- hir_def::expr::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => {
+ hir_def::hir::Pat::TupleStruct { ref args, ellipsis, .. } if variant.is_some() => {
let expected_len = variant.unwrap().variant_data(self.db.upcast()).fields().len();
let subpatterns = self.lower_tuple_subpats(args, expected_len, ellipsis);
self.lower_variant_or_leaf(pat, ty, subpatterns)
}
- hir_def::expr::Pat::Record { ref args, .. } if variant.is_some() => {
+ hir_def::hir::Pat::Record { ref args, .. } if variant.is_some() => {
let variant_data = variant.unwrap().variant_data(self.db.upcast());
let subpatterns = args
.iter()
@@ -187,12 +188,12 @@ impl<'a> PatCtxt<'a> {
}
}
}
- hir_def::expr::Pat::TupleStruct { .. } | hir_def::expr::Pat::Record { .. } => {
+ hir_def::hir::Pat::TupleStruct { .. } | hir_def::hir::Pat::Record { .. } => {
self.errors.push(PatternError::UnresolvedVariant);
PatKind::Wild
}
- hir_def::expr::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
+ hir_def::hir::Pat::Or(ref pats) => PatKind::Or { pats: self.lower_patterns(pats) },
_ => {
self.errors.push(PatternError::Unimplemented);
@@ -279,8 +280,8 @@ impl<'a> PatCtxt<'a> {
}
}
- fn lower_lit(&mut self, expr: hir_def::expr::ExprId) -> PatKind {
- use hir_def::expr::{Expr, Literal::Bool};
+ fn lower_lit(&mut self, expr: hir_def::hir::ExprId) -> PatKind {
+ use hir_def::hir::{Expr, Literal::Bool};
match self.body[expr] {
Expr::Literal(Bool(value)) => PatKind::LiteralBool { value },
@@ -297,7 +298,7 @@ impl HirDisplay for Pat {
match &*self.kind {
PatKind::Wild => write!(f, "_"),
PatKind::Binding { name, subpattern } => {
- write!(f, "{name}")?;
+ write!(f, "{}", name.display(f.db.upcast()))?;
if let Some(subpattern) = subpattern {
write!(f, " @ ")?;
subpattern.hir_fmt(f)?;
@@ -318,10 +319,14 @@ impl HirDisplay for Pat {
match variant {
VariantId::EnumVariantId(v) => {
let data = f.db.enum_data(v.parent);
- write!(f, "{}", data.variants[v.local_id].name)?;
+ write!(f, "{}", data.variants[v.local_id].name.display(f.db.upcast()))?;
+ }
+ VariantId::StructId(s) => {
+ write!(f, "{}", f.db.struct_data(s).name.display(f.db.upcast()))?
+ }
+ VariantId::UnionId(u) => {
+ write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast()))?
}
- VariantId::StructId(s) => write!(f, "{}", f.db.struct_data(s).name)?,
- VariantId::UnionId(u) => write!(f, "{}", f.db.union_data(u).name)?,
};
let variant_data = variant.variant_data(f.db.upcast());
@@ -335,7 +340,11 @@ impl HirDisplay for Pat {
.map(|p| {
printed += 1;
WriteWith(move |f| {
- write!(f, "{}: ", rec_fields[p.field].name)?;
+ write!(
+ f,
+ "{}: ",
+ rec_fields[p.field].name.display(f.db.upcast())
+ )?;
p.pattern.hir_fmt(f)
})
});
@@ -379,7 +388,7 @@ impl HirDisplay for Pat {
}
PatKind::Deref { subpattern } => {
match self.ty.kind(Interner) {
- TyKind::Adt(adt, _) if is_box(adt.0, f.db) => write!(f, "box ")?,
+ TyKind::Adt(adt, _) if is_box(f.db, adt.0) => write!(f, "box ")?,
&TyKind::Ref(mutbl, ..) => {
write!(f, "&{}", if mutbl == Mutability::Mut { "mut " } else { "" })?
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs
index d130827a7..a0f6b9368 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs
@@ -82,7 +82,7 @@ fn expand_or_pat(pat: &Pat) -> Vec<&Pat> {
pats
}
-/// [Constructor] uses this in umimplemented variants.
+/// [Constructor] uses this in unimplemented variants.
/// It allows porting match expressions from upstream algorithm without losing semantics.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(super) enum Void {}
@@ -384,7 +384,7 @@ impl Constructor {
TyKind::Tuple(arity, ..) => arity,
TyKind::Ref(..) => 1,
TyKind::Adt(adt, ..) => {
- if is_box(adt.0, pcx.cx.db) {
+ if is_box(pcx.cx.db, adt.0) {
// The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern.
1
@@ -772,7 +772,7 @@ impl<'p> Fields<'p> {
(0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).filter_map(move |fid| {
let ty = field_ty[fid].clone().substitute(Interner, substs);
- let ty = normalize(cx.db, cx.body, ty);
+ let ty = normalize(cx.db, cx.db.trait_environment_for_body(cx.body), ty);
let is_visible = matches!(adt, hir_def::AdtId::EnumId(..))
|| visibility[fid].is_visible_from(cx.db.upcast(), cx.module);
let is_uninhabited = cx.is_uninhabited(&ty);
@@ -800,7 +800,7 @@ impl<'p> Fields<'p> {
}
TyKind::Ref(.., rty) => Fields::wildcards_from_tys(cx, once(rty.clone())),
&TyKind::Adt(AdtId(adt), ref substs) => {
- if is_box(adt, cx.db) {
+ if is_box(cx.db, adt) {
// The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern.
let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
@@ -905,7 +905,7 @@ impl<'p> DeconstructedPat<'p> {
}
fields = Fields::from_iter(cx, wilds)
}
- TyKind::Adt(adt, substs) if is_box(adt.0, cx.db) => {
+ TyKind::Adt(adt, substs) if is_box(cx.db, adt.0) => {
// The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern.
// FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
@@ -992,7 +992,7 @@ impl<'p> DeconstructedPat<'p> {
})
.collect(),
},
- TyKind::Adt(adt, _) if is_box(adt.0, cx.db) => {
+ TyKind::Adt(adt, _) if is_box(cx.db, adt.0) => {
// Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
// of `std`). So this branch is only reachable when the feature is enabled and
// the pattern is a box pattern.
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs
index b89b4f2bf..217454499 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_util.rs
@@ -1,4 +1,4 @@
-//! Pattern untilities.
+//! Pattern utilities.
//!
//! Originates from `rustc_hir::pat_util`
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs
index c4d709a97..d737b24ad 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/usefulness.rs
@@ -755,7 +755,7 @@ pub(crate) enum Reachability {
/// The arm is reachable. This additionally carries a set of or-pattern branches that have been
/// found to be unreachable despite the overall arm being reachable. Used only in the presence
/// of or-patterns, otherwise it stays empty.
- // FIXME: store ureachable subpattern IDs
+ // FIXME: store unreachable subpattern IDs
Reachable,
/// The arm is unreachable.
Unreachable,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index d25c0ccf0..9f9a56ffa 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -3,7 +3,7 @@
use hir_def::{
body::Body,
- expr::{Expr, ExprId, UnaryOp},
+ hir::{Expr, ExprId, UnaryOp},
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
DefWithBodyId,
};
@@ -18,9 +18,10 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
let is_unsafe = match def {
DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
- DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) | DefWithBodyId::VariantId(_) => {
- false
- }
+ DefWithBodyId::StaticId(_)
+ | DefWithBodyId::ConstId(_)
+ | DefWithBodyId::VariantId(_)
+ | DefWithBodyId::InTypeConstId(_) => false,
};
if is_unsafe {
return res;
@@ -73,7 +74,7 @@ fn walk_unsafe(
}
Expr::Path(path) => {
let resolver = resolver_for_expr(db.upcast(), def, current);
- let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path.mod_path());
+ let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial {
if db.static_data(id).mutable {
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index bd3eccfe4..c1df24d17 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -2,37 +2,44 @@
//! HIR back into source code, and just displaying them for debugging/testing
//! purposes.
-use std::fmt::{self, Debug};
+use std::{
+ fmt::{self, Debug},
+ mem::size_of,
+};
use base_db::CrateId;
use chalk_ir::{BoundVar, TyKind};
use hir_def::{
- adt::VariantData,
- body,
+ data::adt::VariantData,
db::DefDatabase,
find_path,
generics::{TypeOrConstParamData, TypeParamProvenance},
item_scope::ItemInNs,
lang_item::{LangItem, LangItemTarget},
+ nameres::DefMap,
path::{Path, PathKind},
type_ref::{TraitBoundModifier, TypeBound, TypeRef},
visibility::Visibility,
- HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId,
+ EnumVariantId, HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId,
+ TraitId,
};
use hir_expand::{hygiene::Hygiene, name::Name};
use intern::{Internable, Interned};
use itertools::Itertools;
+use la_arena::ArenaMap;
use smallvec::SmallVec;
+use stdx::never;
use crate::{
+ consteval::try_const_usize,
db::HirDatabase,
from_assoc_type_id, from_foreign_def_id, from_placeholder_idx,
- layout::layout_of_ty,
+ layout::Layout,
lt_from_placeholder_idx,
mapping::from_chalk,
mir::pad16,
primitive, to_assoc_type_id,
- utils::{self, generics},
+ utils::{self, detect_variant_from_bytes, generics, ClosureSubst},
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstScalar, ConstValue,
DomainGoal, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives,
MemoryMap, Mutability, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Scalar,
@@ -64,6 +71,7 @@ pub struct HirFormatter<'a> {
curr_size: usize,
pub(crate) max_size: Option<usize>,
omit_verbose_types: bool,
+ closure_style: ClosureStyle,
display_target: DisplayTarget,
}
@@ -87,6 +95,7 @@ pub trait HirDisplay {
max_size: Option<usize>,
omit_verbose_types: bool,
display_target: DisplayTarget,
+ closure_style: ClosureStyle,
) -> HirDisplayWrapper<'a, Self>
where
Self: Sized,
@@ -95,7 +104,14 @@ pub trait HirDisplay {
!matches!(display_target, DisplayTarget::SourceCode { .. }),
"HirDisplayWrapper cannot fail with DisplaySourceCodeError, use HirDisplay::hir_fmt directly instead"
);
- HirDisplayWrapper { db, t: self, max_size, omit_verbose_types, display_target }
+ HirDisplayWrapper {
+ db,
+ t: self,
+ max_size,
+ omit_verbose_types,
+ display_target,
+ closure_style,
+ }
}
/// Returns a `Display`able type that is human-readable.
@@ -109,6 +125,7 @@ pub trait HirDisplay {
t: self,
max_size: None,
omit_verbose_types: false,
+ closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::Diagnostics,
}
}
@@ -128,6 +145,7 @@ pub trait HirDisplay {
t: self,
max_size,
omit_verbose_types: true,
+ closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::Diagnostics,
}
}
@@ -138,6 +156,7 @@ pub trait HirDisplay {
&'a self,
db: &'a dyn HirDatabase,
module_id: ModuleId,
+ allow_opaque: bool,
) -> Result<String, DisplaySourceCodeError> {
let mut result = String::new();
match self.hir_fmt(&mut HirFormatter {
@@ -147,7 +166,8 @@ pub trait HirDisplay {
curr_size: 0,
max_size: None,
omit_verbose_types: false,
- display_target: DisplayTarget::SourceCode { module_id },
+ closure_style: ClosureStyle::ImplFn,
+ display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
}) {
Ok(()) => {}
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
@@ -166,6 +186,7 @@ pub trait HirDisplay {
t: self,
max_size: None,
omit_verbose_types: false,
+ closure_style: ClosureStyle::ImplFn,
display_target: DisplayTarget::Test,
}
}
@@ -235,26 +256,34 @@ pub enum DisplayTarget {
Diagnostics,
/// Display types for inserting them in source files.
/// The generated code should compile, so paths need to be qualified.
- SourceCode { module_id: ModuleId },
+ SourceCode { module_id: ModuleId, allow_opaque: bool },
/// Only for test purpose to keep real types
Test,
}
impl DisplayTarget {
- fn is_source_code(&self) -> bool {
+ fn is_source_code(self) -> bool {
matches!(self, Self::SourceCode { .. })
}
- fn is_test(&self) -> bool {
+
+ fn is_test(self) -> bool {
matches!(self, Self::Test)
}
+
+ fn allows_opaque(self) -> bool {
+ match self {
+ Self::SourceCode { allow_opaque, .. } => allow_opaque,
+ _ => true,
+ }
+ }
}
#[derive(Debug)]
pub enum DisplaySourceCodeError {
PathNotFound,
UnknownType,
- Closure,
Generator,
+ OpaqueType,
}
pub enum HirDisplayError {
@@ -274,9 +303,25 @@ pub struct HirDisplayWrapper<'a, T> {
t: &'a T,
max_size: Option<usize>,
omit_verbose_types: bool,
+ closure_style: ClosureStyle,
display_target: DisplayTarget,
}
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum ClosureStyle {
+ /// `impl FnX(i32, i32) -> i32`, where `FnX` is the most special trait between `Fn`, `FnMut`, `FnOnce` that the
+ /// closure implements. This is the default.
+ ImplFn,
+ /// `|i32, i32| -> i32`
+ RANotation,
+ /// `{closure#14825}`, useful for some diagnostics (like type mismatch) and internal usage.
+ ClosureWithId,
+ /// `{closure#14825}<i32, ()>`, useful for internal usage.
+ ClosureWithSubst,
+ /// `…`, which is the `TYPE_HINT_TRUNCATION`
+ Hide,
+}
+
impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
pub fn write_to<F: HirWrite>(&self, f: &mut F) -> Result<(), HirDisplayError> {
self.t.hir_fmt(&mut HirFormatter {
@@ -287,8 +332,14 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
max_size: self.max_size,
omit_verbose_types: self.omit_verbose_types,
display_target: self.display_target,
+ closure_style: self.closure_style,
})
}
+
+ pub fn with_closure_style(mut self, c: ClosureStyle) -> Self {
+ self.closure_style = c;
+ self
+ }
}
impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T>
@@ -330,7 +381,13 @@ impl HirDisplay for ProjectionTy {
let trait_ref = self.trait_ref(f.db);
write!(f, "<")?;
fmt_trait_ref(f, &trait_ref, true)?;
- write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?;
+ write!(
+ f,
+ ">::{}",
+ f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id))
+ .name
+ .display(f.db.upcast())
+ )?;
let proj_params_count =
self.substitution.len(Interner) - trait_ref.substitution.len(Interner);
let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count];
@@ -373,10 +430,16 @@ impl HirDisplay for Const {
let id = from_placeholder_idx(f.db, *idx);
let generics = generics(f.db.upcast(), id.parent);
let param_data = &generics.params.type_or_consts[id.local_id];
- write!(f, "{}", param_data.name().unwrap())
+ write!(f, "{}", param_data.name().unwrap().display(f.db.upcast()))?;
+ Ok(())
}
ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(b, m) => render_const_scalar(f, &b, m, &data.ty),
+ ConstScalar::UnevaluatedConst(c, parameters) => {
+ write!(f, "{}", c.name(f.db.upcast()))?;
+ hir_fmt_generics(f, parameters, c.generic_def(f.db.upcast()))?;
+ Ok(())
+ }
ConstScalar::Unknown => f.write_char('_'),
},
}
@@ -411,8 +474,11 @@ fn render_const_scalar(
memory_map: &MemoryMap,
ty: &Ty,
) -> Result<(), HirDisplayError> {
+ // FIXME: We need to get krate from the final callers of the hir display
+ // infrastructure and have it here as a field on `f`.
+ let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap();
match ty.kind(Interner) {
- chalk_ir::TyKind::Scalar(s) => match s {
+ TyKind::Scalar(s) => match s {
Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }),
Scalar::Char => {
let x = u128::from_le_bytes(pad16(b, false)) as u32;
@@ -440,22 +506,90 @@ fn render_const_scalar(
}
},
},
- chalk_ir::TyKind::Ref(_, _, t) => match t.kind(Interner) {
- chalk_ir::TyKind::Str => {
+ TyKind::Ref(_, _, t) => match t.kind(Interner) {
+ TyKind::Str => {
let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
- let bytes = memory_map.0.get(&addr).map(|x| &**x).unwrap_or(&[]);
- let s = std::str::from_utf8(bytes).unwrap_or("<utf8-error>");
+ let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
+ let Some(bytes) = memory_map.get(addr, size) else {
+ return f.write_str("<ref-data-not-available>");
+ };
+ let s = std::str::from_utf8(&bytes).unwrap_or("<utf8-error>");
write!(f, "{s:?}")
}
- _ => f.write_str("<ref-not-supported>"),
+ TyKind::Slice(ty) => {
+ let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
+ let count = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
+ let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
+ return f.write_str("<layout-error>");
+ };
+ let size_one = layout.size.bytes_usize();
+ let Some(bytes) = memory_map.get(addr, size_one * count) else {
+ return f.write_str("<ref-data-not-available>");
+ };
+ f.write_str("&[")?;
+ let mut first = true;
+ for i in 0..count {
+ if first {
+ first = false;
+ } else {
+ f.write_str(", ")?;
+ }
+ let offset = size_one * i;
+ render_const_scalar(f, &bytes[offset..offset + size_one], memory_map, &ty)?;
+ }
+ f.write_str("]")
+ }
+ TyKind::Dyn(_) => {
+ let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
+ let ty_id = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
+ let Ok(t) = memory_map.vtable.ty(ty_id) else {
+ return f.write_str("<ty-missing-in-vtable-map>");
+ };
+ let Ok(layout) = f.db.layout_of_ty(t.clone(), krate) else {
+ return f.write_str("<layout-error>");
+ };
+ let size = layout.size.bytes_usize();
+ let Some(bytes) = memory_map.get(addr, size) else {
+ return f.write_str("<ref-data-not-available>");
+ };
+ f.write_str("&")?;
+ render_const_scalar(f, bytes, memory_map, t)
+ }
+ TyKind::Adt(adt, _) if b.len() == 2 * size_of::<usize>() => match adt.0 {
+ hir_def::AdtId::StructId(s) => {
+ let data = f.db.struct_data(s);
+ write!(f, "&{}", data.name.display(f.db.upcast()))?;
+ Ok(())
+ }
+ _ => {
+ return f.write_str("<unsized-enum-or-union>");
+ }
+ },
+ _ => {
+ let addr = usize::from_le_bytes(match b.try_into() {
+ Ok(b) => b,
+ Err(_) => {
+ never!(
+ "tried rendering ty {:?} in const ref with incorrect byte count {}",
+ t,
+ b.len()
+ );
+ return f.write_str("<layout-error>");
+ }
+ });
+ let Ok(layout) = f.db.layout_of_ty(t.clone(), krate) else {
+ return f.write_str("<layout-error>");
+ };
+ let size = layout.size.bytes_usize();
+ let Some(bytes) = memory_map.get(addr, size) else {
+ return f.write_str("<ref-data-not-available>");
+ };
+ f.write_str("&")?;
+ render_const_scalar(f, bytes, memory_map, t)
+ }
},
- chalk_ir::TyKind::Tuple(_, subst) => {
- // FIXME: Remove this line. If the target data layout is independent
- // of the krate, the `db.target_data_layout` and its callers like `layout_of_ty` don't need
- // to get krate. Otherwise, we need to get krate from the final callers of the hir display
- // infrastructure and have it here as a field on `f`.
- let krate = *f.db.crate_graph().crates_in_topological_order().last().unwrap();
- let Ok(layout) = layout_of_ty(f.db, ty, krate) else {
+ TyKind::Tuple(_, subst) => {
+ let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
return f.write_str("<layout-error>");
};
f.write_str("(")?;
@@ -468,7 +602,7 @@ fn render_const_scalar(
}
let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
let offset = layout.fields.offset(id).bytes_usize();
- let Ok(layout) = layout_of_ty(f.db, &ty, krate) else {
+ let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
f.write_str("<layout-error>")?;
continue;
};
@@ -477,62 +611,144 @@ fn render_const_scalar(
}
f.write_str(")")
}
- chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
- hir_def::AdtId::StructId(s) => {
- let data = f.db.struct_data(s);
- let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone()) else {
+ TyKind::Adt(adt, subst) => {
+ let Ok(layout) = f.db.layout_of_adt(adt.0, subst.clone(), krate) else {
+ return f.write_str("<layout-error>");
+ };
+ match adt.0 {
+ hir_def::AdtId::StructId(s) => {
+ let data = f.db.struct_data(s);
+ write!(f, "{}", data.name.display(f.db.upcast()))?;
+ let field_types = f.db.field_types(s.into());
+ render_variant_after_name(
+ &data.variant_data,
+ f,
+ &field_types,
+ adt.0.module(f.db.upcast()).krate(),
+ &layout,
+ subst,
+ b,
+ memory_map,
+ )
+ }
+ hir_def::AdtId::UnionId(u) => {
+ write!(f, "{}", f.db.union_data(u).name.display(f.db.upcast()))
+ }
+ hir_def::AdtId::EnumId(e) => {
+ let Some((var_id, var_layout)) =
+ detect_variant_from_bytes(&layout, f.db, krate, b, e) else {
+ return f.write_str("<failed-to-detect-variant>");
+ };
+ let data = &f.db.enum_data(e).variants[var_id];
+ write!(f, "{}", data.name.display(f.db.upcast()))?;
+ let field_types =
+ f.db.field_types(EnumVariantId { parent: e, local_id: var_id }.into());
+ render_variant_after_name(
+ &data.variant_data,
+ f,
+ &field_types,
+ adt.0.module(f.db.upcast()).krate(),
+ &var_layout,
+ subst,
+ b,
+ memory_map,
+ )
+ }
+ }
+ }
+ TyKind::FnDef(..) => ty.hir_fmt(f),
+ TyKind::Function(_) | TyKind::Raw(_, _) => {
+ let x = u128::from_le_bytes(pad16(b, false));
+ write!(f, "{:#X} as ", x)?;
+ ty.hir_fmt(f)
+ }
+ TyKind::Array(ty, len) => {
+ let Some(len) = try_const_usize(f.db, len) else {
+ return f.write_str("<unknown-array-len>");
+ };
+ let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
+ return f.write_str("<layout-error>");
+ };
+ let size_one = layout.size.bytes_usize();
+ f.write_str("[")?;
+ let mut first = true;
+ for i in 0..len as usize {
+ if first {
+ first = false;
+ } else {
+ f.write_str(", ")?;
+ }
+ let offset = size_one * i;
+ render_const_scalar(f, &b[offset..offset + size_one], memory_map, &ty)?;
+ }
+ f.write_str("]")
+ }
+ TyKind::Never => f.write_str("!"),
+ TyKind::Closure(_, _) => f.write_str("<closure>"),
+ TyKind::Generator(_, _) => f.write_str("<generator>"),
+ TyKind::GeneratorWitness(_, _) => f.write_str("<generator-witness>"),
+ // The below arms are unreachable, since const eval will bail out before here.
+ TyKind::Foreign(_) => f.write_str("<extern-type>"),
+ TyKind::Error
+ | TyKind::Placeholder(_)
+ | TyKind::Alias(_)
+ | TyKind::AssociatedType(_, _)
+ | TyKind::OpaqueType(_, _)
+ | TyKind::BoundVar(_)
+ | TyKind::InferenceVar(_, _) => f.write_str("<placeholder-or-unknown-type>"),
+ // The below arms are unreachable, since we handled them in ref case.
+ TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => f.write_str("<unsized-value>"),
+ }
+}
+
+fn render_variant_after_name(
+ data: &VariantData,
+ f: &mut HirFormatter<'_>,
+ field_types: &ArenaMap<LocalFieldId, Binders<Ty>>,
+ krate: CrateId,
+ layout: &Layout,
+ subst: &Substitution,
+ b: &[u8],
+ memory_map: &MemoryMap,
+) -> Result<(), HirDisplayError> {
+ match data {
+ VariantData::Record(fields) | VariantData::Tuple(fields) => {
+ let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| {
+ let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize();
+ let ty = field_types[id].clone().substitute(Interner, subst);
+ let Ok(layout) = f.db.layout_of_ty(ty.clone(), krate) else {
return f.write_str("<layout-error>");
};
- match data.variant_data.as_ref() {
- VariantData::Record(fields) | VariantData::Tuple(fields) => {
- let field_types = f.db.field_types(s.into());
- let krate = adt.0.module(f.db.upcast()).krate();
- let render_field = |f: &mut HirFormatter<'_>, id: LocalFieldId| {
- let offset = layout
- .fields
- .offset(u32::from(id.into_raw()) as usize)
- .bytes_usize();
- let ty = field_types[id].clone().substitute(Interner, subst);
- let Ok(layout) = layout_of_ty(f.db, &ty, krate) else {
- return f.write_str("<layout-error>");
- };
- let size = layout.size.bytes_usize();
- render_const_scalar(f, &b[offset..offset + size], memory_map, &ty)
- };
- let mut it = fields.iter();
- if matches!(data.variant_data.as_ref(), VariantData::Record(_)) {
- write!(f, "{} {{", data.name)?;
- if let Some((id, data)) = it.next() {
- write!(f, " {}: ", data.name)?;
- render_field(f, id)?;
- }
- for (id, data) in it {
- write!(f, ", {}: ", data.name)?;
- render_field(f, id)?;
- }
- write!(f, " }}")?;
- } else {
- let mut it = it.map(|x| x.0);
- write!(f, "{}(", data.name)?;
- if let Some(id) = it.next() {
- render_field(f, id)?;
- }
- for id in it {
- write!(f, ", ")?;
- render_field(f, id)?;
- }
- write!(f, ")")?;
- }
- return Ok(());
- }
- VariantData::Unit => write!(f, "{}", data.name),
+ let size = layout.size.bytes_usize();
+ render_const_scalar(f, &b[offset..offset + size], memory_map, &ty)
+ };
+ let mut it = fields.iter();
+ if matches!(data, VariantData::Record(_)) {
+ write!(f, " {{")?;
+ if let Some((id, data)) = it.next() {
+ write!(f, " {}: ", data.name.display(f.db.upcast()))?;
+ render_field(f, id)?;
+ }
+ for (id, data) in it {
+ write!(f, ", {}: ", data.name.display(f.db.upcast()))?;
+ render_field(f, id)?;
}
+ write!(f, " }}")?;
+ } else {
+ let mut it = it.map(|x| x.0);
+ write!(f, "(")?;
+ if let Some(id) = it.next() {
+ render_field(f, id)?;
+ }
+ for id in it {
+ write!(f, ", ")?;
+ render_field(f, id)?;
+ }
+ write!(f, ")")?;
}
- hir_def::AdtId::UnionId(u) => write!(f, "{}", f.db.union_data(u).name),
- hir_def::AdtId::EnumId(_) => f.write_str("<enum-not-supported>"),
- },
- chalk_ir::TyKind::FnDef(..) => ty.hir_fmt(f),
- _ => f.write_str("<not-supported>"),
+ return Ok(());
+ }
+ VariantData::Unit => Ok(()),
}
}
@@ -689,11 +905,17 @@ impl HirDisplay for Ty {
let sig = db.callable_item_signature(def).substitute(Interner, parameters);
f.start_location_link(def.into());
match def {
- CallableDefId::FunctionId(ff) => write!(f, "fn {}", db.function_data(ff).name)?,
- CallableDefId::StructId(s) => write!(f, "{}", db.struct_data(s).name)?,
- CallableDefId::EnumVariantId(e) => {
- write!(f, "{}", db.enum_data(e.parent).variants[e.local_id].name)?
+ CallableDefId::FunctionId(ff) => {
+ write!(f, "fn {}", db.function_data(ff).name.display(f.db.upcast()))?
+ }
+ CallableDefId::StructId(s) => {
+ write!(f, "{}", db.struct_data(s).name.display(f.db.upcast()))?
}
+ CallableDefId::EnumVariantId(e) => write!(
+ f,
+ "{}",
+ db.enum_data(e.parent).variants[e.local_id].name.display(f.db.upcast())
+ )?,
};
f.end_location_link();
if parameters.len(Interner) > 0 {
@@ -733,16 +955,16 @@ impl HirDisplay for Ty {
hir_def::AdtId::UnionId(it) => db.union_data(it).name.clone(),
hir_def::AdtId::EnumId(it) => db.enum_data(it).name.clone(),
};
- write!(f, "{name}")?;
+ write!(f, "{}", name.display(f.db.upcast()))?;
}
- DisplayTarget::SourceCode { module_id } => {
+ DisplayTarget::SourceCode { module_id, allow_opaque: _ } => {
if let Some(path) = find_path::find_path(
db.upcast(),
ItemInNs::Types((*def_id).into()),
module_id,
false,
) {
- write!(f, "{path}")?;
+ write!(f, "{}", path.display(f.db.upcast()))?;
} else {
return Err(HirDisplayError::DisplaySourceCodeError(
DisplaySourceCodeError::PathNotFound,
@@ -752,82 +974,9 @@ impl HirDisplay for Ty {
}
f.end_location_link();
- if parameters.len(Interner) > 0 {
- let parameters_to_write = if f.display_target.is_source_code()
- || f.omit_verbose_types()
- {
- match self
- .as_generic_def(db)
- .map(|generic_def_id| db.generic_defaults(generic_def_id))
- .filter(|defaults| !defaults.is_empty())
- {
- None => parameters.as_slice(Interner),
- Some(default_parameters) => {
- fn should_show(
- parameter: &GenericArg,
- default_parameters: &[Binders<GenericArg>],
- i: usize,
- parameters: &Substitution,
- ) -> bool {
- if parameter.ty(Interner).map(|x| x.kind(Interner))
- == Some(&TyKind::Error)
- {
- return true;
- }
- if let Some(ConstValue::Concrete(c)) = parameter
- .constant(Interner)
- .map(|x| &x.data(Interner).value)
- {
- if c.interned == ConstScalar::Unknown {
- return true;
- }
- }
- let default_parameter = match default_parameters.get(i) {
- Some(x) => x,
- None => return true,
- };
- let actual_default =
- default_parameter.clone().substitute(Interner, &parameters);
- parameter != &actual_default
- }
- let mut default_from = 0;
- for (i, parameter) in parameters.iter(Interner).enumerate() {
- if should_show(parameter, &default_parameters, i, parameters) {
- default_from = i + 1;
- }
- }
- &parameters.as_slice(Interner)[0..default_from]
- }
- }
- } else {
- parameters.as_slice(Interner)
- };
- if !parameters_to_write.is_empty() {
- write!(f, "<")?;
-
- if f.display_target.is_source_code() {
- let mut first = true;
- for generic_arg in parameters_to_write {
- if !first {
- write!(f, ", ")?;
- }
- first = false;
-
- if generic_arg.ty(Interner).map(|ty| ty.kind(Interner))
- == Some(&TyKind::Error)
- {
- write!(f, "_")?;
- } else {
- generic_arg.hir_fmt(f)?;
- }
- }
- } else {
- f.write_joined(parameters_to_write, ", ")?;
- }
+ let generic_def = self.as_generic_def(db);
- write!(f, ">")?;
- }
- }
+ hir_fmt_generics(f, parameters, generic_def)?;
}
TyKind::AssociatedType(assoc_type_id, parameters) => {
let type_alias = from_assoc_type_id(*assoc_type_id);
@@ -841,12 +990,12 @@ impl HirDisplay for Ty {
// Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types)
if f.display_target.is_test() {
f.start_location_link(trait_.into());
- write!(f, "{}", trait_data.name)?;
+ write!(f, "{}", trait_data.name.display(f.db.upcast()))?;
f.end_location_link();
write!(f, "::")?;
f.start_location_link(type_alias.into());
- write!(f, "{}", type_alias_data.name)?;
+ write!(f, "{}", type_alias_data.name.display(f.db.upcast()))?;
f.end_location_link();
// Note that the generic args for the associated type come before those for the
// trait (including the self type).
@@ -869,10 +1018,15 @@ impl HirDisplay for Ty {
let alias = from_foreign_def_id(*type_alias);
let type_alias = db.type_alias_data(alias);
f.start_location_link(alias.into());
- write!(f, "{}", type_alias.name)?;
+ write!(f, "{}", type_alias.name.display(f.db.upcast()))?;
f.end_location_link();
}
TyKind::OpaqueType(opaque_ty_id, parameters) => {
+ if !f.display_target.allows_opaque() {
+ return Err(HirDisplayError::DisplaySourceCodeError(
+ DisplaySourceCodeError::OpaqueType,
+ ));
+ }
let impl_trait_id = db.lookup_intern_impl_trait_id((*opaque_ty_id).into());
match impl_trait_id {
ImplTraitId::ReturnTypeImplTrait(func, idx) => {
@@ -919,26 +1073,52 @@ impl HirDisplay for Ty {
}
}
}
- TyKind::Closure(.., substs) => {
+ TyKind::Closure(id, substs) => {
if f.display_target.is_source_code() {
- return Err(HirDisplayError::DisplaySourceCodeError(
- DisplaySourceCodeError::Closure,
- ));
+ if !f.display_target.allows_opaque() {
+ return Err(HirDisplayError::DisplaySourceCodeError(
+ DisplaySourceCodeError::OpaqueType,
+ ));
+ } else if f.closure_style != ClosureStyle::ImplFn {
+ never!("Only `impl Fn` is valid for displaying closures in source code");
+ }
}
- let sig = substs.at(Interner, 0).assert_ty_ref(Interner).callable_sig(db);
+ match f.closure_style {
+ ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),
+ ClosureStyle::ClosureWithId => {
+ return write!(f, "{{closure#{:?}}}", id.0.as_u32())
+ }
+ ClosureStyle::ClosureWithSubst => {
+ write!(f, "{{closure#{:?}}}", id.0.as_u32())?;
+ return hir_fmt_generics(f, substs, None);
+ }
+ _ => (),
+ }
+ let sig = ClosureSubst(substs).sig_ty().callable_sig(db);
if let Some(sig) = sig {
+ let (def, _) = db.lookup_intern_closure((*id).into());
+ let infer = db.infer(def);
+ let (_, kind) = infer.closure_info(id);
+ match f.closure_style {
+ ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?,
+ ClosureStyle::RANotation => write!(f, "|")?,
+ _ => unreachable!(),
+ }
if sig.params().is_empty() {
- write!(f, "||")?;
} else if f.should_truncate() {
- write!(f, "|{TYPE_HINT_TRUNCATION}|")?;
+ write!(f, "{TYPE_HINT_TRUNCATION}")?;
} else {
- write!(f, "|")?;
f.write_joined(sig.params(), ", ")?;
- write!(f, "|")?;
};
-
- write!(f, " -> ")?;
- sig.ret().hir_fmt(f)?;
+ match f.closure_style {
+ ClosureStyle::ImplFn => write!(f, ")")?,
+ ClosureStyle::RANotation => write!(f, "|")?,
+ _ => unreachable!(),
+ }
+ if f.closure_style == ClosureStyle::RANotation || !sig.ret().is_unit() {
+ write!(f, " -> ")?;
+ sig.ret().hir_fmt(f)?;
+ }
} else {
write!(f, "{{closure}}")?;
}
@@ -950,7 +1130,11 @@ impl HirDisplay for Ty {
match param_data {
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
- write!(f, "{}", p.name.clone().unwrap_or_else(Name::missing))?
+ write!(
+ f,
+ "{}",
+ p.name.clone().unwrap_or_else(Name::missing).display(f.db.upcast())
+ )?
}
TypeParamProvenance::ArgumentImplTrait => {
let substs = generics.placeholder_subst(db);
@@ -979,7 +1163,7 @@ impl HirDisplay for Ty {
}
},
TypeOrConstParamData::ConstParamData(p) => {
- write!(f, "{}", p.name)?;
+ write!(f, "{}", p.name.display(f.db.upcast()))?;
}
}
}
@@ -1004,6 +1188,11 @@ impl HirDisplay for Ty {
}
TyKind::Alias(AliasTy::Projection(p_ty)) => p_ty.hir_fmt(f)?,
TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
+ if !f.display_target.allows_opaque() {
+ return Err(HirDisplayError::DisplaySourceCodeError(
+ DisplaySourceCodeError::OpaqueType,
+ ));
+ }
let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.opaque_ty_id.into());
match impl_trait_id {
ImplTraitId::ReturnTypeImplTrait(func, idx) => {
@@ -1067,6 +1256,88 @@ impl HirDisplay for Ty {
}
}
+fn hir_fmt_generics(
+ f: &mut HirFormatter<'_>,
+ parameters: &Substitution,
+ generic_def: Option<hir_def::GenericDefId>,
+) -> Result<(), HirDisplayError> {
+ let db = f.db;
+ let lifetime_args_count = generic_def.map_or(0, |g| db.generic_params(g).lifetimes.len());
+ if parameters.len(Interner) + lifetime_args_count > 0 {
+ let parameters_to_write = if f.display_target.is_source_code() || f.omit_verbose_types() {
+ match generic_def
+ .map(|generic_def_id| db.generic_defaults(generic_def_id))
+ .filter(|defaults| !defaults.is_empty())
+ {
+ None => parameters.as_slice(Interner),
+ Some(default_parameters) => {
+ fn should_show(
+ parameter: &GenericArg,
+ default_parameters: &[Binders<GenericArg>],
+ i: usize,
+ parameters: &Substitution,
+ ) -> bool {
+ if parameter.ty(Interner).map(|x| x.kind(Interner)) == Some(&TyKind::Error)
+ {
+ return true;
+ }
+ if let Some(ConstValue::Concrete(c)) =
+ parameter.constant(Interner).map(|x| &x.data(Interner).value)
+ {
+ if c.interned == ConstScalar::Unknown {
+ return true;
+ }
+ }
+ let default_parameter = match default_parameters.get(i) {
+ Some(x) => x,
+ None => return true,
+ };
+ let actual_default =
+ default_parameter.clone().substitute(Interner, &parameters);
+ parameter != &actual_default
+ }
+ let mut default_from = 0;
+ for (i, parameter) in parameters.iter(Interner).enumerate() {
+ if should_show(parameter, &default_parameters, i, parameters) {
+ default_from = i + 1;
+ }
+ }
+ &parameters.as_slice(Interner)[0..default_from]
+ }
+ }
+ } else {
+ parameters.as_slice(Interner)
+ };
+ if !parameters_to_write.is_empty() || lifetime_args_count != 0 {
+ write!(f, "<")?;
+ let mut first = true;
+ for _ in 0..lifetime_args_count {
+ if !first {
+ write!(f, ", ")?;
+ }
+ first = false;
+ write!(f, "'_")?;
+ }
+ for generic_arg in parameters_to_write {
+ if !first {
+ write!(f, ", ")?;
+ }
+ first = false;
+ if f.display_target.is_source_code()
+ && generic_arg.ty(Interner).map(|ty| ty.kind(Interner)) == Some(&TyKind::Error)
+ {
+ write!(f, "_")?;
+ } else {
+ generic_arg.hir_fmt(f)?;
+ }
+ }
+
+ write!(f, ">")?;
+ }
+ }
+ Ok(())
+}
+
impl HirDisplay for CallableSig {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write!(f, "fn(")?;
@@ -1170,7 +1441,7 @@ fn write_bounds_like_dyn_trait(
// existential) here, which is the only thing that's
// possible in actual Rust, and hence don't print it
f.start_location_link(trait_.into());
- write!(f, "{}", f.db.trait_data(trait_).name)?;
+ write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
f.end_location_link();
if let [_, params @ ..] = &*trait_ref.substitution.as_slice(Interner) {
if is_fn_trait {
@@ -1209,7 +1480,7 @@ fn write_bounds_like_dyn_trait(
let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id);
let type_alias = f.db.type_alias_data(assoc_ty_id);
f.start_location_link(assoc_ty_id.into());
- write!(f, "{}", type_alias.name)?;
+ write!(f, "{}", type_alias.name.display(f.db.upcast()))?;
f.end_location_link();
let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self();
@@ -1276,7 +1547,7 @@ fn fmt_trait_ref(
}
let trait_ = tr.hir_trait_id();
f.start_location_link(trait_.into());
- write!(f, "{}", f.db.trait_data(trait_).name)?;
+ write!(f, "{}", f.db.trait_data(trait_).name.display(f.db.upcast()))?;
f.end_location_link();
if tr.substitution.len(Interner) > 1 {
write!(f, "<")?;
@@ -1306,7 +1577,7 @@ impl HirDisplay for WhereClause {
write!(f, ">::",)?;
let type_alias = from_assoc_type_id(projection_ty.associated_ty_id);
f.start_location_link(type_alias.into());
- write!(f, "{}", f.db.type_alias_data(type_alias).name,)?;
+ write!(f, "{}", f.db.type_alias_data(type_alias).name.display(f.db.upcast()),)?;
f.end_location_link();
write!(f, " = ")?;
ty.hir_fmt(f)?;
@@ -1344,7 +1615,8 @@ impl HirDisplay for LifetimeData {
let id = lt_from_placeholder_idx(f.db, *idx);
let generics = generics(f.db.upcast(), id.parent);
let param_data = &generics.params.lifetimes[id.local_id];
- write!(f, "{}", param_data.name)
+ write!(f, "{}", param_data.name.display(f.db.upcast()))?;
+ Ok(())
}
LifetimeData::Static => write!(f, "'static"),
LifetimeData::Erased => Ok(()),
@@ -1376,7 +1648,7 @@ pub fn write_visibility(
Visibility::Public => write!(f, "pub "),
Visibility::Module(vis_id) => {
let def_map = module_id.def_map(f.db.upcast());
- let root_module_id = def_map.module_id(def_map.root());
+ let root_module_id = def_map.module_id(DefMap::ROOT);
if vis_id == module_id {
// pub(self) or omitted
Ok(())
@@ -1420,7 +1692,7 @@ impl HirDisplay for TypeRef {
};
write!(f, "&")?;
if let Some(lifetime) = lifetime {
- write!(f, "{} ", lifetime.name)?;
+ write!(f, "{} ", lifetime.name.display(f.db.upcast()))?;
}
write!(f, "{mutability}")?;
inner.hir_fmt(f)?;
@@ -1428,7 +1700,7 @@ impl HirDisplay for TypeRef {
TypeRef::Array(inner, len) => {
write!(f, "[")?;
inner.hir_fmt(f)?;
- write!(f, "; {len}]")?;
+ write!(f, "; {}]", len.display(f.db.upcast()))?;
}
TypeRef::Slice(inner) => {
write!(f, "[")?;
@@ -1445,7 +1717,7 @@ impl HirDisplay for TypeRef {
for index in 0..function_parameters.len() {
let (param_name, param_type) = &function_parameters[index];
if let Some(name) = param_name {
- write!(f, "{name}: ")?;
+ write!(f, "{}: ", name.display(f.db.upcast()))?;
}
param_type.hir_fmt(f)?;
@@ -1477,7 +1749,10 @@ impl HirDisplay for TypeRef {
}
TypeRef::Macro(macro_call) => {
let macro_call = macro_call.to_node(f.db.upcast());
- let ctx = body::LowerCtx::with_hygiene(f.db.upcast(), &Hygiene::new_unhygienic());
+ let ctx = hir_def::lower::LowerCtx::with_hygiene(
+ f.db.upcast(),
+ &Hygiene::new_unhygienic(),
+ );
match macro_call.path() {
Some(path) => match Path::from_src(path, &ctx) {
Some(path) => path.hir_fmt(f)?,
@@ -1503,9 +1778,13 @@ impl HirDisplay for TypeBound {
}
path.hir_fmt(f)
}
- TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name),
+ TypeBound::Lifetime(lifetime) => write!(f, "{}", lifetime.name.display(f.db.upcast())),
TypeBound::ForLifetime(lifetimes, path) => {
- write!(f, "for<{}> ", lifetimes.iter().format(", "))?;
+ write!(
+ f,
+ "for<{}> ",
+ lifetimes.iter().map(|it| it.display(f.db.upcast())).format(", ")
+ )?;
path.hir_fmt(f)
}
TypeBound::Error => write!(f, "{{error}}"),
@@ -1551,7 +1830,7 @@ impl HirDisplay for Path {
if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 {
write!(f, "::")?;
}
- write!(f, "{}", segment.name)?;
+ write!(f, "{}", segment.name.display(f.db.upcast()))?;
if let Some(generic_args) = segment.args_and_bindings {
// We should be in type context, so format as `Foo<Bar>` instead of `Foo::<Bar>`.
// Do we actually format expressions?
@@ -1598,7 +1877,7 @@ impl HirDisplay for Path {
} else {
write!(f, ", ")?;
}
- write!(f, "{}", binding.name)?;
+ write!(f, "{}", binding.name.display(f.db.upcast()))?;
match &binding.type_ref {
Some(ty) => {
write!(f, " = ")?;
@@ -1621,8 +1900,10 @@ impl HirDisplay for hir_def::path::GenericArg {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
match self {
hir_def::path::GenericArg::Type(ty) => ty.hir_fmt(f),
- hir_def::path::GenericArg::Const(c) => write!(f, "{c}"),
- hir_def::path::GenericArg::Lifetime(lifetime) => write!(f, "{}", lifetime.name),
+ hir_def::path::GenericArg::Const(c) => write!(f, "{}", c.display(f.db.upcast())),
+ hir_def::path::GenericArg::Lifetime(lifetime) => {
+ write!(f, "{}", lifetime.name.display(f.db.upcast()))
+ }
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 7de5b4295..1ac0837b5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -13,34 +13,43 @@
//! to certain types. To record this, we use the union-find implementation from
//! the `ena` crate, which is extracted from rustc.
-use std::ops::Index;
-use std::sync::Arc;
+use std::{convert::identity, ops::Index};
-use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
+use chalk_ir::{
+ cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety,
+ Scalar, TyKind, TypeFlags,
+};
use either::Either;
use hir_def::{
body::Body,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
data::{ConstData, StaticData},
- expr::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
+ hir::LabelId,
+ hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, PatId},
lang_item::{LangItem, LangItemTarget},
layout::Integer,
- path::Path,
+ path::{ModPath, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::TypeRef,
- AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
- ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
+ AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, ItemContainerId, Lookup,
+ TraitId, TypeAliasId, VariantId,
};
use hir_expand::name::{name, Name};
-use la_arena::ArenaMap;
+use la_arena::{ArenaMap, Entry};
use rustc_hash::{FxHashMap, FxHashSet};
-use stdx::always;
+use stdx::{always, never};
+use triomphe::Arc;
use crate::{
- db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany,
- lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
- GenericArg, Goal, ImplTraitId, InEnvironment, Interner, ProjectionTy, RpitId, Substitution,
- TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
+ db::HirDatabase,
+ fold_tys,
+ infer::coerce::CoerceMany,
+ lower::ImplTraitLoweringMode,
+ static_lifetime, to_assoc_type_id,
+ traits::FnTrait,
+ utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
+ AliasEq, AliasTy, ClosureId, DomainGoal, GenericArg, Goal, ImplTraitId, InEnvironment,
+ Interner, ProjectionTy, RpitId, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt,
};
// This lint has a false positive here. See the link below for details.
@@ -51,12 +60,15 @@ pub use coerce::could_coerce;
#[allow(unreachable_pub)]
pub use unify::could_unify;
+pub(crate) use self::closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
+
pub(crate) mod unify;
mod path;
mod expr;
mod pat;
mod coerce;
-mod closure;
+pub(crate) mod closure;
+mod mutability;
/// The entry point of type inference.
pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> {
@@ -95,10 +107,24 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
},
});
}
+ DefWithBodyId::InTypeConstId(c) => {
+ // FIXME(const-generic-body): We should not get the return type in this way.
+ ctx.return_ty = c
+ .lookup(db.upcast())
+ .thing
+ .box_any()
+ .downcast::<InTypeConstIdMetadata>()
+ .unwrap()
+ .0;
+ }
}
ctx.infer_body();
+ ctx.infer_mut_body();
+
+ ctx.infer_closures();
+
Arc::new(ctx.resolve_all())
}
@@ -106,14 +132,15 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
///
/// This is appropriate to use only after type-check: it assumes
/// that normalization will succeed, for example.
-pub(crate) fn normalize(db: &dyn HirDatabase, owner: DefWithBodyId, ty: Ty) -> Ty {
- if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION) {
+pub(crate) fn normalize(db: &dyn HirDatabase, trait_env: Arc<TraitEnvironment>, ty: Ty) -> Ty {
+ // FIXME: TypeFlags::HAS_CT_PROJECTION is not implemented in chalk, so TypeFlags::HAS_PROJECTION only
+ // works for the type case, so we check array unconditionally. Remove the array part
+ // when the bug in chalk becomes fixed.
+ if !ty.data(Interner).flags.intersects(TypeFlags::HAS_PROJECTION)
+ && !matches!(ty.kind(Interner), TyKind::Array(..))
+ {
return ty;
}
- let krate = owner.module(db.upcast()).krate();
- let trait_env = owner
- .as_generic_def_id()
- .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
let mut table = unify::InferenceTable::new(db, trait_env);
let ty_with_vars = table.normalize_associated_types_in(ty);
@@ -188,7 +215,7 @@ pub enum InferenceDiagnostic {
/// Contains the type the field resolves to
field_with_same_name: Option<Ty>,
},
- // FIXME: Make this proper
+ // FIXME: This should be emitted in body lowering
BreakOutsideOfLoop {
expr: ExprId,
is_break: bool,
@@ -203,6 +230,10 @@ pub enum InferenceDiagnostic {
call_expr: ExprId,
found: Ty,
},
+ TypedHole {
+ expr: ExprId,
+ expected: Ty,
+ },
}
/// A mismatch between an expected and an inferred type.
@@ -276,6 +307,13 @@ pub struct Adjustment {
pub target: Ty,
}
+impl Adjustment {
+ pub fn borrow(m: Mutability, ty: Ty) -> Self {
+ let ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
+ Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty }
+ }
+}
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Adjust {
/// Go from ! to any type.
@@ -304,6 +342,13 @@ pub enum AutoBorrow {
RawPtr(Mutability),
}
+impl AutoBorrow {
+ fn mutability(self) -> Mutability {
+ let (AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m)) = self;
+ m
+ }
+}
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PointerCast {
/// Go from a fn-item type to a fn-pointer type.
@@ -337,6 +382,10 @@ pub enum PointerCast {
}
/// The result of type inference: A mapping from expressions and patterns to types.
+///
+/// When you add a field that stores types (including `Substitution` and the like), don't forget
+/// `resolve_completely()`'ing them in `InferenceContext::resolve_all()`. Inference variables must
+/// not appear in the final inference result.
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct InferenceResult {
/// For each method call expr, records the function it resolves to.
@@ -363,8 +412,11 @@ pub struct InferenceResult {
standard_types: InternedStandardTypes,
/// Stores the types which were implicitly dereferenced in pattern binding modes.
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
- pub pat_binding_modes: FxHashMap<PatId, BindingMode>,
+ pub binding_modes: ArenaMap<BindingId, BindingMode>,
pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>,
+ pub(crate) closure_info: FxHashMap<ClosureId, (Vec<CapturedItem>, FnTrait)>,
+ // FIXME: remove this field
+ pub mutated_bindings_in_closure: FxHashSet<BindingId>,
}
impl InferenceResult {
@@ -401,6 +453,9 @@ impl InferenceResult {
_ => None,
})
}
+ pub fn closure_info(&self, closure: &ClosureId) -> &(Vec<CapturedItem>, FnTrait) {
+ self.closure_info.get(closure).unwrap()
+ }
}
impl Index<ExprId> for InferenceResult {
@@ -435,7 +490,6 @@ pub(crate) struct InferenceContext<'a> {
pub(crate) body: &'a Body,
pub(crate) resolver: Resolver,
table: unify::InferenceTable<'a>,
- trait_env: Arc<TraitEnvironment>,
/// The traits in scope, disregarding block modules. This is used for caching purposes.
traits_in_scope: FxHashSet<TraitId>,
pub(crate) result: InferenceResult,
@@ -453,6 +507,14 @@ pub(crate) struct InferenceContext<'a> {
resume_yield_tys: Option<(Ty, Ty)>,
diverges: Diverges,
breakables: Vec<BreakableContext>,
+
+ // fields related to closure capture
+ current_captures: Vec<CapturedItemWithoutTy>,
+ current_closure: Option<ClosureId>,
+ /// Stores the list of closure ids that need to be analyzed before this closure. See the
+ /// comment on `InferenceContext::sort_closures`
+ closure_dependencies: FxHashMap<ClosureId, Vec<ClosureId>>,
+ deferred_closures: FxHashMap<ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>,
}
#[derive(Clone, Debug)]
@@ -462,7 +524,7 @@ struct BreakableContext {
/// The coercion target of the context.
coerce: Option<CoerceMany>,
/// The optional label of the context.
- label: Option<name::Name>,
+ label: Option<LabelId>,
kind: BreakableKind,
}
@@ -477,21 +539,21 @@ enum BreakableKind {
fn find_breakable<'c>(
ctxs: &'c mut [BreakableContext],
- label: Option<&name::Name>,
+ label: Option<LabelId>,
) -> Option<&'c mut BreakableContext> {
let mut ctxs = ctxs
.iter_mut()
.rev()
.take_while(|it| matches!(it.kind, BreakableKind::Block | BreakableKind::Loop));
match label {
- Some(_) => ctxs.find(|ctx| ctx.label.as_ref() == label),
+ Some(_) => ctxs.find(|ctx| ctx.label == label),
None => ctxs.find(|ctx| matches!(ctx.kind, BreakableKind::Loop)),
}
}
fn find_continuable<'c>(
ctxs: &'c mut [BreakableContext],
- label: Option<&name::Name>,
+ label: Option<LabelId>,
) -> Option<&'c mut BreakableContext> {
match label {
Some(_) => find_breakable(ctxs, label).filter(|it| matches!(it.kind, BreakableKind::Loop)),
@@ -506,14 +568,10 @@ impl<'a> InferenceContext<'a> {
body: &'a Body,
resolver: Resolver,
) -> Self {
- let krate = owner.module(db.upcast()).krate();
- let trait_env = owner
- .as_generic_def_id()
- .map_or_else(|| Arc::new(TraitEnvironment::empty(krate)), |d| db.trait_environment(d));
+ let trait_env = db.trait_environment_for_body(owner);
InferenceContext {
result: InferenceResult::default(),
- table: unify::InferenceTable::new(db, trait_env.clone()),
- trait_env,
+ table: unify::InferenceTable::new(db, trait_env),
return_ty: TyKind::Error.intern(Interner), // set in collect_* calls
resume_yield_tys: None,
return_coercion: None,
@@ -524,6 +582,10 @@ impl<'a> InferenceContext<'a> {
resolver,
diverges: Diverges::Maybe,
breakables: Vec::new(),
+ current_captures: vec![],
+ current_closure: None,
+ deferred_closures: FxHashMap::default(),
+ closure_dependencies: FxHashMap::default(),
}
}
@@ -533,6 +595,30 @@ impl<'a> InferenceContext<'a> {
// there is no problem in it being `pub(crate)`, remove this comment.
pub(crate) fn resolve_all(self) -> InferenceResult {
let InferenceContext { mut table, mut result, .. } = self;
+ // Destructure every single field so whenever new fields are added to `InferenceResult` we
+ // don't forget to handle them here.
+ let InferenceResult {
+ method_resolutions,
+ field_resolutions: _,
+ variant_resolutions: _,
+ assoc_resolutions,
+ diagnostics,
+ type_of_expr,
+ type_of_pat,
+ type_of_binding,
+ type_of_rpit,
+ type_of_for_iterator,
+ type_mismatches,
+ standard_types: _,
+ pat_adjustments,
+ binding_modes: _,
+ expr_adjustments,
+ // Types in `closure_info` have already been `resolve_completely()`'d during
+ // `InferenceContext::infer_closures()` (in `HirPlace::ty()` specifically), so no need
+ // to resolve them here.
+ closure_info: _,
+ mutated_bindings_in_closure: _,
+ } = &mut result;
table.fallback_if_possible();
@@ -541,62 +627,63 @@ impl<'a> InferenceContext<'a> {
// make sure diverging type variables are marked as such
table.propagate_diverging_flag();
- for ty in result.type_of_expr.values_mut() {
+ for ty in type_of_expr.values_mut() {
*ty = table.resolve_completely(ty.clone());
}
- for ty in result.type_of_pat.values_mut() {
+ for ty in type_of_pat.values_mut() {
*ty = table.resolve_completely(ty.clone());
}
- for ty in result.type_of_binding.values_mut() {
+ for ty in type_of_binding.values_mut() {
*ty = table.resolve_completely(ty.clone());
}
- for ty in result.type_of_rpit.values_mut() {
+ for ty in type_of_rpit.values_mut() {
*ty = table.resolve_completely(ty.clone());
}
- for ty in result.type_of_for_iterator.values_mut() {
+ for ty in type_of_for_iterator.values_mut() {
*ty = table.resolve_completely(ty.clone());
}
- for mismatch in result.type_mismatches.values_mut() {
+ for mismatch in type_mismatches.values_mut() {
mismatch.expected = table.resolve_completely(mismatch.expected.clone());
mismatch.actual = table.resolve_completely(mismatch.actual.clone());
}
- result.diagnostics.retain_mut(|diagnostic| {
- if let InferenceDiagnostic::ExpectedFunction { found: ty, .. }
- | InferenceDiagnostic::UnresolvedField { receiver: ty, .. }
- | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic
- {
- *ty = table.resolve_completely(ty.clone());
- // FIXME: Remove this when we are on par with rustc in terms of inference
- if ty.contains_unknown() {
- return false;
- }
+ diagnostics.retain_mut(|diagnostic| {
+ use InferenceDiagnostic::*;
+ match diagnostic {
+ ExpectedFunction { found: ty, .. }
+ | UnresolvedField { receiver: ty, .. }
+ | UnresolvedMethodCall { receiver: ty, .. } => {
+ *ty = table.resolve_completely(ty.clone());
+ // FIXME: Remove this when we are on par with rustc in terms of inference
+ if ty.contains_unknown() {
+ return false;
+ }
- if let InferenceDiagnostic::UnresolvedMethodCall { field_with_same_name, .. } =
- diagnostic
- {
- let clear = if let Some(ty) = field_with_same_name {
- *ty = table.resolve_completely(ty.clone());
- ty.contains_unknown()
- } else {
- false
- };
- if clear {
- *field_with_same_name = None;
+ if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic {
+ if let Some(ty) = field_with_same_name {
+ *ty = table.resolve_completely(ty.clone());
+ if ty.contains_unknown() {
+ *field_with_same_name = None;
+ }
+ }
}
}
+ TypedHole { expected: ty, .. } => {
+ *ty = table.resolve_completely(ty.clone());
+ }
+ _ => (),
}
true
});
- for (_, subst) in result.method_resolutions.values_mut() {
+ for (_, subst) in method_resolutions.values_mut() {
*subst = table.resolve_completely(subst.clone());
}
- for (_, subst) in result.assoc_resolutions.values_mut() {
+ for (_, subst) in assoc_resolutions.values_mut() {
*subst = table.resolve_completely(subst.clone());
}
- for adjustment in result.expr_adjustments.values_mut().flatten() {
+ for adjustment in expr_adjustments.values_mut().flatten() {
adjustment.target = table.resolve_completely(adjustment.target.clone());
}
- for adjustment in result.pat_adjustments.values_mut().flatten() {
+ for adjustment in pat_adjustments.values_mut().flatten() {
*adjustment = table.resolve_completely(adjustment.clone());
}
result
@@ -612,10 +699,10 @@ impl<'a> InferenceContext<'a> {
fn collect_fn(&mut self, func: FunctionId) {
let data = self.db.function_data(func);
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
+ let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, func.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Param);
let mut param_tys =
- data.params.iter().map(|(_, type_ref)| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
+ data.params.iter().map(|type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>();
// Check if function contains a va_list, if it does then we append it to the parameter types
// that are collected from the function data
if data.is_varargs() {
@@ -634,14 +721,9 @@ impl<'a> InferenceContext<'a> {
self.infer_top_pat(*pat, &ty);
}
- let error_ty = &TypeRef::Error;
- let return_ty = if data.has_async_kw() {
- data.async_ret_type.as_deref().unwrap_or(error_ty)
- } else {
- &*data.ret_type
- };
+ let return_ty = &*data.ret_type;
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver)
+ let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
let return_ty = ctx.lower_ty(return_ty);
let return_ty = self.insert_type_vars(return_ty);
@@ -649,36 +731,16 @@ impl<'a> InferenceContext<'a> {
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
// RPIT opaque types use substitution of their parent function.
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
- fold_tys(
- return_ty,
- |ty, _| {
- let opaque_ty_id = match ty.kind(Interner) {
- TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
- _ => return ty,
- };
- let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
- ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
- _ => unreachable!(),
- };
- let bounds = (*rpits).map_ref(|rpits| {
- rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter())
- });
- let var = self.table.new_type_var();
- let var_subst = Substitution::from1(Interner, var.clone());
- for bound in bounds {
- let predicate =
- bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
- let (var_predicate, binders) = predicate
- .substitute(Interner, &var_subst)
- .into_value_and_skipped_binders();
- always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
- self.push_obligation(var_predicate.cast(Interner));
- }
- self.result.type_of_rpit.insert(idx, var.clone());
- var
- },
- DebruijnIndex::INNERMOST,
- )
+ let result =
+ self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders);
+ let rpits = rpits.skip_binders();
+ for (id, _) in rpits.impl_traits.iter() {
+ if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
+ never!("Missed RPIT in `insert_inference_vars_for_rpit`");
+ e.insert(TyKind::Error.intern(Interner));
+ }
+ }
+ result
} else {
return_ty
};
@@ -687,6 +749,50 @@ impl<'a> InferenceContext<'a> {
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
}
+ fn insert_inference_vars_for_rpit<T>(
+ &mut self,
+ t: T,
+ rpits: Arc<chalk_ir::Binders<crate::ReturnTypeImplTraits>>,
+ fn_placeholders: Substitution,
+ ) -> T
+ where
+ T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
+ {
+ fold_tys(
+ t,
+ |ty, _| {
+ let opaque_ty_id = match ty.kind(Interner) {
+ TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
+ _ => return ty,
+ };
+ let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
+ ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
+ _ => unreachable!(),
+ };
+ let bounds = (*rpits)
+ .map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.into_iter()));
+ let var = self.table.new_type_var();
+ let var_subst = Substitution::from1(Interner, var.clone());
+ for bound in bounds {
+ let predicate =
+ bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
+ let (var_predicate, binders) =
+ predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
+ always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
+ let var_predicate = self.insert_inference_vars_for_rpit(
+ var_predicate,
+ rpits.clone(),
+ fn_placeholders.clone(),
+ );
+ self.push_obligation(var_predicate.cast(Interner));
+ }
+ self.result.type_of_rpit.insert(idx, var.clone());
+ var
+ },
+ DebruijnIndex::INNERMOST,
+ )
+ }
+
fn infer_body(&mut self) {
match self.return_coercion {
Some(_) => self.infer_return(self.body.body_expr),
@@ -732,7 +838,7 @@ impl<'a> InferenceContext<'a> {
}
fn make_ty(&mut self, type_ref: &TypeRef) -> Ty {
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
+ let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
let ty = ctx.lower_ty(type_ref);
let ty = self.insert_type_vars(ty);
self.normalize_associated_types_in(ty)
@@ -742,43 +848,16 @@ impl<'a> InferenceContext<'a> {
self.result.standard_types.unknown.clone()
}
- /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
- fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
- let data = c.data(Interner);
- match &data.value {
- ConstValue::Concrete(cc) => match cc.interned {
- crate::ConstScalar::Unknown => self.table.new_const_var(data.ty.clone()),
- _ => c,
- },
- _ => c,
- }
- }
-
/// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
- match ty.kind(Interner) {
- TyKind::Error => self.table.new_type_var(),
- TyKind::InferenceVar(..) => {
- let ty_resolved = self.resolve_ty_shallow(&ty);
- if ty_resolved.is_unknown() {
- self.table.new_type_var()
- } else {
- ty
- }
- }
- _ => ty,
- }
+ self.table.insert_type_vars_shallow(ty)
}
- fn insert_type_vars(&mut self, ty: Ty) -> Ty {
- fold_tys_and_consts(
- ty,
- |x, _| match x {
- Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)),
- Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)),
- },
- DebruijnIndex::INNERMOST,
- )
+ fn insert_type_vars<T>(&mut self, ty: T) -> T
+ where
+ T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
+ {
+ self.table.insert_type_vars(ty)
}
fn push_obligation(&mut self, o: DomainGoal) {
@@ -786,7 +865,80 @@ impl<'a> InferenceContext<'a> {
}
fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
- self.table.unify(ty1, ty2)
+ let ty1 = ty1
+ .clone()
+ .try_fold_with(
+ &mut UnevaluatedConstEvaluatorFolder { db: self.db },
+ DebruijnIndex::INNERMOST,
+ )
+ .unwrap();
+ let ty2 = ty2
+ .clone()
+ .try_fold_with(
+ &mut UnevaluatedConstEvaluatorFolder { db: self.db },
+ DebruijnIndex::INNERMOST,
+ )
+ .unwrap();
+ self.table.unify(&ty1, &ty2)
+ }
+
+ /// Attempts to returns the deeply last field of nested structures, but
+ /// does not apply any normalization in its search. Returns the same type
+ /// if input `ty` is not a structure at all.
+ fn struct_tail_without_normalization(&mut self, ty: Ty) -> Ty {
+ self.struct_tail_with_normalize(ty, identity)
+ }
+
+ /// Returns the deeply last field of nested structures, or the same type if
+ /// not a structure at all. Corresponds to the only possible unsized field,
+ /// and its type can be used to determine unsizing strategy.
+ ///
+ /// This is parameterized over the normalization strategy (i.e. how to
+ /// handle `<T as Trait>::Assoc` and `impl Trait`); pass the identity
+ /// function to indicate no normalization should take place.
+ fn struct_tail_with_normalize(
+ &mut self,
+ mut ty: Ty,
+ mut normalize: impl FnMut(Ty) -> Ty,
+ ) -> Ty {
+ // FIXME: fetch the limit properly
+ let recursion_limit = 10;
+ for iteration in 0.. {
+ if iteration > recursion_limit {
+ return self.err_ty();
+ }
+ match ty.kind(Interner) {
+ TyKind::Adt(chalk_ir::AdtId(hir_def::AdtId::StructId(struct_id)), substs) => {
+ match self.db.field_types((*struct_id).into()).values().next_back().cloned() {
+ Some(field) => {
+ ty = field.substitute(Interner, substs);
+ }
+ None => break,
+ }
+ }
+ TyKind::Adt(..) => break,
+ TyKind::Tuple(_, substs) => {
+ match substs
+ .as_slice(Interner)
+ .split_last()
+ .and_then(|(last_ty, _)| last_ty.ty(Interner))
+ {
+ Some(last_ty) => ty = last_ty.clone(),
+ None => break,
+ }
+ }
+ TyKind::Alias(..) => {
+ let normalized = normalize(ty.clone());
+ if ty == normalized {
+ return ty;
+ } else {
+ ty = normalized;
+ }
+ }
+ _ => break,
+ }
+ }
+ ty
}
/// Recurses through the given type, normalizing associated types mentioned
@@ -795,7 +947,10 @@ impl<'a> InferenceContext<'a> {
/// type annotation (e.g. from a let type annotation, field type or function
/// call). `make_ty` handles this already, but e.g. for field types we need
/// to do it as well.
- fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
+ fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
+ where
+ T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
+ {
self.table.normalize_associated_types_in(ty)
}
@@ -847,11 +1002,9 @@ impl<'a> InferenceContext<'a> {
Some(path) => path,
None => return (self.err_ty(), None),
};
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
- // FIXME: this should resolve assoc items as well, see this example:
- // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
+ let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
let (resolution, unresolved) = if value_ns {
- match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path()) {
+ match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
Some(ResolveValueResult::ValueNs(value)) => match value {
ValueNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
@@ -872,11 +1025,15 @@ impl<'a> InferenceContext<'a> {
None => return (self.err_ty(), None),
}
} else {
- match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
+ match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
Some(it) => it,
None => return (self.err_ty(), None),
}
};
+ let Some(mod_path) = path.mod_path() else {
+ never!("resolver should always resolve lang item paths");
+ return (self.err_ty(), None);
+ };
return match resolution {
TypeNs::AdtId(AdtId::StructId(strukt)) => {
let substs = ctx.substs_from_path(path, strukt.into(), true);
@@ -899,8 +1056,68 @@ impl<'a> InferenceContext<'a> {
TypeNs::SelfType(impl_id) => {
let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
let substs = generics.placeholder_subst(self.db);
- let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
- self.resolve_variant_on_alias(ty, unresolved, path)
+ let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
+
+ let Some(mut remaining_idx) = unresolved else {
+ return self.resolve_variant_on_alias(ty, None, mod_path);
+ };
+
+ let mut remaining_segments = path.segments().skip(remaining_idx);
+
+ // We need to try resolving unresolved segments one by one because each may resolve
+ // to a projection, which `TyLoweringContext` cannot handle on its own.
+ while !remaining_segments.is_empty() {
+ let resolved_segment = path.segments().get(remaining_idx - 1).unwrap();
+ let current_segment = remaining_segments.take(1);
+
+ // If we can resolve to an enum variant, it takes priority over associated type
+ // of the same name.
+ if let Some((AdtId::EnumId(id), _)) = ty.as_adt() {
+ let enum_data = self.db.enum_data(id);
+ let name = current_segment.first().unwrap().name;
+ if let Some(local_id) = enum_data.variant(name) {
+ let variant = EnumVariantId { parent: id, local_id };
+ return if remaining_segments.len() == 1 {
+ (ty, Some(variant.into()))
+ } else {
+ // We still have unresolved paths, but enum variants never have
+ // associated types!
+ (self.err_ty(), None)
+ };
+ }
+ }
+
+ // `lower_partly_resolved_path()` returns `None` as type namespace unless
+ // `remaining_segments` is empty, which is never the case here. We don't know
+ // which namespace the new `ty` is in until normalized anyway.
+ (ty, _) = ctx.lower_partly_resolved_path(
+ resolution,
+ resolved_segment,
+ current_segment,
+ false,
+ );
+
+ ty = self.table.insert_type_vars(ty);
+ ty = self.table.normalize_associated_types_in(ty);
+ ty = self.table.resolve_ty_shallow(&ty);
+ if ty.is_unknown() {
+ return (self.err_ty(), None);
+ }
+
+ // FIXME(inherent_associated_types): update `resolution` based on `ty` here.
+ remaining_idx += 1;
+ remaining_segments = remaining_segments.skip(1);
+ }
+
+ let variant = ty.as_adt().and_then(|(id, _)| match id {
+ AdtId::StructId(s) => Some(VariantId::StructId(s)),
+ AdtId::UnionId(u) => Some(VariantId::UnionId(u)),
+ AdtId::EnumId(_) => {
+ // FIXME Error E0071, expected struct, variant or union type, found enum `Foo`
+ None
+ }
+ });
+ (ty, variant)
}
TypeNs::TypeAliasId(it) => {
let container = it.lookup(self.db.upcast()).container;
@@ -917,7 +1134,7 @@ impl<'a> InferenceContext<'a> {
let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst)
.fill_with_inference_vars(&mut self.table)
.build();
- self.resolve_variant_on_alias(ty, unresolved, path)
+ self.resolve_variant_on_alias(ty, unresolved, mod_path)
}
TypeNs::AdtSelfType(_) => {
// FIXME this could happen in array size expressions, once we're checking them
@@ -953,9 +1170,9 @@ impl<'a> InferenceContext<'a> {
&mut self,
ty: Ty,
unresolved: Option<usize>,
- path: &Path,
+ path: &ModPath,
) -> (Ty, Option<VariantId>) {
- let remaining = unresolved.map(|x| path.segments().skip(x).len()).filter(|x| x > &0);
+ let remaining = unresolved.map(|x| path.segments()[x..].len()).filter(|x| x > &0);
match remaining {
None => {
let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
@@ -969,7 +1186,7 @@ impl<'a> InferenceContext<'a> {
(ty, variant)
}
Some(1) => {
- let segment = path.mod_path().segments().last().unwrap();
+ let segment = path.segments().last().unwrap();
// this could be an enum variant or associated type
if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
let enum_data = self.db.enum_data(enum_id);
@@ -993,22 +1210,6 @@ impl<'a> InferenceContext<'a> {
self.db.lang_item(krate, item)
}
- fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
- let ItemContainerId::TraitId(trait_) = self.resolve_lang_item(LangItem::IntoIterIntoIter)?
- .as_function()?
- .lookup(self.db.upcast()).container
- else { return None };
- self.db.trait_data(trait_).associated_type_by_name(&name![IntoIter])
- }
-
- fn resolve_iterator_item(&self) -> Option<TypeAliasId> {
- let ItemContainerId::TraitId(trait_) = self.resolve_lang_item(LangItem::IteratorNext)?
- .as_function()?
- .lookup(self.db.upcast()).container
- else { return None };
- self.db.trait_data(trait_).associated_type_by_name(&name![Item])
- }
-
fn resolve_output_on(&self, trait_: TraitId) -> Option<TypeAliasId> {
self.db.trait_data(trait_).associated_type_by_name(&name![Output])
}
@@ -1017,10 +1218,6 @@ impl<'a> InferenceContext<'a> {
self.resolve_lang_item(lang)?.as_trait()
}
- fn resolve_ops_try_output(&self) -> Option<TypeAliasId> {
- self.resolve_output_on(self.resolve_lang_trait(LangItem::Try)?)
- }
-
fn resolve_ops_neg_output(&self) -> Option<TypeAliasId> {
self.resolve_output_on(self.resolve_lang_trait(LangItem::Neg)?)
}
@@ -1136,9 +1333,8 @@ impl Expectation {
/// which still is useful, because it informs integer literals and the like.
/// See the test case `test/ui/coerce-expect-unsized.rs` and #20169
/// for examples of where this comes up,.
- fn rvalue_hint(table: &mut unify::InferenceTable<'_>, ty: Ty) -> Self {
- // FIXME: do struct_tail_without_normalization
- match table.resolve_ty_shallow(&ty).kind(Interner) {
+ fn rvalue_hint(ctx: &mut InferenceContext<'_>, ty: Ty) -> Self {
+ match ctx.struct_tail_without_normalization(ty.clone()).kind(Interner) {
TyKind::Slice(_) | TyKind::Str | TyKind::Dyn(_) => Expectation::RValueLikeUnsized(ty),
_ => Expectation::has_type(ty),
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
index a6449d019..ff64ae252 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
@@ -1,12 +1,33 @@
//! Inference of closure parameter types based on the closure's expected type.
-use chalk_ir::{cast::Cast, AliasEq, AliasTy, FnSubst, WhereClause};
-use hir_def::{expr::ExprId, HasModule};
+use std::{cmp, collections::HashMap, convert::Infallible, mem};
+
+use chalk_ir::{
+ cast::Cast,
+ fold::{FallibleTypeFolder, TypeFoldable},
+ AliasEq, AliasTy, BoundVar, DebruijnIndex, FnSubst, Mutability, TyKind, WhereClause,
+};
+use hir_def::{
+ data::adt::VariantData,
+ hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp},
+ lang_item::LangItem,
+ resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
+ DefWithBodyId, FieldId, HasModule, VariantId,
+};
+use hir_expand::name;
+use rustc_hash::FxHashMap;
use smallvec::SmallVec;
+use stdx::never;
use crate::{
- to_chalk_trait_id, utils, ChalkTraitId, DynTy, FnPointer, FnSig, Interner, Substitution, Ty,
- TyExt, TyKind,
+ db::HirDatabase,
+ from_placeholder_idx, make_binders,
+ mir::{BorrowKind, MirSpan, ProjectionElem},
+ static_lifetime, to_chalk_trait_id,
+ traits::FnTrait,
+ utils::{self, generics, Generics},
+ Adjust, Adjustment, Binders, BindingMode, ChalkTraitId, ClosureId, DynTy, FnPointer, FnSig,
+ Interner, Substitution, Ty, TyExt,
};
use super::{Expectation, InferenceContext};
@@ -86,3 +107,905 @@ impl InferenceContext<'_> {
None
}
}
+
+// The below functions handle capture and closure kind (Fn, FnMut, ..)
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub(crate) struct HirPlace {
+ pub(crate) local: BindingId,
+ pub(crate) projections: Vec<ProjectionElem<Infallible, Ty>>,
+}
+
+impl HirPlace {
+ fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty {
+ let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone());
+ for p in &self.projections {
+ ty = p.projected_ty(
+ ty,
+ ctx.db,
+ |_, _, _| {
+ unreachable!("Closure field only happens in MIR");
+ },
+ ctx.owner.module(ctx.db.upcast()).krate(),
+ );
+ }
+ ty.clone()
+ }
+
+ fn capture_kind_of_truncated_place(
+ &self,
+ mut current_capture: CaptureKind,
+ len: usize,
+ ) -> CaptureKind {
+ match current_capture {
+ CaptureKind::ByRef(BorrowKind::Mut { .. }) => {
+ if self.projections[len..].iter().any(|x| *x == ProjectionElem::Deref) {
+ current_capture = CaptureKind::ByRef(BorrowKind::Unique);
+ }
+ }
+ _ => (),
+ }
+ current_capture
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum CaptureKind {
+ ByRef(BorrowKind),
+ ByValue,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct CapturedItem {
+ pub(crate) place: HirPlace,
+ pub(crate) kind: CaptureKind,
+ pub(crate) span: MirSpan,
+ pub(crate) ty: Binders<Ty>,
+}
+
+impl CapturedItem {
+ pub fn local(&self) -> BindingId {
+ self.place.local
+ }
+
+ pub fn ty(&self, subst: &Substitution) -> Ty {
+ self.ty.clone().substitute(Interner, utils::ClosureSubst(subst).parent_subst())
+ }
+
+ pub fn kind(&self) -> CaptureKind {
+ self.kind
+ }
+
+ pub fn display_place(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String {
+ let body = db.body(owner);
+ let mut result = body[self.place.local].name.display(db.upcast()).to_string();
+ let mut field_need_paren = false;
+ for proj in &self.place.projections {
+ match proj {
+ ProjectionElem::Deref => {
+ result = format!("*{result}");
+ field_need_paren = true;
+ }
+ ProjectionElem::Field(f) => {
+ if field_need_paren {
+ result = format!("({result})");
+ }
+ let variant_data = f.parent.variant_data(db.upcast());
+ let field = match &*variant_data {
+ VariantData::Record(fields) => fields[f.local_id]
+ .name
+ .as_str()
+ .unwrap_or("[missing field]")
+ .to_string(),
+ VariantData::Tuple(fields) => fields
+ .iter()
+ .position(|x| x.0 == f.local_id)
+ .unwrap_or_default()
+ .to_string(),
+ VariantData::Unit => "[missing field]".to_string(),
+ };
+ result = format!("{result}.{field}");
+ field_need_paren = false;
+ }
+ &ProjectionElem::TupleOrClosureField(field) => {
+ if field_need_paren {
+ result = format!("({result})");
+ }
+ result = format!("{result}.{field}");
+ field_need_paren = false;
+ }
+ ProjectionElem::Index(_)
+ | ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Subslice { .. }
+ | ProjectionElem::OpaqueCast(_) => {
+ never!("Not happen in closure capture");
+ continue;
+ }
+ }
+ }
+ result
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct CapturedItemWithoutTy {
+ pub(crate) place: HirPlace,
+ pub(crate) kind: CaptureKind,
+ pub(crate) span: MirSpan,
+}
+
+impl CapturedItemWithoutTy {
+ fn with_ty(self, ctx: &mut InferenceContext<'_>) -> CapturedItem {
+ let ty = self.place.ty(ctx).clone();
+ let ty = match &self.kind {
+ CaptureKind::ByValue => ty,
+ CaptureKind::ByRef(bk) => {
+ let m = match bk {
+ BorrowKind::Mut { .. } => Mutability::Mut,
+ _ => Mutability::Not,
+ };
+ TyKind::Ref(m, static_lifetime(), ty).intern(Interner)
+ }
+ };
+ return CapturedItem {
+ place: self.place,
+ kind: self.kind,
+ span: self.span,
+ ty: replace_placeholder_with_binder(ctx.db, ctx.owner, ty),
+ };
+
+ fn replace_placeholder_with_binder(
+ db: &dyn HirDatabase,
+ owner: DefWithBodyId,
+ ty: Ty,
+ ) -> Binders<Ty> {
+ struct Filler<'a> {
+ db: &'a dyn HirDatabase,
+ generics: Generics,
+ }
+ impl FallibleTypeFolder<Interner> for Filler<'_> {
+ type Error = ();
+
+ fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
+ self
+ }
+
+ fn interner(&self) -> Interner {
+ Interner
+ }
+
+ fn try_fold_free_placeholder_const(
+ &mut self,
+ ty: chalk_ir::Ty<Interner>,
+ idx: chalk_ir::PlaceholderIndex,
+ outer_binder: DebruijnIndex,
+ ) -> Result<chalk_ir::Const<Interner>, Self::Error> {
+ let x = from_placeholder_idx(self.db, idx);
+ let Some(idx) = self.generics.param_idx(x) else {
+ return Err(());
+ };
+ Ok(BoundVar::new(outer_binder, idx).to_const(Interner, ty))
+ }
+
+ fn try_fold_free_placeholder_ty(
+ &mut self,
+ idx: chalk_ir::PlaceholderIndex,
+ outer_binder: DebruijnIndex,
+ ) -> std::result::Result<Ty, Self::Error> {
+ let x = from_placeholder_idx(self.db, idx);
+ let Some(idx) = self.generics.param_idx(x) else {
+ return Err(());
+ };
+ Ok(BoundVar::new(outer_binder, idx).to_ty(Interner))
+ }
+ }
+ let Some(generic_def) = owner.as_generic_def_id() else {
+ return Binders::empty(Interner, ty);
+ };
+ let filler = &mut Filler { db, generics: generics(db.upcast(), generic_def) };
+ let result = ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST).unwrap_or(ty);
+ make_binders(db, &filler.generics, result)
+ }
+ }
+}
+
+impl InferenceContext<'_> {
+ fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
+ let r = self.place_of_expr_without_adjust(tgt_expr)?;
+ let default = vec![];
+ let adjustments = self.result.expr_adjustments.get(&tgt_expr).unwrap_or(&default);
+ apply_adjusts_to_place(r, adjustments)
+ }
+
+ fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
+ match &self.body[tgt_expr] {
+ Expr::Path(p) => {
+ let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
+ if let Some(r) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) {
+ if let ResolveValueResult::ValueNs(v) = r {
+ if let ValueNs::LocalBinding(b) = v {
+ return Some(HirPlace { local: b, projections: vec![] });
+ }
+ }
+ }
+ }
+ Expr::Field { expr, name } => {
+ let mut place = self.place_of_expr(*expr)?;
+ if let TyKind::Tuple(..) = self.expr_ty(*expr).kind(Interner) {
+ let index = name.as_tuple_index()?;
+ place.projections.push(ProjectionElem::TupleOrClosureField(index))
+ } else {
+ let field = self.result.field_resolution(tgt_expr)?;
+ place.projections.push(ProjectionElem::Field(field));
+ }
+ return Some(place);
+ }
+ Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
+ if matches!(
+ self.expr_ty_after_adjustments(*expr).kind(Interner),
+ TyKind::Ref(..) | TyKind::Raw(..)
+ ) {
+ let mut place = self.place_of_expr(*expr)?;
+ place.projections.push(ProjectionElem::Deref);
+ return Some(place);
+ }
+ }
+ _ => (),
+ }
+ None
+ }
+
+ fn push_capture(&mut self, capture: CapturedItemWithoutTy) {
+ self.current_captures.push(capture);
+ }
+
+ fn ref_expr(&mut self, expr: ExprId) {
+ if let Some(place) = self.place_of_expr(expr) {
+ self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared), expr.into());
+ }
+ self.walk_expr(expr);
+ }
+
+ fn add_capture(&mut self, place: HirPlace, kind: CaptureKind, span: MirSpan) {
+ if self.is_upvar(&place) {
+ self.push_capture(CapturedItemWithoutTy { place, kind, span });
+ }
+ }
+
+ fn mutate_expr(&mut self, expr: ExprId) {
+ if let Some(place) = self.place_of_expr(expr) {
+ self.add_capture(
+ place,
+ CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false }),
+ expr.into(),
+ );
+ }
+ self.walk_expr(expr);
+ }
+
+ fn consume_expr(&mut self, expr: ExprId) {
+ if let Some(place) = self.place_of_expr(expr) {
+ self.consume_place(place, expr.into());
+ }
+ self.walk_expr(expr);
+ }
+
+ fn consume_place(&mut self, place: HirPlace, span: MirSpan) {
+ if self.is_upvar(&place) {
+ let ty = place.ty(self).clone();
+ let kind = if self.is_ty_copy(ty) {
+ CaptureKind::ByRef(BorrowKind::Shared)
+ } else {
+ CaptureKind::ByValue
+ };
+ self.push_capture(CapturedItemWithoutTy { place, kind, span });
+ }
+ }
+
+ fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) {
+ if let Some((last, rest)) = adjustment.split_last() {
+ match last.kind {
+ Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => {
+ self.walk_expr_with_adjust(tgt_expr, rest)
+ }
+ Adjust::Deref(Some(m)) => match m.0 {
+ Some(m) => {
+ self.ref_capture_with_adjusts(m, tgt_expr, rest);
+ }
+ None => unreachable!(),
+ },
+ Adjust::Borrow(b) => {
+ self.ref_capture_with_adjusts(b.mutability(), tgt_expr, rest);
+ }
+ }
+ } else {
+ self.walk_expr_without_adjust(tgt_expr);
+ }
+ }
+
+ fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) {
+ let capture_kind = match m {
+ Mutability::Mut => {
+ CaptureKind::ByRef(BorrowKind::Mut { allow_two_phase_borrow: false })
+ }
+ Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared),
+ };
+ if let Some(place) = self.place_of_expr_without_adjust(tgt_expr) {
+ if let Some(place) = apply_adjusts_to_place(place, rest) {
+ self.add_capture(place, capture_kind, tgt_expr.into());
+ }
+ }
+ self.walk_expr_with_adjust(tgt_expr, rest);
+ }
+
+ fn walk_expr(&mut self, tgt_expr: ExprId) {
+ if let Some(x) = self.result.expr_adjustments.get_mut(&tgt_expr) {
+ // FIXME: this take is completely unneeded, and just is here to make borrow checker
+ // happy. Remove it if you can.
+ let x_taken = mem::take(x);
+ self.walk_expr_with_adjust(tgt_expr, &x_taken);
+ *self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken;
+ } else {
+ self.walk_expr_without_adjust(tgt_expr);
+ }
+ }
+
+ fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
+ match &self.body[tgt_expr] {
+ Expr::If { condition, then_branch, else_branch } => {
+ self.consume_expr(*condition);
+ self.consume_expr(*then_branch);
+ if let &Some(expr) = else_branch {
+ self.consume_expr(expr);
+ }
+ }
+ Expr::Async { statements, tail, .. }
+ | Expr::Unsafe { statements, tail, .. }
+ | Expr::Block { statements, tail, .. } => {
+ for s in statements.iter() {
+ match s {
+ Statement::Let { pat, type_ref: _, initializer, else_branch } => {
+ if let Some(else_branch) = else_branch {
+ self.consume_expr(*else_branch);
+ if let Some(initializer) = initializer {
+ self.consume_expr(*initializer);
+ }
+ return;
+ }
+ if let Some(initializer) = initializer {
+ self.walk_expr(*initializer);
+ if let Some(place) = self.place_of_expr(*initializer) {
+ self.consume_with_pat(place, *pat);
+ }
+ }
+ }
+ Statement::Expr { expr, has_semi: _ } => {
+ self.consume_expr(*expr);
+ }
+ }
+ }
+ if let Some(tail) = tail {
+ self.consume_expr(*tail);
+ }
+ }
+ Expr::While { condition, body, label: _ } => {
+ self.consume_expr(*condition);
+ self.consume_expr(*body);
+ }
+ Expr::Call { callee, args, is_assignee_expr: _ } => {
+ self.consume_expr(*callee);
+ self.consume_exprs(args.iter().copied());
+ }
+ Expr::MethodCall { receiver, args, .. } => {
+ self.consume_expr(*receiver);
+ self.consume_exprs(args.iter().copied());
+ }
+ Expr::Match { expr, arms } => {
+ for arm in arms.iter() {
+ self.consume_expr(arm.expr);
+ if let Some(guard) = arm.guard {
+ self.consume_expr(guard);
+ }
+ }
+ self.walk_expr(*expr);
+ if let Some(discr_place) = self.place_of_expr(*expr) {
+ if self.is_upvar(&discr_place) {
+ let mut capture_mode = None;
+ for arm in arms.iter() {
+ self.walk_pat(&mut capture_mode, arm.pat);
+ }
+ if let Some(c) = capture_mode {
+ self.push_capture(CapturedItemWithoutTy {
+ place: discr_place,
+ kind: c,
+ span: (*expr).into(),
+ })
+ }
+ }
+ }
+ }
+ Expr::Break { expr, label: _ }
+ | Expr::Return { expr }
+ | Expr::Yield { expr }
+ | Expr::Yeet { expr } => {
+ if let &Some(expr) = expr {
+ self.consume_expr(expr);
+ }
+ }
+ Expr::RecordLit { fields, spread, .. } => {
+ if let &Some(expr) = spread {
+ self.consume_expr(expr);
+ }
+ self.consume_exprs(fields.iter().map(|x| x.expr));
+ }
+ Expr::Field { expr, name: _ } => self.select_from_expr(*expr),
+ Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
+ if matches!(
+ self.expr_ty_after_adjustments(*expr).kind(Interner),
+ TyKind::Ref(..) | TyKind::Raw(..)
+ ) {
+ self.select_from_expr(*expr);
+ } else if let Some((f, _)) = self.result.method_resolution(tgt_expr) {
+ let mutability = 'b: {
+ if let Some(deref_trait) =
+ self.resolve_lang_item(LangItem::DerefMut).and_then(|x| x.as_trait())
+ {
+ if let Some(deref_fn) =
+ self.db.trait_data(deref_trait).method_by_name(&name![deref_mut])
+ {
+ break 'b deref_fn == f;
+ }
+ }
+ false
+ };
+ if mutability {
+ self.mutate_expr(*expr);
+ } else {
+ self.ref_expr(*expr);
+ }
+ } else {
+ self.select_from_expr(*expr);
+ }
+ }
+ Expr::UnaryOp { expr, op: _ }
+ | Expr::Array(Array::Repeat { initializer: expr, repeat: _ })
+ | Expr::Await { expr }
+ | Expr::Loop { body: expr, label: _ }
+ | Expr::Let { pat: _, expr }
+ | Expr::Box { expr }
+ | Expr::Cast { expr, type_ref: _ } => {
+ self.consume_expr(*expr);
+ }
+ Expr::Ref { expr, rawness: _, mutability } => match mutability {
+ hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr),
+ hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr),
+ },
+ Expr::BinaryOp { lhs, rhs, op } => {
+ let Some(op) = op else {
+ return;
+ };
+ if matches!(op, BinaryOp::Assignment { .. }) {
+ self.mutate_expr(*lhs);
+ self.consume_expr(*rhs);
+ return;
+ }
+ self.consume_expr(*lhs);
+ self.consume_expr(*rhs);
+ }
+ Expr::Range { lhs, rhs, range_type: _ } => {
+ if let &Some(expr) = lhs {
+ self.consume_expr(expr);
+ }
+ if let &Some(expr) = rhs {
+ self.consume_expr(expr);
+ }
+ }
+ Expr::Index { base, index } => {
+ self.select_from_expr(*base);
+ self.consume_expr(*index);
+ }
+ Expr::Closure { .. } => {
+ let ty = self.expr_ty(tgt_expr);
+ let TyKind::Closure(id, _) = ty.kind(Interner) else {
+ never!("closure type is always closure");
+ return;
+ };
+ let (captures, _) =
+ self.result.closure_info.get(id).expect(
+ "We sort closures, so we should always have data for inner closures",
+ );
+ let mut cc = mem::take(&mut self.current_captures);
+ cc.extend(captures.iter().filter(|x| self.is_upvar(&x.place)).map(|x| {
+ CapturedItemWithoutTy { place: x.place.clone(), kind: x.kind, span: x.span }
+ }));
+ self.current_captures = cc;
+ }
+ Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ })
+ | Expr::Tuple { exprs, is_assignee_expr: _ } => {
+ self.consume_exprs(exprs.iter().copied())
+ }
+ Expr::Missing
+ | Expr::Continue { .. }
+ | Expr::Path(_)
+ | Expr::Literal(_)
+ | Expr::Const(_)
+ | Expr::Underscore => (),
+ }
+ }
+
+ fn walk_pat(&mut self, result: &mut Option<CaptureKind>, pat: PatId) {
+ let mut update_result = |ck: CaptureKind| match result {
+ Some(r) => {
+ *r = cmp::max(*r, ck);
+ }
+ None => *result = Some(ck),
+ };
+
+ self.walk_pat_inner(
+ pat,
+ &mut update_result,
+ BorrowKind::Mut { allow_two_phase_borrow: false },
+ );
+ }
+
+ fn walk_pat_inner(
+ &mut self,
+ p: PatId,
+ update_result: &mut impl FnMut(CaptureKind),
+ mut for_mut: BorrowKind,
+ ) {
+ match &self.body[p] {
+ Pat::Ref { .. }
+ | Pat::Box { .. }
+ | Pat::Missing
+ | Pat::Wild
+ | Pat::Tuple { .. }
+ | Pat::Or(_) => (),
+ Pat::TupleStruct { .. } | Pat::Record { .. } => {
+ if let Some(variant) = self.result.variant_resolution_for_pat(p) {
+ let adt = variant.adt_id();
+ let is_multivariant = match adt {
+ hir_def::AdtId::EnumId(e) => self.db.enum_data(e).variants.len() != 1,
+ _ => false,
+ };
+ if is_multivariant {
+ update_result(CaptureKind::ByRef(BorrowKind::Shared));
+ }
+ }
+ }
+ Pat::Slice { .. }
+ | Pat::ConstBlock(_)
+ | Pat::Path(_)
+ | Pat::Lit(_)
+ | Pat::Range { .. } => {
+ update_result(CaptureKind::ByRef(BorrowKind::Shared));
+ }
+ Pat::Bind { id, .. } => match self.result.binding_modes[*id] {
+ crate::BindingMode::Move => {
+ if self.is_ty_copy(self.result.type_of_binding[*id].clone()) {
+ update_result(CaptureKind::ByRef(BorrowKind::Shared));
+ } else {
+ update_result(CaptureKind::ByValue);
+ }
+ }
+ crate::BindingMode::Ref(r) => match r {
+ Mutability::Mut => update_result(CaptureKind::ByRef(for_mut)),
+ Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)),
+ },
+ },
+ }
+ if self.result.pat_adjustments.get(&p).map_or(false, |x| !x.is_empty()) {
+ for_mut = BorrowKind::Unique;
+ }
+ self.body.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut));
+ }
+
+ fn expr_ty(&self, expr: ExprId) -> Ty {
+ self.result[expr].clone()
+ }
+
+ fn expr_ty_after_adjustments(&self, e: ExprId) -> Ty {
+ let mut ty = None;
+ if let Some(x) = self.result.expr_adjustments.get(&e) {
+ if let Some(x) = x.last() {
+ ty = Some(x.target.clone());
+ }
+ }
+ ty.unwrap_or_else(|| self.expr_ty(e))
+ }
+
+ fn is_upvar(&self, place: &HirPlace) -> bool {
+ if let Some(c) = self.current_closure {
+ let (_, root) = self.db.lookup_intern_closure(c.into());
+ return self.body.is_binding_upvar(place.local, root);
+ }
+ false
+ }
+
+ fn is_ty_copy(&mut self, ty: Ty) -> bool {
+ if let TyKind::Closure(id, _) = ty.kind(Interner) {
+ // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We
+ // should probably let chalk know which closures are copy, but I don't know how doing it
+ // without creating query cycles.
+ return self.result.closure_info.get(id).map(|x| x.1 == FnTrait::Fn).unwrap_or(true);
+ }
+ self.table.resolve_completely(ty).is_copy(self.db, self.owner)
+ }
+
+ fn select_from_expr(&mut self, expr: ExprId) {
+ self.walk_expr(expr);
+ }
+
+ fn adjust_for_move_closure(&mut self) {
+ for capture in &mut self.current_captures {
+ if let Some(first_deref) =
+ capture.place.projections.iter().position(|proj| *proj == ProjectionElem::Deref)
+ {
+ capture.place.projections.truncate(first_deref);
+ }
+ capture.kind = CaptureKind::ByValue;
+ }
+ }
+
+ fn minimize_captures(&mut self) {
+ self.current_captures.sort_by_key(|x| x.place.projections.len());
+ let mut hash_map = HashMap::<HirPlace, usize>::new();
+ let result = mem::take(&mut self.current_captures);
+ for item in result {
+ let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] };
+ let mut it = item.place.projections.iter();
+ let prev_index = loop {
+ if let Some(k) = hash_map.get(&lookup_place) {
+ break Some(*k);
+ }
+ match it.next() {
+ Some(x) => lookup_place.projections.push(x.clone()),
+ None => break None,
+ }
+ };
+ match prev_index {
+ Some(p) => {
+ let len = self.current_captures[p].place.projections.len();
+ let kind_after_truncate =
+ item.place.capture_kind_of_truncated_place(item.kind, len);
+ self.current_captures[p].kind =
+ cmp::max(kind_after_truncate, self.current_captures[p].kind);
+ }
+ None => {
+ hash_map.insert(item.place.clone(), self.current_captures.len());
+ self.current_captures.push(item);
+ }
+ }
+ }
+ }
+
+ fn consume_with_pat(&mut self, mut place: HirPlace, pat: PatId) {
+ let cnt = self.result.pat_adjustments.get(&pat).map(|x| x.len()).unwrap_or_default();
+ place.projections = place
+ .projections
+ .iter()
+ .cloned()
+ .chain((0..cnt).map(|_| ProjectionElem::Deref))
+ .collect::<Vec<_>>()
+ .into();
+ match &self.body[pat] {
+ Pat::Missing | Pat::Wild => (),
+ Pat::Tuple { args, ellipsis } => {
+ let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
+ let field_count = match self.result[pat].kind(Interner) {
+ TyKind::Tuple(_, s) => s.len(Interner),
+ _ => return,
+ };
+ let fields = 0..field_count;
+ let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
+ for (arg, i) in it {
+ let mut p = place.clone();
+ p.projections.push(ProjectionElem::TupleOrClosureField(i));
+ self.consume_with_pat(p, *arg);
+ }
+ }
+ Pat::Or(pats) => {
+ for pat in pats.iter() {
+ self.consume_with_pat(place.clone(), *pat);
+ }
+ }
+ Pat::Record { args, .. } => {
+ let Some(variant) = self.result.variant_resolution_for_pat(pat) else {
+ return;
+ };
+ match variant {
+ VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
+ self.consume_place(place, pat.into())
+ }
+ VariantId::StructId(s) => {
+ let vd = &*self.db.struct_data(s).variant_data;
+ for field_pat in args.iter() {
+ let arg = field_pat.pat;
+ let Some(local_id) = vd.field(&field_pat.name) else {
+ continue;
+ };
+ let mut p = place.clone();
+ p.projections.push(ProjectionElem::Field(FieldId {
+ parent: variant.into(),
+ local_id,
+ }));
+ self.consume_with_pat(p, arg);
+ }
+ }
+ }
+ }
+ Pat::Range { .. }
+ | Pat::Slice { .. }
+ | Pat::ConstBlock(_)
+ | Pat::Path(_)
+ | Pat::Lit(_) => self.consume_place(place, pat.into()),
+ Pat::Bind { id, subpat: _ } => {
+ let mode = self.result.binding_modes[*id];
+ let capture_kind = match mode {
+ BindingMode::Move => {
+ self.consume_place(place, pat.into());
+ return;
+ }
+ BindingMode::Ref(Mutability::Not) => BorrowKind::Shared,
+ BindingMode::Ref(Mutability::Mut) => {
+ BorrowKind::Mut { allow_two_phase_borrow: false }
+ }
+ };
+ self.add_capture(place, CaptureKind::ByRef(capture_kind), pat.into());
+ }
+ Pat::TupleStruct { path: _, args, ellipsis } => {
+ let Some(variant) = self.result.variant_resolution_for_pat(pat) else {
+ return;
+ };
+ match variant {
+ VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
+ self.consume_place(place, pat.into())
+ }
+ VariantId::StructId(s) => {
+ let vd = &*self.db.struct_data(s).variant_data;
+ let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
+ let fields = vd.fields().iter();
+ let it =
+ al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
+ for (arg, (i, _)) in it {
+ let mut p = place.clone();
+ p.projections.push(ProjectionElem::Field(FieldId {
+ parent: variant.into(),
+ local_id: i,
+ }));
+ self.consume_with_pat(p, *arg);
+ }
+ }
+ }
+ }
+ Pat::Ref { pat, mutability: _ } => {
+ place.projections.push(ProjectionElem::Deref);
+ self.consume_with_pat(place, *pat)
+ }
+ Pat::Box { .. } => (), // not supported
+ }
+ }
+
+ fn consume_exprs(&mut self, exprs: impl Iterator<Item = ExprId>) {
+ for expr in exprs {
+ self.consume_expr(expr);
+ }
+ }
+
+ fn closure_kind(&self) -> FnTrait {
+ let mut r = FnTrait::Fn;
+ for x in &self.current_captures {
+ r = cmp::min(
+ r,
+ match &x.kind {
+ CaptureKind::ByRef(BorrowKind::Unique | BorrowKind::Mut { .. }) => {
+ FnTrait::FnMut
+ }
+ CaptureKind::ByRef(BorrowKind::Shallow | BorrowKind::Shared) => FnTrait::Fn,
+ CaptureKind::ByValue => FnTrait::FnOnce,
+ },
+ )
+ }
+ r
+ }
+
+ fn analyze_closure(&mut self, closure: ClosureId) -> FnTrait {
+ let (_, root) = self.db.lookup_intern_closure(closure.into());
+ self.current_closure = Some(closure);
+ let Expr::Closure { body, capture_by, .. } = &self.body[root] else {
+ unreachable!("Closure expression id is always closure");
+ };
+ self.consume_expr(*body);
+ for item in &self.current_captures {
+ if matches!(item.kind, CaptureKind::ByRef(BorrowKind::Mut { .. }))
+ && !item.place.projections.contains(&ProjectionElem::Deref)
+ {
+ // FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in
+ // MIR. I didn't do that due duplicate diagnostics.
+ self.result.mutated_bindings_in_closure.insert(item.place.local);
+ }
+ }
+ // closure_kind should be done before adjust_for_move_closure
+ let closure_kind = self.closure_kind();
+ match capture_by {
+ CaptureBy::Value => self.adjust_for_move_closure(),
+ CaptureBy::Ref => (),
+ }
+ self.minimize_captures();
+ let result = mem::take(&mut self.current_captures);
+ let captures = result.into_iter().map(|x| x.with_ty(self)).collect::<Vec<_>>();
+ self.result.closure_info.insert(closure, (captures, closure_kind));
+ closure_kind
+ }
+
+ pub(crate) fn infer_closures(&mut self) {
+ let deferred_closures = self.sort_closures();
+ for (closure, exprs) in deferred_closures.into_iter().rev() {
+ self.current_captures = vec![];
+ let kind = self.analyze_closure(closure);
+
+ for (derefed_callee, callee_ty, params, expr) in exprs {
+ if let &Expr::Call { callee, .. } = &self.body[expr] {
+ let mut adjustments =
+ self.result.expr_adjustments.remove(&callee).unwrap_or_default();
+ self.write_fn_trait_method_resolution(
+ kind,
+ &derefed_callee,
+ &mut adjustments,
+ &callee_ty,
+ &params,
+ expr,
+ );
+ self.result.expr_adjustments.insert(callee, adjustments);
+ }
+ }
+ }
+ }
+
+ /// We want to analyze some closures before others, to have a correct analysis:
+ /// * We should analyze nested closures before the parent, since the parent should capture some of
+ /// the things that its children captures.
+ /// * If a closure calls another closure, we need to analyze the callee, to find out how we should
+ /// capture it (e.g. by move for FnOnce)
+ ///
+ /// These dependencies are collected in the main inference. We do a topological sort in this function. It
+ /// will consume the `deferred_closures` field and return its content in a sorted vector.
+ fn sort_closures(&mut self) -> Vec<(ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>)> {
+ let mut deferred_closures = mem::take(&mut self.deferred_closures);
+ let mut dependents_count: FxHashMap<ClosureId, usize> =
+ deferred_closures.keys().map(|x| (*x, 0)).collect();
+ for (_, deps) in &self.closure_dependencies {
+ for dep in deps {
+ *dependents_count.entry(*dep).or_default() += 1;
+ }
+ }
+ let mut queue: Vec<_> =
+ deferred_closures.keys().copied().filter(|x| dependents_count[x] == 0).collect();
+ let mut result = vec![];
+ while let Some(x) = queue.pop() {
+ if let Some(d) = deferred_closures.remove(&x) {
+ result.push((x, d));
+ }
+ for dep in self.closure_dependencies.get(&x).into_iter().flat_map(|x| x.iter()) {
+ let cnt = dependents_count.get_mut(dep).unwrap();
+ *cnt -= 1;
+ if *cnt == 0 {
+ queue.push(*dep);
+ }
+ }
+ }
+ result
+ }
+}
+
+fn apply_adjusts_to_place(mut r: HirPlace, adjustments: &[Adjustment]) -> Option<HirPlace> {
+ for adj in adjustments {
+ match &adj.kind {
+ Adjust::Deref(None) => {
+ r.projections.push(ProjectionElem::Deref);
+ }
+ _ => return None,
+ }
+ }
+ Some(r)
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
index 48c915302..05a476f63 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
@@ -5,14 +5,15 @@
//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
//! `rustc_hir_analysis/check/coercion.rs`.
-use std::{iter, sync::Arc};
+use std::iter;
-use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyVariableKind};
+use chalk_ir::{cast::Cast, BoundVar, Goal, Mutability, TyKind, TyVariableKind};
use hir_def::{
- expr::ExprId,
+ hir::ExprId,
lang_item::{LangItem, LangItemTarget},
};
use stdx::always;
+use triomphe::Arc;
use crate::{
autoderef::{Autoderef, AutoderefKind},
@@ -21,8 +22,10 @@ use crate::{
Adjust, Adjustment, AutoBorrow, InferOk, InferenceContext, OverloadedDeref, PointerCast,
TypeError, TypeMismatch,
},
- static_lifetime, Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner,
- Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
+ static_lifetime,
+ utils::ClosureSubst,
+ Canonical, DomainGoal, FnPointer, FnSig, Guidance, InEnvironment, Interner, Solution,
+ Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
};
use super::unify::InferenceTable;
@@ -47,15 +50,23 @@ fn success(
Ok(InferOk { goals, value: (adj, target) })
}
+pub(super) enum CoercionCause {
+ // FIXME: Make better use of this. Right now things like return and break without a value
+ // use it to point to themselves, causing us to report a mismatch on those expressions even
+ // though technically they themselves are `!`
+ Expr(ExprId),
+}
+
#[derive(Clone, Debug)]
pub(super) struct CoerceMany {
expected_ty: Ty,
final_ty: Option<Ty>,
+ expressions: Vec<ExprId>,
}
impl CoerceMany {
pub(super) fn new(expected: Ty) -> Self {
- CoerceMany { expected_ty: expected, final_ty: None }
+ CoerceMany { expected_ty: expected, final_ty: None, expressions: vec![] }
}
/// Returns the "expected type" with which this coercion was
@@ -86,8 +97,12 @@ impl CoerceMany {
}
}
- pub(super) fn coerce_forced_unit(&mut self, ctx: &mut InferenceContext<'_>) {
- self.coerce(ctx, None, &ctx.result.standard_types.unit.clone())
+ pub(super) fn coerce_forced_unit(
+ &mut self,
+ ctx: &mut InferenceContext<'_>,
+ cause: CoercionCause,
+ ) {
+ self.coerce(ctx, None, &ctx.result.standard_types.unit.clone(), cause)
}
/// Merge two types from different branches, with possible coercion.
@@ -102,6 +117,7 @@ impl CoerceMany {
ctx: &mut InferenceContext<'_>,
expr: Option<ExprId>,
expr_ty: &Ty,
+ cause: CoercionCause,
) {
let expr_ty = ctx.resolve_ty_shallow(expr_ty);
self.expected_ty = ctx.resolve_ty_shallow(&self.expected_ty);
@@ -110,6 +126,8 @@ impl CoerceMany {
// pointers to have a chance at getting a match. See
// https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916
let sig = match (self.merged_ty().kind(Interner), expr_ty.kind(Interner)) {
+ (TyKind::FnDef(x, _), TyKind::FnDef(y, _)) if x == y => None,
+ (TyKind::Closure(x, _), TyKind::Closure(y, _)) if x == y => None,
(TyKind::FnDef(..) | TyKind::Closure(..), TyKind::FnDef(..) | TyKind::Closure(..)) => {
// FIXME: we're ignoring safety here. To be more correct, if we have one FnDef and one Closure,
// we should be coercing the closure to a fn pointer of the safety of the FnDef
@@ -125,8 +143,15 @@ impl CoerceMany {
let result1 = ctx.table.coerce_inner(self.merged_ty(), &target_ty);
let result2 = ctx.table.coerce_inner(expr_ty.clone(), &target_ty);
if let (Ok(result1), Ok(result2)) = (result1, result2) {
- ctx.table.register_infer_ok(result1);
- ctx.table.register_infer_ok(result2);
+ ctx.table.register_infer_ok(InferOk { value: (), goals: result1.goals });
+ for &e in &self.expressions {
+ ctx.write_expr_adj(e, result1.value.0.clone());
+ }
+ ctx.table.register_infer_ok(InferOk { value: (), goals: result2.goals });
+ if let Some(expr) = expr {
+ ctx.write_expr_adj(expr, result2.value.0);
+ self.expressions.push(expr);
+ }
return self.final_ty = Some(target_ty);
}
}
@@ -140,14 +165,19 @@ impl CoerceMany {
} else if let Ok(res) = ctx.coerce(expr, &self.merged_ty(), &expr_ty) {
self.final_ty = Some(res);
} else {
- if let Some(id) = expr {
- ctx.result.type_mismatches.insert(
- id.into(),
- TypeMismatch { expected: self.merged_ty().clone(), actual: expr_ty.clone() },
- );
+ match cause {
+ CoercionCause::Expr(id) => {
+ ctx.result.type_mismatches.insert(
+ id.into(),
+ TypeMismatch { expected: self.merged_ty(), actual: expr_ty.clone() },
+ );
+ }
}
cov_mark::hit!(coerce_merge_fail_fallback);
}
+ if let Some(expr) = expr {
+ self.expressions.push(expr);
+ }
}
}
@@ -625,7 +655,7 @@ impl<'a> InferenceTable<'a> {
// Need to find out in what cases this is necessary
let solution = self
.db
- .trait_solve(krate, canonicalized.value.clone().cast(Interner))
+ .trait_solve(krate, self.trait_env.block, canonicalized.value.clone().cast(Interner))
.ok_or(TypeError)?;
match solution {
@@ -657,7 +687,7 @@ impl<'a> InferenceTable<'a> {
}
fn coerce_closure_fn_ty(closure_substs: &Substitution, safety: chalk_ir::Safety) -> Ty {
- let closure_sig = closure_substs.at(Interner, 0).assert_ty_ref(Interner).clone();
+ let closure_sig = ClosureSubst(closure_substs).sig_ty().clone();
match closure_sig.kind(Interner) {
TyKind::Function(fn_ty) => TyKind::Function(FnPointer {
num_binders: fn_ty.num_binders,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index ee186673e..194471f00 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -6,37 +6,43 @@ use std::{
};
use chalk_ir::{
- cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyKind, TyVariableKind,
+ cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
};
use hir_def::{
- expr::{
+ generics::TypeOrConstParamData,
+ hir::{
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
},
- generics::TypeOrConstParamData,
- lang_item::LangItem,
+ lang_item::{LangItem, LangItemTarget},
path::{GenericArg, GenericArgs},
- ConstParamId, FieldId, ItemContainerId, Lookup,
+ BlockId, ConstParamId, FieldId, ItemContainerId, Lookup,
};
use hir_expand::name::{name, Name};
use stdx::always;
use syntax::ast::RangeOp;
+use triomphe::Arc;
use crate::{
- autoderef::{self, Autoderef},
+ autoderef::{builtin_deref, deref_by_trait, Autoderef},
consteval,
infer::{
- coerce::CoerceMany, find_continuable, pat::contains_explicit_ref_binding, BreakableKind,
+ coerce::{CoerceMany, CoercionCause},
+ find_continuable,
+ pat::contains_explicit_ref_binding,
+ BreakableKind,
},
+ lang_items::lang_items_for_bin_op,
lower::{
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
},
mapping::{from_chalk, ToChalk},
- method_resolution::{self, lang_items_for_bin_op, VisibleFromModule},
+ method_resolution::{self, VisibleFromModule},
primitive::{self, UintTy},
static_lifetime, to_chalk_trait_id,
+ traits::FnTrait,
utils::{generics, Generics},
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst,
- Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt,
+ Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind,
};
use super::{
@@ -83,10 +89,10 @@ impl<'a> InferenceContext<'a> {
}
}
- pub(super) fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
+ fn infer_expr_coerce_never(&mut self, expr: ExprId, expected: &Expectation) -> Ty {
let ty = self.infer_expr_inner(expr, expected);
// While we don't allow *arbitrary* coercions here, we *do* allow
- // coercions from ! to `expected`.
+ // coercions from `!` to `expected`.
if ty.is_never() {
if let Some(adjustments) = self.result.expr_adjustments.get(&expr) {
return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments {
@@ -96,13 +102,22 @@ impl<'a> InferenceContext<'a> {
};
}
- let adj_ty = self.table.new_type_var();
- self.write_expr_adj(
- expr,
- vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty.clone() }],
- );
- adj_ty
+ if let Some(target) = expected.only_has_type(&mut self.table) {
+ self.coerce(Some(expr), &ty, &target)
+ .expect("never-to-any coercion should always succeed")
+ } else {
+ ty
+ }
} else {
+ if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
+ let could_unify = self.unify(&ty, &expected_ty);
+ if !could_unify {
+ self.result.type_mismatches.insert(
+ expr.into(),
+ TypeMismatch { expected: expected_ty, actual: ty.clone() },
+ );
+ }
+ }
ty
}
}
@@ -120,24 +135,28 @@ impl<'a> InferenceContext<'a> {
);
let condition_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
- let mut both_arms_diverge = Diverges::Always;
let then_ty = self.infer_expr_inner(then_branch, expected);
- both_arms_diverge &= mem::replace(&mut self.diverges, Diverges::Maybe);
+ let then_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let mut coerce = CoerceMany::new(expected.coercion_target_type(&mut self.table));
- coerce.coerce(self, Some(then_branch), &then_ty);
+ coerce.coerce(self, Some(then_branch), &then_ty, CoercionCause::Expr(then_branch));
match else_branch {
Some(else_branch) => {
let else_ty = self.infer_expr_inner(else_branch, expected);
- coerce.coerce(self, Some(else_branch), &else_ty);
+ let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
+ coerce.coerce(
+ self,
+ Some(else_branch),
+ &else_ty,
+ CoercionCause::Expr(else_branch),
+ );
+ self.diverges = condition_diverges | then_diverges & else_diverges;
}
None => {
- coerce.coerce_forced_unit(self);
+ coerce.coerce_forced_unit(self, CoercionCause::Expr(tgt_expr));
+ self.diverges = condition_diverges;
}
}
- both_arms_diverge &= self.diverges;
-
- self.diverges = condition_diverges | both_arms_diverge;
coerce.complete(self)
}
@@ -146,67 +165,21 @@ impl<'a> InferenceContext<'a> {
self.infer_top_pat(pat, &input_ty);
self.result.standard_types.bool_.clone()
}
- Expr::Block { statements, tail, label, id: _ } => {
- self.infer_block(tgt_expr, statements, *tail, *label, expected)
+ Expr::Block { statements, tail, label, id } => {
+ self.infer_block(tgt_expr, *id, statements, *tail, *label, expected)
}
- Expr::Unsafe { id: _, statements, tail } => {
- self.infer_block(tgt_expr, statements, *tail, None, expected)
+ Expr::Unsafe { id, statements, tail } => {
+ self.infer_block(tgt_expr, *id, statements, *tail, None, expected)
}
- Expr::Const { id: _, statements, tail } => {
+ Expr::Const(id) => {
self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
- this.infer_block(tgt_expr, statements, *tail, None, expected)
+ let loc = this.db.lookup_intern_anonymous_const(*id);
+ this.infer_expr(loc.root, expected)
})
.1
}
- Expr::TryBlock { id: _, statements, tail } => {
- // The type that is returned from the try block
- let try_ty = self.table.new_type_var();
- if let Some(ty) = expected.only_has_type(&mut self.table) {
- self.unify(&try_ty, &ty);
- }
-
- // The ok-ish type that is expected from the last expression
- let ok_ty =
- self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_output());
-
- self.infer_block(
- tgt_expr,
- statements,
- *tail,
- None,
- &Expectation::has_type(ok_ty.clone()),
- );
- try_ty
- }
- Expr::Async { id: _, statements, tail } => {
- let ret_ty = self.table.new_type_var();
- let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
- let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
- let prev_ret_coercion =
- mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone())));
-
- let (_, inner_ty) =
- self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
- this.infer_block(
- tgt_expr,
- statements,
- *tail,
- None,
- &Expectation::has_type(ret_ty),
- )
- });
-
- self.diverges = prev_diverges;
- self.return_ty = prev_ret_ty;
- self.return_coercion = prev_ret_coercion;
-
- // Use the first type parameter as the output type of future.
- // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
- let impl_trait_id =
- crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr);
- let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
- TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty))
- .intern(Interner)
+ Expr::Async { id, statements, tail } => {
+ self.infer_async_block(tgt_expr, id, statements, tail)
}
&Expr::Loop { body, label } => {
// FIXME: should be:
@@ -238,25 +211,7 @@ impl<'a> InferenceContext<'a> {
self.diverges = Diverges::Maybe;
TyBuilder::unit()
}
- &Expr::For { iterable, body, pat, label } => {
- let iterable_ty = self.infer_expr(iterable, &Expectation::none());
- let into_iter_ty =
- self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
- let pat_ty = self
- .resolve_associated_type(into_iter_ty.clone(), self.resolve_iterator_item());
-
- self.result.type_of_for_iterator.insert(tgt_expr, into_iter_ty);
-
- self.infer_top_pat(pat, &pat_ty);
- self.with_breakable_ctx(BreakableKind::Loop, None, label, |this| {
- this.infer_expr(body, &Expectation::HasType(TyBuilder::unit()));
- });
-
- // the body may not run, so it diverging doesn't mean we diverge
- self.diverges = Diverges::Maybe;
- TyBuilder::unit()
- }
- Expr::Closure { body, args, ret_type, arg_types, closure_kind } => {
+ Expr::Closure { body, args, ret_type, arg_types, closure_kind, capture_by: _ } => {
assert_eq!(args.len(), arg_types.len());
let mut sig_tys = Vec::with_capacity(arg_types.len() + 1);
@@ -276,18 +231,7 @@ impl<'a> InferenceContext<'a> {
None => self.table.new_type_var(),
};
if let ClosureKind::Async = closure_kind {
- // Use the first type parameter as the output type of future.
- // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
- let impl_trait_id =
- crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, *body);
- let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
- sig_tys.push(
- TyKind::OpaqueType(
- opaque_ty_id,
- Substitution::from1(Interner, ret_ty.clone()),
- )
- .intern(Interner),
- );
+ sig_tys.push(self.lower_async_block_type_impl_trait(ret_ty.clone(), *body));
} else {
sig_tys.push(ret_ty.clone());
}
@@ -302,7 +246,7 @@ impl<'a> InferenceContext<'a> {
})
.intern(Interner);
- let (ty, resume_yield_tys) = match closure_kind {
+ let (id, ty, resume_yield_tys) = match closure_kind {
ClosureKind::Generator(_) => {
// FIXME: report error when there are more than 1 parameter.
let resume_ty = match sig_tys.first() {
@@ -322,17 +266,20 @@ impl<'a> InferenceContext<'a> {
let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into();
let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner);
- (generator_ty, Some((resume_ty, yield_ty)))
+ (None, generator_ty, Some((resume_ty, yield_ty)))
}
ClosureKind::Closure | ClosureKind::Async => {
let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
let closure_ty = TyKind::Closure(
closure_id,
- Substitution::from1(Interner, sig_ty.clone()),
+ TyBuilder::subst_for_closure(self.db, self.owner, sig_ty.clone()),
)
.intern(Interner);
-
- (closure_ty, None)
+ self.deferred_closures.entry(closure_id).or_default();
+ if let Some(c) = self.current_closure {
+ self.closure_dependencies.entry(c).or_default().push(closure_id);
+ }
+ (Some(closure_id), closure_ty, None)
}
};
@@ -348,9 +295,10 @@ impl<'a> InferenceContext<'a> {
// FIXME: lift these out into a struct
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
+ let prev_closure = mem::replace(&mut self.current_closure, id);
let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
let prev_ret_coercion =
- mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone())));
+ mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty)));
let prev_resume_yield_tys =
mem::replace(&mut self.resume_yield_tys, resume_yield_tys);
@@ -361,6 +309,7 @@ impl<'a> InferenceContext<'a> {
self.diverges = prev_diverges;
self.return_ty = prev_ret_ty;
self.return_coercion = prev_ret_coercion;
+ self.current_closure = prev_closure;
self.resume_yield_tys = prev_resume_yield_tys;
ty
@@ -385,16 +334,31 @@ impl<'a> InferenceContext<'a> {
|| res.is_none();
let (param_tys, ret_ty) = match res {
Some((func, params, ret_ty)) => {
- let adjustments = auto_deref_adjust_steps(&derefs);
- // FIXME: Handle call adjustments for Fn/FnMut
- self.write_expr_adj(*callee, adjustments);
- if let Some((trait_, func)) = func {
- let subst = TyBuilder::subst_for_def(self.db, trait_, None)
- .push(callee_ty.clone())
- .push(TyBuilder::tuple_with(params.iter().cloned()))
- .build();
- self.write_method_resolution(tgt_expr, func, subst.clone());
+ let mut adjustments = auto_deref_adjust_steps(&derefs);
+ if let TyKind::Closure(c, _) =
+ self.table.resolve_completely(callee_ty.clone()).kind(Interner)
+ {
+ if let Some(par) = self.current_closure {
+ self.closure_dependencies.entry(par).or_default().push(*c);
+ }
+ self.deferred_closures.entry(*c).or_default().push((
+ derefed_callee.clone(),
+ callee_ty.clone(),
+ params.clone(),
+ tgt_expr,
+ ));
}
+ if let Some(fn_x) = func {
+ self.write_fn_trait_method_resolution(
+ fn_x,
+ &derefed_callee,
+ &mut adjustments,
+ &callee_ty,
+ &params,
+ tgt_expr,
+ );
+ }
+ self.write_expr_adj(*callee, adjustments);
(params, ret_ty)
}
None => {
@@ -470,7 +434,7 @@ impl<'a> InferenceContext<'a> {
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
all_arms_diverge &= self.diverges;
- coerce.coerce(self, Some(arm.expr), &arm_ty);
+ coerce.coerce(self, Some(arm.expr), &arm_ty, CoercionCause::Expr(arm.expr));
}
self.diverges = matchee_diverges | all_arms_diverge;
@@ -484,8 +448,8 @@ impl<'a> InferenceContext<'a> {
self.resolver.reset_to_guard(g);
ty
}
- Expr::Continue { label } => {
- if let None = find_continuable(&mut self.breakables, label.as_ref()) {
+ &Expr::Continue { label } => {
+ if let None = find_continuable(&mut self.breakables, label) {
self.push_diagnostic(InferenceDiagnostic::BreakOutsideOfLoop {
expr: tgt_expr,
is_break: false,
@@ -494,9 +458,9 @@ impl<'a> InferenceContext<'a> {
};
self.result.standard_types.never.clone()
}
- Expr::Break { expr, label } => {
- let val_ty = if let Some(expr) = *expr {
- let opt_coerce_to = match find_breakable(&mut self.breakables, label.as_ref()) {
+ &Expr::Break { expr, label } => {
+ let val_ty = if let Some(expr) = expr {
+ let opt_coerce_to = match find_breakable(&mut self.breakables, label) {
Some(ctxt) => match &ctxt.coerce {
Some(coerce) => coerce.expected_ty(),
None => {
@@ -515,13 +479,17 @@ impl<'a> InferenceContext<'a> {
TyBuilder::unit()
};
- match find_breakable(&mut self.breakables, label.as_ref()) {
+ match find_breakable(&mut self.breakables, label) {
Some(ctxt) => match ctxt.coerce.take() {
Some(mut coerce) => {
- coerce.coerce(self, *expr, &val_ty);
+ let cause = match expr {
+ Some(expr) => CoercionCause::Expr(expr),
+ None => CoercionCause::Expr(tgt_expr),
+ };
+ coerce.coerce(self, expr, &val_ty, cause);
// Avoiding borrowck
- let ctxt = find_breakable(&mut self.breakables, label.as_ref())
+ let ctxt = find_breakable(&mut self.breakables, label)
.expect("breakable stack changed during coercion");
ctxt.may_break = true;
ctxt.coerce = Some(coerce);
@@ -538,7 +506,7 @@ impl<'a> InferenceContext<'a> {
}
self.result.standard_types.never.clone()
}
- &Expr::Return { expr } => self.infer_expr_return(expr),
+ &Expr::Return { expr } => self.infer_expr_return(tgt_expr, expr),
Expr::Yield { expr } => {
if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
if let Some(expr) = expr {
@@ -589,6 +557,9 @@ impl<'a> InferenceContext<'a> {
let field_ty = field_def.map_or(self.err_ty(), |it| {
field_types[it.local_id].clone().substitute(Interner, &substs)
});
+ // Field type might have some unknown types
+ // FIXME: we may want to emit a single type variable for all instance of type fields?
+ let field_ty = self.insert_type_vars(field_ty);
self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty));
}
if let Some(expr) = spread {
@@ -601,26 +572,18 @@ impl<'a> InferenceContext<'a> {
let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
}
- Expr::Try { expr } => {
- let inner_ty = self.infer_expr_inner(*expr, &Expectation::none());
- if let Some(trait_) = self.resolve_lang_trait(LangItem::Try) {
- if let Some(func) = self.db.trait_data(trait_).method_by_name(&name!(branch)) {
- let subst = TyBuilder::subst_for_def(self.db, trait_, None)
- .push(inner_ty.clone())
- .build();
- self.write_method_resolution(tgt_expr, func, subst.clone());
- }
- let try_output = self.resolve_output_on(trait_);
- self.resolve_associated_type(inner_ty, try_output)
- } else {
- self.err_ty()
- }
- }
Expr::Cast { expr, type_ref } => {
let cast_ty = self.make_ty(type_ref);
// FIXME: propagate the "castable to" expectation
- let _inner_ty = self.infer_expr_no_expect(*expr);
- // FIXME check the cast...
+ let inner_ty = self.infer_expr_no_expect(*expr);
+ match (inner_ty.kind(Interner), cast_ty.kind(Interner)) {
+ (TyKind::Ref(_, _, inner), TyKind::Raw(_, cast)) => {
+ // FIXME: record invalid cast diagnostic in case of mismatch
+ self.unify(inner, cast);
+ }
+ // FIXME check the other kinds of cast...
+ _ => (),
+ }
cast_ty
}
Expr::Ref { expr, rawness, mutability } => {
@@ -638,7 +601,7 @@ impl<'a> InferenceContext<'a> {
// FIXME: record type error - expected reference but found ptr,
// which cannot be coerced
}
- Expectation::rvalue_hint(&mut self.table, Ty::clone(exp_inner))
+ Expectation::rvalue_hint(self, Ty::clone(exp_inner))
} else {
Expectation::none()
};
@@ -656,7 +619,25 @@ impl<'a> InferenceContext<'a> {
// FIXME: Note down method resolution her
match op {
UnaryOp::Deref => {
- autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty())
+ if let Some(deref_trait) = self.resolve_lang_trait(LangItem::Deref) {
+ if let Some(deref_fn) =
+ self.db.trait_data(deref_trait).method_by_name(&name![deref])
+ {
+ // FIXME: this is wrong in multiple ways, subst is empty, and we emit it even for builtin deref (note that
+ // the mutability is not wrong, and will be fixed in `self.infer_mut`).
+ self.write_method_resolution(
+ tgt_expr,
+ deref_fn,
+ Substitution::empty(Interner),
+ );
+ }
+ }
+ if let Some(derefed) = builtin_deref(&mut self.table, &inner_ty, true) {
+ self.resolve_ty_shallow(derefed)
+ } else {
+ deref_by_trait(&mut self.table, inner_ty)
+ .unwrap_or_else(|| self.err_ty())
+ }
}
UnaryOp::Neg => {
match inner_ty.kind(Interner) {
@@ -767,14 +748,16 @@ impl<'a> InferenceContext<'a> {
let canonicalized = self.canonicalize(base_ty.clone());
let receiver_adjustments = method_resolution::resolve_indexing_op(
self.db,
- self.trait_env.clone(),
+ self.table.trait_env.clone(),
canonicalized.value,
index_trait,
);
- let (self_ty, adj) = receiver_adjustments
+ let (self_ty, mut adj) = receiver_adjustments
.map_or((self.err_ty(), Vec::new()), |adj| {
adj.apply(&mut self.table, base_ty)
});
+ // mutability will be fixed up in `InferenceContext::infer_mut`;
+ adj.push(Adjustment::borrow(Mutability::Not, self_ty.clone()));
self.write_expr_adj(*base, adj);
if let Some(func) =
self.db.trait_data(index_trait).method_by_name(&name!(index))
@@ -783,7 +766,7 @@ impl<'a> InferenceContext<'a> {
.push(self_ty.clone())
.push(index_ty.clone())
.build();
- self.write_method_resolution(tgt_expr, func, substs.clone());
+ self.write_method_resolution(tgt_expr, func, substs);
}
self.resolve_associated_type_with_params(
self_ty,
@@ -834,6 +817,20 @@ impl<'a> InferenceContext<'a> {
let array_type = TyKind::Array(byte_type, len).intern(Interner);
TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner)
}
+ Literal::CString(..) => TyKind::Ref(
+ Mutability::Not,
+ static_lifetime(),
+ self.resolve_lang_item(LangItem::CStr)
+ .and_then(LangItemTarget::as_struct)
+ .map_or_else(
+ || self.err_ty(),
+ |strukt| {
+ TyKind::Adt(AdtId(strukt.into()), Substitution::empty(Interner))
+ .intern(Interner)
+ },
+ ),
+ )
+ .intern(Interner),
Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(Interner),
Literal::Int(_v, ty) => match ty {
Some(int_ty) => {
@@ -859,9 +856,15 @@ impl<'a> InferenceContext<'a> {
},
Expr::Underscore => {
// Underscore expressions may only appear in assignee expressions,
- // which are handled by `infer_assignee_expr()`, so any underscore
- // expression reaching this branch is an error.
- self.err_ty()
+ // which are handled by `infer_assignee_expr()`.
+ // Any other underscore expression is an error, we render a specialized diagnostic
+ // to let the user know what type is expected though.
+ let expected = expected.to_option(&mut self.table).unwrap_or_else(|| self.err_ty());
+ self.push_diagnostic(InferenceDiagnostic::TypedHole {
+ expr: tgt_expr,
+ expected: expected.clone(),
+ });
+ expected
}
};
// use a new type variable if we got unknown here
@@ -874,6 +877,88 @@ impl<'a> InferenceContext<'a> {
ty
}
+ fn infer_async_block(
+ &mut self,
+ tgt_expr: ExprId,
+ id: &Option<BlockId>,
+ statements: &[Statement],
+ tail: &Option<ExprId>,
+ ) -> Ty {
+ let ret_ty = self.table.new_type_var();
+ let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
+ let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
+ let prev_ret_coercion =
+ mem::replace(&mut self.return_coercion, Some(CoerceMany::new(ret_ty.clone())));
+
+ let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
+ this.infer_block(tgt_expr, *id, statements, *tail, None, &Expectation::has_type(ret_ty))
+ });
+
+ self.diverges = prev_diverges;
+ self.return_ty = prev_ret_ty;
+ self.return_coercion = prev_ret_coercion;
+
+ self.lower_async_block_type_impl_trait(inner_ty, tgt_expr)
+ }
+
+ pub(crate) fn lower_async_block_type_impl_trait(
+ &mut self,
+ inner_ty: Ty,
+ tgt_expr: ExprId,
+ ) -> Ty {
+ // Use the first type parameter as the output type of future.
+ // existential type AsyncBlockImplTrait<InnerType>: Future<Output = InnerType>
+ let impl_trait_id = crate::ImplTraitId::AsyncBlockTypeImplTrait(self.owner, tgt_expr);
+ let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into();
+ TyKind::OpaqueType(opaque_ty_id, Substitution::from1(Interner, inner_ty)).intern(Interner)
+ }
+
+ pub(crate) fn write_fn_trait_method_resolution(
+ &mut self,
+ fn_x: FnTrait,
+ derefed_callee: &Ty,
+ adjustments: &mut Vec<Adjustment>,
+ callee_ty: &Ty,
+ params: &Vec<Ty>,
+ tgt_expr: ExprId,
+ ) {
+ match fn_x {
+ FnTrait::FnOnce => (),
+ FnTrait::FnMut => {
+ if let TyKind::Ref(Mutability::Mut, _, inner) = derefed_callee.kind(Interner) {
+ if adjustments
+ .last()
+ .map(|x| matches!(x.kind, Adjust::Borrow(_)))
+ .unwrap_or(true)
+ {
+ // prefer reborrow to move
+ adjustments
+ .push(Adjustment { kind: Adjust::Deref(None), target: inner.clone() });
+ adjustments.push(Adjustment::borrow(Mutability::Mut, inner.clone()))
+ }
+ } else {
+ adjustments.push(Adjustment::borrow(Mutability::Mut, derefed_callee.clone()));
+ }
+ }
+ FnTrait::Fn => {
+ if !matches!(derefed_callee.kind(Interner), TyKind::Ref(Mutability::Not, _, _)) {
+ adjustments.push(Adjustment::borrow(Mutability::Not, derefed_callee.clone()));
+ }
+ }
+ }
+ let Some(trait_) = fn_x.get_id(self.db, self.table.trait_env.krate) else {
+ return;
+ };
+ let trait_data = self.db.trait_data(trait_);
+ if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
+ let subst = TyBuilder::subst_for_def(self.db, trait_, None)
+ .push(callee_ty.clone())
+ .push(TyBuilder::tuple_with(params.iter().cloned()))
+ .build();
+ self.write_method_resolution(tgt_expr, func, subst.clone());
+ }
+ }
+
fn infer_expr_array(
&mut self,
array: &Array,
@@ -892,10 +977,10 @@ impl<'a> InferenceContext<'a> {
(elem_ty, consteval::usize_const(self.db, Some(0), krate))
}
Array::ElementList { elements, .. } => {
- let mut coerce = CoerceMany::new(elem_ty.clone());
+ let mut coerce = CoerceMany::new(elem_ty);
for &expr in elements.iter() {
let cur_elem_ty = self.infer_expr_inner(expr, &expected);
- coerce.coerce(self, Some(expr), &cur_elem_ty);
+ coerce.coerce(self, Some(expr), &cur_elem_ty, CoercionCause::Expr(expr));
}
(
coerce.complete(self),
@@ -904,12 +989,13 @@ impl<'a> InferenceContext<'a> {
}
&Array::Repeat { initializer, repeat } => {
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty.clone()));
- self.infer_expr(
- repeat,
- &Expectation::HasType(
- TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
- ),
- );
+ let usize = TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner);
+ match self.body[repeat] {
+ Expr::Underscore => {
+ self.write_expr_ty(repeat, usize);
+ }
+ _ => _ = self.infer_expr(repeat, &Expectation::HasType(usize)),
+ }
(
elem_ty,
@@ -928,7 +1014,8 @@ impl<'a> InferenceContext<'a> {
)
}
};
-
+ // Try to evaluate unevaluated constant, and insert variable if is not possible.
+ let len = self.table.insert_const_vars_shallow(len);
TyKind::Array(elem_ty, len).intern(Interner)
}
@@ -940,18 +1027,18 @@ impl<'a> InferenceContext<'a> {
.expected_ty();
let return_expr_ty = self.infer_expr_inner(expr, &Expectation::HasType(ret_ty));
let mut coerce_many = self.return_coercion.take().unwrap();
- coerce_many.coerce(self, Some(expr), &return_expr_ty);
+ coerce_many.coerce(self, Some(expr), &return_expr_ty, CoercionCause::Expr(expr));
self.return_coercion = Some(coerce_many);
}
- fn infer_expr_return(&mut self, expr: Option<ExprId>) -> Ty {
+ fn infer_expr_return(&mut self, ret: ExprId, expr: Option<ExprId>) -> Ty {
match self.return_coercion {
Some(_) => {
if let Some(expr) = expr {
self.infer_return(expr);
} else {
let mut coerce = self.return_coercion.take().unwrap();
- coerce.coerce_forced_unit(self);
+ coerce.coerce_forced_unit(self, CoercionCause::Expr(ret));
self.return_coercion = Some(coerce);
}
}
@@ -976,7 +1063,7 @@ impl<'a> InferenceContext<'a> {
.filter(|(e_adt, _)| e_adt == &box_id)
.map(|(_, subts)| {
let g = subts.at(Interner, 0);
- Expectation::rvalue_hint(table, Ty::clone(g.assert_ty_ref(Interner)))
+ Expectation::rvalue_hint(self, Ty::clone(g.assert_ty_ref(Interner)))
})
.unwrap_or_else(Expectation::none);
@@ -1185,6 +1272,7 @@ impl<'a> InferenceContext<'a> {
fn infer_block(
&mut self,
expr: ExprId,
+ block_id: Option<BlockId>,
statements: &[Statement],
tail: Option<ExprId>,
label: Option<LabelId>,
@@ -1192,9 +1280,14 @@ impl<'a> InferenceContext<'a> {
) -> Ty {
let coerce_ty = expected.coercion_target_type(&mut self.table);
let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr);
+ let prev_env = block_id.map(|block_id| {
+ let prev_env = self.table.trait_env.clone();
+ Arc::make_mut(&mut self.table.trait_env).block = Some(block_id);
+ prev_env
+ });
let (break_ty, ty) =
- self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty.clone()), label, |this| {
+ self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty), label, |this| {
for stmt in statements {
match stmt {
Statement::Let { pat, type_ref, initializer, else_branch } => {
@@ -1280,6 +1373,9 @@ impl<'a> InferenceContext<'a> {
}
});
self.resolver.reset_to_guard(g);
+ if let Some(prev_env) = prev_env {
+ self.table.trait_env = prev_env;
+ }
break_ty.unwrap_or(ty)
}
@@ -1378,7 +1474,7 @@ impl<'a> InferenceContext<'a> {
method_resolution::lookup_method(
self.db,
&canonicalized_receiver.value,
- self.trait_env.clone(),
+ self.table.trait_env.clone(),
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
VisibleFromModule::Filter(self.resolver.module()),
name,
@@ -1411,7 +1507,7 @@ impl<'a> InferenceContext<'a> {
let resolved = method_resolution::lookup_method(
self.db,
&canonicalized_receiver.value,
- self.trait_env.clone(),
+ self.table.trait_env.clone(),
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
VisibleFromModule::Filter(self.resolver.module()),
method_name,
@@ -1562,7 +1658,7 @@ impl<'a> InferenceContext<'a> {
// the parameter to coerce to the expected type (for example in
// `coerce_unsize_expected_type_4`).
let param_ty = self.normalize_associated_types_in(param_ty);
- let expected = Expectation::rvalue_hint(&mut self.table, expected_ty);
+ let expected = Expectation::rvalue_hint(self, expected_ty);
// infer with the expected type we have...
let ty = self.infer_expr_inner(arg, &expected);
@@ -1575,9 +1671,10 @@ impl<'a> InferenceContext<'a> {
} else {
param_ty
};
- if !coercion_target.is_unknown()
- && self.coerce(Some(arg), &ty, &coercion_target).is_err()
- {
+ // The function signature may contain some unknown types, so we need to insert
+ // type vars here to avoid type mismatch false positive.
+ let coercion_target = self.insert_type_vars(coercion_target);
+ if self.coerce(Some(arg), &ty, &coercion_target).is_err() {
self.result.type_mismatches.insert(
arg.into(),
TypeMismatch { expected: coercion_target, actual: ty.clone() },
@@ -1618,6 +1715,7 @@ impl<'a> InferenceContext<'a> {
const_or_path_to_chalk(
this.db,
&this.resolver,
+ this.owner.into(),
ty,
c,
ParamLoweringMode::Placeholder,
@@ -1868,7 +1966,6 @@ impl<'a> InferenceContext<'a> {
cb: impl FnOnce(&mut Self) -> T,
) -> (Option<Ty>, T) {
self.breakables.push({
- let label = label.map(|label| self.body[label].name.clone());
BreakableContext { kind, may_break: false, coerce: ty.map(CoerceMany::new), label }
});
let res = cb(self);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
new file mode 100644
index 000000000..46f2e1d7d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -0,0 +1,218 @@
+//! Finds if an expression is an immutable context or a mutable context, which is used in selecting
+//! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar.
+
+use chalk_ir::Mutability;
+use hir_def::{
+ hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
+ lang_item::LangItem,
+};
+use hir_expand::name;
+
+use crate::{lower::lower_to_chalk_mutability, Adjust, Adjustment, AutoBorrow, OverloadedDeref};
+
+use super::InferenceContext;
+
+impl<'a> InferenceContext<'a> {
+ pub(crate) fn infer_mut_body(&mut self) {
+ self.infer_mut_expr(self.body.body_expr, Mutability::Not);
+ }
+
+ fn infer_mut_expr(&mut self, tgt_expr: ExprId, mut mutability: Mutability) {
+ if let Some(adjustments) = self.result.expr_adjustments.get_mut(&tgt_expr) {
+ for adj in adjustments.iter_mut().rev() {
+ match &mut adj.kind {
+ Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => (),
+ Adjust::Deref(Some(d)) => *d = OverloadedDeref(Some(mutability)),
+ Adjust::Borrow(b) => match b {
+ AutoBorrow::Ref(m) | AutoBorrow::RawPtr(m) => mutability = *m,
+ },
+ }
+ }
+ }
+ self.infer_mut_expr_without_adjust(tgt_expr, mutability);
+ }
+
+ fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
+ match &self.body[tgt_expr] {
+ Expr::Missing => (),
+ &Expr::If { condition, then_branch, else_branch } => {
+ self.infer_mut_expr(condition, Mutability::Not);
+ self.infer_mut_expr(then_branch, Mutability::Not);
+ if let Some(else_branch) = else_branch {
+ self.infer_mut_expr(else_branch, Mutability::Not);
+ }
+ }
+ Expr::Const(id) => {
+ let loc = self.db.lookup_intern_anonymous_const(*id);
+ self.infer_mut_expr(loc.root, Mutability::Not);
+ }
+ Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)),
+ Expr::Block { id: _, statements, tail, label: _ }
+ | Expr::Async { id: _, statements, tail }
+ | Expr::Unsafe { id: _, statements, tail } => {
+ for st in statements.iter() {
+ match st {
+ Statement::Let { pat, type_ref: _, initializer, else_branch } => {
+ if let Some(i) = initializer {
+ self.infer_mut_expr(*i, self.pat_bound_mutability(*pat));
+ }
+ if let Some(e) = else_branch {
+ self.infer_mut_expr(*e, Mutability::Not);
+ }
+ }
+ Statement::Expr { expr, has_semi: _ } => {
+ self.infer_mut_expr(*expr, Mutability::Not);
+ }
+ }
+ }
+ if let Some(tail) = tail {
+ self.infer_mut_expr(*tail, Mutability::Not);
+ }
+ }
+ &Expr::While { condition: c, body, label: _ } => {
+ self.infer_mut_expr(c, Mutability::Not);
+ self.infer_mut_expr(body, Mutability::Not);
+ }
+ Expr::MethodCall { receiver: x, method_name: _, args, generic_args: _ }
+ | Expr::Call { callee: x, args, is_assignee_expr: _ } => {
+ self.infer_mut_not_expr_iter(args.iter().copied().chain(Some(*x)));
+ }
+ Expr::Match { expr, arms } => {
+ let m = self.pat_iter_bound_mutability(arms.iter().map(|x| x.pat));
+ self.infer_mut_expr(*expr, m);
+ for arm in arms.iter() {
+ self.infer_mut_expr(arm.expr, Mutability::Not);
+ if let Some(g) = arm.guard {
+ self.infer_mut_expr(g, Mutability::Not);
+ }
+ }
+ }
+ Expr::Yield { expr }
+ | Expr::Yeet { expr }
+ | Expr::Return { expr }
+ | Expr::Break { expr, label: _ } => {
+ if let &Some(expr) = expr {
+ self.infer_mut_expr(expr, Mutability::Not);
+ }
+ }
+ Expr::RecordLit { path: _, fields, spread, ellipsis: _, is_assignee_expr: _ } => {
+ self.infer_mut_not_expr_iter(fields.iter().map(|x| x.expr).chain(*spread))
+ }
+ &Expr::Index { base, index } => {
+ if mutability == Mutability::Mut {
+ if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
+ if let Some(index_trait) = self
+ .db
+ .lang_item(self.table.trait_env.krate, LangItem::IndexMut)
+ .and_then(|l| l.as_trait())
+ {
+ if let Some(index_fn) =
+ self.db.trait_data(index_trait).method_by_name(&name![index_mut])
+ {
+ *f = index_fn;
+ let base_adjustments = self
+ .result
+ .expr_adjustments
+ .get_mut(&base)
+ .and_then(|it| it.last_mut());
+ if let Some(Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(mutability)),
+ ..
+ }) = base_adjustments
+ {
+ *mutability = Mutability::Mut;
+ }
+ }
+ }
+ }
+ }
+ self.infer_mut_expr(base, mutability);
+ self.infer_mut_expr(index, Mutability::Not);
+ }
+ Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
+ if let Some((f, _)) = self.result.method_resolutions.get_mut(&tgt_expr) {
+ if mutability == Mutability::Mut {
+ if let Some(deref_trait) = self
+ .db
+ .lang_item(self.table.trait_env.krate, LangItem::DerefMut)
+ .and_then(|l| l.as_trait())
+ {
+ if let Some(deref_fn) =
+ self.db.trait_data(deref_trait).method_by_name(&name![deref_mut])
+ {
+ *f = deref_fn;
+ }
+ }
+ }
+ }
+ self.infer_mut_expr(*expr, mutability);
+ }
+ Expr::Field { expr, name: _ } => {
+ self.infer_mut_expr(*expr, mutability);
+ }
+ Expr::UnaryOp { expr, op: _ }
+ | Expr::Range { lhs: Some(expr), rhs: None, range_type: _ }
+ | Expr::Range { rhs: Some(expr), lhs: None, range_type: _ }
+ | Expr::Await { expr }
+ | Expr::Box { expr }
+ | Expr::Loop { body: expr, label: _ }
+ | Expr::Cast { expr, type_ref: _ } => {
+ self.infer_mut_expr(*expr, Mutability::Not);
+ }
+ Expr::Ref { expr, rawness: _, mutability } => {
+ let mutability = lower_to_chalk_mutability(*mutability);
+ self.infer_mut_expr(*expr, mutability);
+ }
+ Expr::BinaryOp { lhs, rhs, op: Some(BinaryOp::Assignment { .. }) } => {
+ self.infer_mut_expr(*lhs, Mutability::Mut);
+ self.infer_mut_expr(*rhs, Mutability::Not);
+ }
+ Expr::Array(Array::Repeat { initializer: lhs, repeat: rhs })
+ | Expr::BinaryOp { lhs, rhs, op: _ }
+ | Expr::Range { lhs: Some(lhs), rhs: Some(rhs), range_type: _ } => {
+ self.infer_mut_expr(*lhs, Mutability::Not);
+ self.infer_mut_expr(*rhs, Mutability::Not);
+ }
+ Expr::Closure { body, .. } => {
+ self.infer_mut_expr(*body, Mutability::Not);
+ }
+ Expr::Tuple { exprs, is_assignee_expr: _ }
+ | Expr::Array(Array::ElementList { elements: exprs, is_assignee_expr: _ }) => {
+ self.infer_mut_not_expr_iter(exprs.iter().copied());
+ }
+ // These don't need any action, as they don't have sub expressions
+ Expr::Range { lhs: None, rhs: None, range_type: _ }
+ | Expr::Literal(_)
+ | Expr::Path(_)
+ | Expr::Continue { .. }
+ | Expr::Underscore => (),
+ }
+ }
+
+ fn infer_mut_not_expr_iter(&mut self, exprs: impl Iterator<Item = ExprId>) {
+ for expr in exprs {
+ self.infer_mut_expr(expr, Mutability::Not);
+ }
+ }
+
+ fn pat_iter_bound_mutability(&self, mut pat: impl Iterator<Item = PatId>) -> Mutability {
+ if pat.any(|p| self.pat_bound_mutability(p) == Mutability::Mut) {
+ Mutability::Mut
+ } else {
+ Mutability::Not
+ }
+ }
+
+ /// Checks if the pat contains a `ref mut` binding. Such paths makes the context of bounded expressions
+ /// mutable. For example in `let (ref mut x0, ref x1) = *x;` we need to use `DerefMut` for `*x` but in
+ /// `let (ref x0, ref x1) = *x;` we should use `Deref`.
+ fn pat_bound_mutability(&self, pat: PatId) -> Mutability {
+ let mut r = Mutability::Not;
+ self.body.walk_bindings_in_pat(pat, |b| {
+ if self.body.bindings[b].mode == BindingAnnotation::RefMut {
+ r = Mutability::Mut;
+ }
+ });
+ r
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index 5f839fc30..2480f8bab 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -5,7 +5,7 @@ use std::iter::repeat_with;
use chalk_ir::Mutability;
use hir_def::{
body::Body,
- expr::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
+ hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
path::Path,
};
use hir_expand::name::Name;
@@ -255,15 +255,15 @@ impl<'a> InferenceContext<'a> {
self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm)
}
Pat::Wild => expected.clone(),
- Pat::Range { start, end } => {
- let start_ty = self.infer_expr(*start, &Expectation::has_type(expected.clone()));
- self.infer_expr(*end, &Expectation::has_type(start_ty))
+ Pat::Range { .. } => {
+ // FIXME: do some checks here.
+ expected.clone()
}
&Pat::Lit(expr) => {
// Don't emit type mismatches again, the expression lowering already did that.
let ty = self.infer_lit_pat(expr, &expected);
self.write_pat_ty(pat, ty.clone());
- return ty;
+ return self.pat_ty_after_adjustment(pat);
}
Pat::Box { inner } => match self.resolve_boxed_box() {
Some(box_adt) => {
@@ -298,22 +298,38 @@ impl<'a> InferenceContext<'a> {
.type_mismatches
.insert(pat.into(), TypeMismatch { expected, actual: ty.clone() });
}
- self.write_pat_ty(pat, ty.clone());
- ty
+ self.write_pat_ty(pat, ty);
+ self.pat_ty_after_adjustment(pat)
+ }
+
+ fn pat_ty_after_adjustment(&self, pat: PatId) -> Ty {
+ self.result
+ .pat_adjustments
+ .get(&pat)
+ .and_then(|x| x.first())
+ .unwrap_or(&self.result.type_of_pat[pat])
+ .clone()
}
fn infer_ref_pat(
&mut self,
- pat: PatId,
+ inner_pat: PatId,
mutability: Mutability,
expected: &Ty,
default_bm: BindingMode,
) -> Ty {
let expectation = match expected.as_reference() {
Some((inner_ty, _lifetime, _exp_mut)) => inner_ty.clone(),
- _ => self.result.standard_types.unknown.clone(),
+ None => {
+ let inner_ty = self.table.new_type_var();
+ let ref_ty =
+ TyKind::Ref(mutability, static_lifetime(), inner_ty.clone()).intern(Interner);
+ // Unification failure will be reported by the caller.
+ self.unify(&ref_ty, expected);
+ inner_ty
+ }
};
- let subty = self.infer_pat(pat, &expectation, default_bm);
+ let subty = self.infer_pat(inner_pat, &expectation, default_bm);
TyKind::Ref(mutability, static_lifetime(), subty).intern(Interner)
}
@@ -331,7 +347,7 @@ impl<'a> InferenceContext<'a> {
} else {
BindingMode::convert(mode)
};
- self.result.pat_binding_modes.insert(pat, mode);
+ self.result.binding_modes.insert(binding, mode);
let inner_ty = match subpat {
Some(subpat) => self.infer_pat(subpat, &expected, default_bm),
@@ -345,7 +361,7 @@ impl<'a> InferenceContext<'a> {
}
BindingMode::Move => inner_ty.clone(),
};
- self.write_pat_ty(pat, bound_ty.clone());
+ self.write_pat_ty(pat, inner_ty.clone());
self.write_binding_ty(binding, bound_ty);
return inner_ty;
}
@@ -370,7 +386,7 @@ impl<'a> InferenceContext<'a> {
if let &Some(slice_pat_id) = slice {
let rest_pat_ty = match expected.kind(Interner) {
TyKind::Array(_, length) => {
- let len = try_const_usize(length);
+ let len = try_const_usize(self.db, length);
let len =
len.and_then(|len| len.checked_sub((prefix.len() + suffix.len()) as u128));
TyKind::Array(elem_ty.clone(), usize_const(self.db, len, self.resolver.krate()))
@@ -419,17 +435,10 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
// FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented.
Pat::Path(..) => true,
Pat::ConstBlock(..) => true,
- Pat::Lit(expr) => {
- !matches!(body[*expr], Expr::Literal(Literal::String(..) | Literal::ByteString(..)))
- }
- Pat::Bind { id, subpat: Some(subpat), .. }
- if matches!(
- body.bindings[*id].mode,
- BindingAnnotation::Mutable | BindingAnnotation::Unannotated
- ) =>
- {
- is_non_ref_pat(body, *subpat)
- }
+ Pat::Lit(expr) => !matches!(
+ body[*expr],
+ Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..))
+ ),
Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false,
}
}
@@ -437,7 +446,7 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
let mut res = false;
body.walk_pats(pat_id, &mut |pat| {
- res |= matches!(pat, Pat::Bind { id, .. } if body.bindings[*id].mode == BindingAnnotation::Ref);
+ res |= matches!(body[pat], Pat::Bind { id, .. } if body.bindings[id].mode == BindingAnnotation::Ref);
});
res
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index 2267fedaa..79d9e21e7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -4,7 +4,7 @@ use chalk_ir::cast::Cast;
use hir_def::{
path::{Path, PathSegment},
resolver::{ResolveValueResult, TypeNs, ValueNs},
- AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup,
+ AdtId, AssocItemId, EnumVariantId, GenericDefId, ItemContainerId, Lookup,
};
use hir_expand::name::Name;
use stdx::never;
@@ -13,6 +13,7 @@ use crate::{
builder::ParamKind,
consteval,
method_resolution::{self, VisibleFromModule},
+ to_chalk_trait_id,
utils::generics,
InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
ValueTyDefId,
@@ -20,26 +21,44 @@ use crate::{
use super::{ExprOrPatId, InferenceContext, TraitRef};
-impl<'a> InferenceContext<'a> {
+impl InferenceContext<'_> {
pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
- let ty = self.resolve_value_path(path, id)?;
- let ty = self.insert_type_vars(ty);
+ let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? {
+ ValuePathResolution::GenericDef(value_def, generic_def, substs) => {
+ (value_def, generic_def, substs)
+ }
+ ValuePathResolution::NonGeneric(ty) => return Some(ty),
+ };
+ let substs = self.insert_type_vars(substs);
+ let substs = self.normalize_associated_types_in(substs);
+
+ self.add_required_obligations_for_value_path(generic_def, &substs);
+
+ let ty = self.db.value_ty(value_def).substitute(Interner, &substs);
let ty = self.normalize_associated_types_in(ty);
Some(ty)
}
- fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
+ fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<ValuePathResolution> {
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
- let Some(last) = path.segments().last() else { return None };
- let ty = self.make_ty(type_ref);
+ let last = path.segments().last()?;
+
+ // Don't use `self.make_ty()` here as we need `orig_ns`.
+ let ctx =
+ crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
+ let (ty, orig_ns) = ctx.lower_ty_ext(type_ref);
+ let ty = self.table.insert_type_vars(ty);
+ let ty = self.table.normalize_associated_types_in(ty);
+
let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
- let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
+ let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty);
+ let ty = self.table.insert_type_vars(ty);
+ let ty = self.table.normalize_associated_types_in(ty);
self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
} else {
// FIXME: report error, unresolved first path segment
let value_or_partial =
- self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
+ self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
match value_or_partial {
ResolveValueResult::ValueNs(it) => (it, None),
@@ -49,9 +68,9 @@ impl<'a> InferenceContext<'a> {
}
};
- let typable: ValueTyDefId = match value {
+ let value_def = match value {
ValueNs::LocalBinding(pat) => match self.result.type_of_binding.get(pat) {
- Some(ty) => return Some(ty.clone()),
+ Some(ty) => return Some(ValuePathResolution::NonGeneric(ty.clone())),
None => {
never!("uninferred pattern?");
return None;
@@ -75,28 +94,45 @@ impl<'a> InferenceContext<'a> {
let substs = generics.placeholder_subst(self.db);
let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() {
- let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs);
- return Some(ty);
+ return Some(ValuePathResolution::GenericDef(
+ struct_id.into(),
+ struct_id.into(),
+ substs.clone(),
+ ));
} else {
// FIXME: report error, invalid Self reference
return None;
}
}
- ValueNs::GenericParam(it) => return Some(self.db.const_param_ty(it)),
+ ValueNs::GenericParam(it) => {
+ return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty(it)))
+ }
};
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
- let substs = ctx.substs_from_path(path, typable, true);
+ let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
+ let substs = ctx.substs_from_path(path, value_def, true);
let substs = substs.as_slice(Interner);
let parent_substs = self_subst.or_else(|| {
- let generics = generics(self.db.upcast(), typable.to_generic_def_id()?);
+ let generics = generics(self.db.upcast(), value_def.to_generic_def_id()?);
let parent_params_len = generics.parent_generics()?.len();
let parent_args = &substs[substs.len() - parent_params_len..];
Some(Substitution::from_iter(Interner, parent_args))
});
let parent_substs_len = parent_substs.as_ref().map_or(0, |s| s.len(Interner));
let mut it = substs.iter().take(substs.len() - parent_substs_len).cloned();
- let ty = TyBuilder::value_ty(self.db, typable, parent_substs)
+
+ let Some(generic_def) = value_def.to_generic_def_id() else {
+ // `value_def` is the kind of item that can never be generic (i.e. statics, at least
+ // currently). We can just skip the binders to get its type.
+ let (ty, binders) = self.db.value_ty(value_def).into_value_and_skipped_binders();
+ stdx::always!(
+ parent_substs.is_none() && binders.is_empty(Interner),
+ "non-empty binders for non-generic def",
+ );
+ return Some(ValuePathResolution::NonGeneric(ty));
+ };
+ let builder = TyBuilder::subst_for_def(self.db, generic_def, parent_substs);
+ let substs = builder
.fill(|x| {
it.next().unwrap_or_else(|| match x {
ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner),
@@ -104,7 +140,35 @@ impl<'a> InferenceContext<'a> {
})
})
.build();
- Some(ty)
+
+ Some(ValuePathResolution::GenericDef(value_def, generic_def, substs))
+ }
+
+ fn add_required_obligations_for_value_path(&mut self, def: GenericDefId, subst: &Substitution) {
+ let predicates = self.db.generic_predicates(def);
+ for predicate in predicates.iter() {
+ let (predicate, binders) =
+ predicate.clone().substitute(Interner, &subst).into_value_and_skipped_binders();
+ // Quantified where clauses are not yet handled.
+ stdx::always!(binders.is_empty(Interner));
+ self.push_obligation(predicate.cast(Interner));
+ }
+
+ // We need to add `Self: Trait` obligation when `def` is a trait assoc item.
+ let container = match def {
+ GenericDefId::FunctionId(id) => id.lookup(self.db.upcast()).container,
+ GenericDefId::ConstId(id) => id.lookup(self.db.upcast()).container,
+ _ => return,
+ };
+
+ if let ItemContainerId::TraitId(trait_) = container {
+ let param_len = generics(self.db.upcast(), def).len_self();
+ let parent_subst =
+ Substitution::from_iter(Interner, subst.iter(Interner).skip(param_len));
+ let trait_ref =
+ TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: parent_subst };
+ self.push_obligation(trait_ref.cast(Interner));
+ }
}
fn resolve_assoc_item(
@@ -127,7 +191,11 @@ impl<'a> InferenceContext<'a> {
(TypeNs::TraitId(trait_), true) => {
let segment =
remaining_segments.last().expect("there should be at least one segment here");
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
+ let ctx = crate::lower::TyLoweringContext::new(
+ self.db,
+ &self.resolver,
+ self.owner.into(),
+ );
let trait_ref =
ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None);
self.resolve_trait_assoc_item(trait_ref, segment, id)
@@ -139,7 +207,11 @@ impl<'a> InferenceContext<'a> {
// as Iterator>::Item::default`)
let remaining_segments_for_ty =
remaining_segments.take(remaining_segments.len() - 1);
- let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
+ let ctx = crate::lower::TyLoweringContext::new(
+ self.db,
+ &self.resolver,
+ self.owner.into(),
+ );
let (ty, _) = ctx.lower_partly_resolved_path(
def,
resolved_segment,
@@ -169,7 +241,7 @@ impl<'a> InferenceContext<'a> {
) -> Option<(ValueNs, Substitution)> {
let trait_ = trait_ref.hir_trait_id();
let item =
- self.db.trait_data(trait_).items.iter().map(|(_name, id)| (*id)).find_map(|item| {
+ self.db.trait_data(trait_).items.iter().map(|(_name, id)| *id).find_map(|item| {
match item {
AssocItemId::FunctionId(func) => {
if segment.name == &self.db.function_data(func).name {
@@ -288,7 +360,7 @@ impl<'a> InferenceContext<'a> {
name: &Name,
id: ExprOrPatId,
) -> Option<(ValueNs, Substitution)> {
- let ty = self.resolve_ty_shallow(ty);
+ let ty = self.resolve_ty_shallow(&ty);
let (enum_id, subst) = match ty.as_adt() {
Some((AdtId::EnumId(e), subst)) => (e, subst),
_ => return None,
@@ -300,3 +372,10 @@ impl<'a> InferenceContext<'a> {
Some((ValueNs::EnumVariantId(variant), subst.clone()))
}
}
+
+enum ValuePathResolution {
+ // It's awkward to wrap a single ID in two enums, but we need both and this saves fallible
+ // conversion between them + `unwrap()`.
+ GenericDef(ValueTyDefId, GenericDefId, Substitution),
+ NonGeneric(Ty),
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index 504f0743a..e33d8f179 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -1,23 +1,25 @@
//! Unification and canonicalization logic.
-use std::{fmt, iter, mem, sync::Arc};
+use std::{fmt, iter, mem};
use chalk_ir::{
cast::Cast, fold::TypeFoldable, interner::HasInterner, zip::Zip, CanonicalVarKind, FloatTy,
IntTy, TyVariableKind, UniverseIndex,
};
use chalk_solve::infer::ParameterEnaVariableExt;
+use either::Either;
use ena::unify::UnifyKey;
-use hir_def::{FunctionId, TraitId};
use hir_expand::name;
use stdx::never;
+use triomphe::Arc;
use super::{InferOk, InferResult, InferenceContext, TypeError};
use crate::{
- db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar,
- Canonical, Const, DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment,
- InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution,
- Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
+ consteval::unknown_const, db::HirDatabase, fold_tys_and_consts, static_lifetime,
+ to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue,
+ DebruijnIndex, GenericArg, GenericArgData, Goal, Guidance, InEnvironment, InferenceVar,
+ Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution,
+ TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
};
impl<'a> InferenceContext<'a> {
@@ -130,7 +132,7 @@ pub(crate) fn unify(
}
bitflags::bitflags! {
- #[derive(Default)]
+ #[derive(Default, Clone, Copy)]
pub(crate) struct TypeVariableFlags: u8 {
const DIVERGING = 1 << 0;
const INTEGER = 1 << 1;
@@ -230,14 +232,40 @@ impl<'a> InferenceTable<'a> {
/// type annotation (e.g. from a let type annotation, field type or function
/// call). `make_ty` handles this already, but e.g. for field types we need
/// to do it as well.
- pub(crate) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
- fold_tys(
+ pub(crate) fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
+ where
+ T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
+ {
+ fold_tys_and_consts(
ty,
- |ty, _| match ty.kind(Interner) {
- TyKind::Alias(AliasTy::Projection(proj_ty)) => {
- self.normalize_projection_ty(proj_ty.clone())
- }
- _ => ty,
+ |e, _| match e {
+ Either::Left(ty) => Either::Left(match ty.kind(Interner) {
+ TyKind::Alias(AliasTy::Projection(proj_ty)) => {
+ self.normalize_projection_ty(proj_ty.clone())
+ }
+ _ => ty,
+ }),
+ Either::Right(c) => Either::Right(match &c.data(Interner).value {
+ chalk_ir::ConstValue::Concrete(cc) => match &cc.interned {
+ crate::ConstScalar::UnevaluatedConst(c_id, subst) => {
+ // FIXME: Ideally here we should do everything that we do with type alias, i.e. adding a variable
+ // and registering an obligation. But it needs chalk support, so we handle the most basic
+ // case (a non associated const without generic parameters) manually.
+ if subst.len(Interner) == 0 {
+ if let Ok(eval) = self.db.const_eval((*c_id).into(), subst.clone())
+ {
+ eval
+ } else {
+ unknown_const(c.data(Interner).ty.clone())
+ }
+ } else {
+ unknown_const(c.data(Interner).ty.clone())
+ }
+ }
+ _ => c,
+ },
+ _ => c,
+ }),
},
DebruijnIndex::INNERMOST,
)
@@ -463,7 +491,8 @@ impl<'a> InferenceTable<'a> {
pub(crate) fn try_obligation(&mut self, goal: Goal) -> Option<Solution> {
let in_env = InEnvironment::new(&self.trait_env.env, goal);
let canonicalized = self.canonicalize(in_env);
- let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value);
+ let solution =
+ self.db.trait_solve(self.trait_env.krate, self.trait_env.block, canonicalized.value);
solution
}
@@ -598,7 +627,11 @@ impl<'a> InferenceTable<'a> {
&mut self,
canonicalized: &Canonicalized<InEnvironment<Goal>>,
) -> bool {
- let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value.clone());
+ let solution = self.db.trait_solve(
+ self.trait_env.krate,
+ self.trait_env.block,
+ canonicalized.value.clone(),
+ );
match solution {
Some(Solution::Unique(canonical_subst)) => {
@@ -631,10 +664,13 @@ impl<'a> InferenceTable<'a> {
&mut self,
ty: &Ty,
num_args: usize,
- ) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
+ ) -> Option<(Option<FnTrait>, Vec<Ty>, Ty)> {
match ty.callable_sig(self.db) {
Some(sig) => Some((None, sig.params().to_vec(), sig.ret().clone())),
- None => self.callable_sig_from_fn_trait(ty, num_args),
+ None => {
+ let (f, args_ty, return_ty) = self.callable_sig_from_fn_trait(ty, num_args)?;
+ Some((Some(f), args_ty, return_ty))
+ }
}
}
@@ -642,7 +678,7 @@ impl<'a> InferenceTable<'a> {
&mut self,
ty: &Ty,
num_args: usize,
- ) -> Option<(Option<(TraitId, FunctionId)>, Vec<Ty>, Ty)> {
+ ) -> Option<(FnTrait, Vec<Ty>, Ty)> {
let krate = self.trait_env.krate;
let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?;
let trait_data = self.db.trait_data(fn_once_trait);
@@ -676,23 +712,90 @@ impl<'a> InferenceTable<'a> {
};
let trait_env = self.trait_env.env.clone();
+ let mut trait_ref = projection.trait_ref(self.db);
let obligation = InEnvironment {
- goal: projection.trait_ref(self.db).cast(Interner),
- environment: trait_env,
+ goal: trait_ref.clone().cast(Interner),
+ environment: trait_env.clone(),
};
let canonical = self.canonicalize(obligation.clone());
- if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() {
+ if self
+ .db
+ .trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner))
+ .is_some()
+ {
self.register_obligation(obligation.goal);
let return_ty = self.normalize_projection_ty(projection);
- Some((
- Some(fn_once_trait).zip(trait_data.method_by_name(&name!(call_once))),
- arg_tys,
- return_ty,
- ))
+ for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
+ let fn_x_trait = fn_x.get_id(self.db, krate)?;
+ trait_ref.trait_id = to_chalk_trait_id(fn_x_trait);
+ let obligation: chalk_ir::InEnvironment<chalk_ir::Goal<Interner>> = InEnvironment {
+ goal: trait_ref.clone().cast(Interner),
+ environment: trait_env.clone(),
+ };
+ let canonical = self.canonicalize(obligation.clone());
+ if self
+ .db
+ .trait_solve(krate, self.trait_env.block, canonical.value.cast(Interner))
+ .is_some()
+ {
+ return Some((fn_x, arg_tys, return_ty));
+ }
+ }
+ unreachable!("It should at least implement FnOnce at this point");
} else {
None
}
}
+
+ pub(super) fn insert_type_vars<T>(&mut self, ty: T) -> T
+ where
+ T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
+ {
+ fold_tys_and_consts(
+ ty,
+ |x, _| match x {
+ Either::Left(ty) => Either::Left(self.insert_type_vars_shallow(ty)),
+ Either::Right(c) => Either::Right(self.insert_const_vars_shallow(c)),
+ },
+ DebruijnIndex::INNERMOST,
+ )
+ }
+
+ /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
+ pub(super) fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
+ match ty.kind(Interner) {
+ TyKind::Error => self.new_type_var(),
+ TyKind::InferenceVar(..) => {
+ let ty_resolved = self.resolve_ty_shallow(&ty);
+ if ty_resolved.is_unknown() {
+ self.new_type_var()
+ } else {
+ ty
+ }
+ }
+ _ => ty,
+ }
+ }
+
+ /// Replaces ConstScalar::Unknown by a new type var, so we can maybe still infer it.
+ pub(super) fn insert_const_vars_shallow(&mut self, c: Const) -> Const {
+ let data = c.data(Interner);
+ match &data.value {
+ ConstValue::Concrete(cc) => match &cc.interned {
+ crate::ConstScalar::Unknown => self.new_const_var(data.ty.clone()),
+ // try to evaluate unevaluated const. Replace with new var if const eval failed.
+ crate::ConstScalar::UnevaluatedConst(id, subst) => {
+ if let Ok(eval) = self.db.const_eval(*id, subst.clone()) {
+ eval
+ } else {
+ self.new_const_var(data.ty.clone())
+ }
+ }
+ _ => c,
+ },
+ _ => c,
+ }
+ }
}
impl<'a> fmt::Debug for InferenceTable<'a> {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
index 36af78153..e5038543b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs
@@ -6,9 +6,10 @@ use chalk_ir::{
DebruijnIndex,
};
use hir_def::{
- adt::VariantData, attr::Attrs, visibility::Visibility, AdtId, EnumVariantId, HasModule, Lookup,
- ModuleId, VariantId,
+ attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule,
+ Lookup, ModuleId, VariantId,
};
+use rustc_hash::FxHashSet;
use crate::{
consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
@@ -16,7 +17,8 @@ use crate::{
/// Checks whether a type is visibly uninhabited from a particular module.
pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
- let mut uninhabited_from = UninhabitedFrom { target_mod, db };
+ let mut uninhabited_from =
+ UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
inhabitedness == BREAK_VISIBLY_UNINHABITED
}
@@ -32,7 +34,8 @@ pub(crate) fn is_enum_variant_uninhabited_from(
let vars_attrs = db.variants_attrs(variant.parent);
let is_local = variant.parent.lookup(db.upcast()).container.krate() == target_mod.krate();
- let mut uninhabited_from = UninhabitedFrom { target_mod, db };
+ let mut uninhabited_from =
+ UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
let inhabitedness = uninhabited_from.visit_variant(
variant.into(),
&enum_data.variants[variant.local_id].variant_data,
@@ -45,6 +48,9 @@ pub(crate) fn is_enum_variant_uninhabited_from(
struct UninhabitedFrom<'a> {
target_mod: ModuleId,
+ recursive_ty: FxHashSet<Ty>,
+ // guard for preventing stack overflow in non trivial non terminating types
+ max_depth: usize,
db: &'a dyn HirDatabase,
}
@@ -65,17 +71,27 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
ty: &Ty,
outer_binder: DebruijnIndex,
) -> ControlFlow<VisiblyUninhabited> {
- match ty.kind(Interner) {
+ if self.recursive_ty.contains(ty) || self.max_depth == 0 {
+ // rustc considers recursive types always inhabited. I think it is valid to consider
+ // recursive types as always uninhabited, but we should do what rustc is doing.
+ return CONTINUE_OPAQUELY_INHABITED;
+ }
+ self.recursive_ty.insert(ty.clone());
+ self.max_depth -= 1;
+ let r = match ty.kind(Interner) {
TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
TyKind::Never => BREAK_VISIBLY_UNINHABITED,
TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
- TyKind::Array(item_ty, len) => match try_const_usize(len) {
+ TyKind::Array(item_ty, len) => match try_const_usize(self.db, len) {
Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
Some(1..) => item_ty.super_visit_with(self, outer_binder),
},
TyKind::Ref(..) | _ => CONTINUE_OPAQUELY_INHABITED,
- }
+ };
+ self.recursive_ty.remove(ty);
+ self.max_depth += 1;
+ r
}
fn interner(&self) -> Interner {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
index aea7e9762..e4dd4b86c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs
@@ -7,7 +7,8 @@ use chalk_ir::{Goal, GoalData};
use hir_def::TypeAliasId;
use intern::{impl_internable, Interned};
use smallvec::SmallVec;
-use std::{fmt, sync::Arc};
+use std::fmt;
+use triomphe::Arc;
#[derive(Debug, Copy, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct Interner;
@@ -43,7 +44,7 @@ impl_internable!(
);
impl chalk_ir::interner::Interner for Interner {
- type InternedType = Interned<InternedWrapper<chalk_ir::TyData<Interner>>>;
+ type InternedType = Interned<InternedWrapper<chalk_ir::TyData<Self>>>;
type InternedLifetime = Interned<InternedWrapper<chalk_ir::LifetimeData<Self>>>;
type InternedConst = Interned<InternedWrapper<chalk_ir::ConstData<Self>>>;
type InternedConcreteConst = ConstScalar;
@@ -51,8 +52,8 @@ impl chalk_ir::interner::Interner for Interner {
type InternedGoal = Arc<GoalData<Self>>;
type InternedGoals = Vec<Goal<Self>>;
type InternedSubstitution = Interned<InternedWrapper<SmallVec<[GenericArg; 2]>>>;
- type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
type InternedProgramClauses = Interned<InternedWrapper<Vec<chalk_ir::ProgramClause<Self>>>>;
+ type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
type InternedQuantifiedWhereClauses =
Interned<InternedWrapper<Vec<chalk_ir::QuantifiedWhereClause<Self>>>>;
type InternedVariableKinds = Interned<InternedWrapper<Vec<chalk_ir::VariableKind<Interner>>>>;
@@ -86,6 +87,27 @@ impl chalk_ir::interner::Interner for Interner {
tls::with_current_program(|prog| Some(prog?.debug_assoc_type_id(id, fmt)))
}
+ fn debug_opaque_ty_id(
+ opaque_ty_id: chalk_ir::OpaqueTyId<Self>,
+ fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ Some(write!(fmt, "OpaqueTy#{}", opaque_ty_id.0))
+ }
+
+ fn debug_fn_def_id(
+ fn_def_id: chalk_ir::FnDefId<Self>,
+ fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ tls::with_current_program(|prog| Some(prog?.debug_fn_def_id(fn_def_id, fmt)))
+ }
+
+ fn debug_closure_id(
+ _fn_def_id: chalk_ir::ClosureId<Self>,
+ _fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ None
+ }
+
fn debug_alias(
alias: &chalk_ir::AliasTy<Interner>,
fmt: &mut fmt::Formatter<'_>,
@@ -113,13 +135,6 @@ impl chalk_ir::interner::Interner for Interner {
Some(write!(fmt, "{:?}", opaque_ty.opaque_ty_id))
}
- fn debug_opaque_ty_id(
- opaque_ty_id: chalk_ir::OpaqueTyId<Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "OpaqueTy#{}", opaque_ty_id.0))
- }
-
fn debug_ty(ty: &chalk_ir::Ty<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
Some(write!(fmt, "{:?}", ty.data(Interner)))
}
@@ -131,76 +146,56 @@ impl chalk_ir::interner::Interner for Interner {
Some(write!(fmt, "{:?}", lifetime.data(Interner)))
}
- fn debug_generic_arg(
- parameter: &GenericArg,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", parameter.data(Interner).inner_debug()))
- }
-
- fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
- let goal_data = goal.data(Interner);
- Some(write!(fmt, "{goal_data:?}"))
- }
-
- fn debug_goals(
- goals: &chalk_ir::Goals<Interner>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", goals.debug(Interner)))
- }
-
- fn debug_program_clause_implication(
- pci: &chalk_ir::ProgramClauseImplication<Interner>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", pci.debug(Interner)))
- }
-
- fn debug_substitution(
- substitution: &chalk_ir::Substitution<Interner>,
+ fn debug_const(
+ constant: &chalk_ir::Const<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", substitution.debug(Interner)))
+ Some(write!(fmt, "{:?}", constant.data(Interner)))
}
- fn debug_separator_trait_ref(
- separator_trait_ref: &chalk_ir::SeparatorTraitRef<'_, Interner>,
+ fn debug_generic_arg(
+ parameter: &GenericArg,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", separator_trait_ref.debug(Interner)))
+ Some(write!(fmt, "{:?}", parameter.data(Interner).inner_debug()))
}
- fn debug_fn_def_id(
- fn_def_id: chalk_ir::FnDefId<Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- tls::with_current_program(|prog| Some(prog?.debug_fn_def_id(fn_def_id, fmt)))
- }
- fn debug_const(
- constant: &chalk_ir::Const<Self>,
- fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- Some(write!(fmt, "{:?}", constant.data(Interner)))
- }
fn debug_variable_kinds(
variable_kinds: &chalk_ir::VariableKinds<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
Some(write!(fmt, "{:?}", variable_kinds.as_slice(Interner)))
}
+
fn debug_variable_kinds_with_angles(
variable_kinds: &chalk_ir::VariableKinds<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
Some(write!(fmt, "{:?}", variable_kinds.inner_debug(Interner)))
}
+
fn debug_canonical_var_kinds(
canonical_var_kinds: &chalk_ir::CanonicalVarKinds<Self>,
fmt: &mut fmt::Formatter<'_>,
) -> Option<fmt::Result> {
Some(write!(fmt, "{:?}", canonical_var_kinds.as_slice(Interner)))
}
+ fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
+ let goal_data = goal.data(Interner);
+ Some(write!(fmt, "{goal_data:?}"))
+ }
+ fn debug_goals(
+ goals: &chalk_ir::Goals<Interner>,
+ fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ Some(write!(fmt, "{:?}", goals.debug(Interner)))
+ }
+ fn debug_program_clause_implication(
+ pci: &chalk_ir::ProgramClauseImplication<Interner>,
+ fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ Some(write!(fmt, "{:?}", pci.debug(Interner)))
+ }
fn debug_program_clause(
clause: &chalk_ir::ProgramClause<Self>,
fmt: &mut fmt::Formatter<'_>,
@@ -213,6 +208,19 @@ impl chalk_ir::interner::Interner for Interner {
) -> Option<fmt::Result> {
Some(write!(fmt, "{:?}", clauses.as_slice(Interner)))
}
+ fn debug_substitution(
+ substitution: &chalk_ir::Substitution<Interner>,
+ fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ Some(write!(fmt, "{:?}", substitution.debug(Interner)))
+ }
+ fn debug_separator_trait_ref(
+ separator_trait_ref: &chalk_ir::SeparatorTraitRef<'_, Interner>,
+ fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ Some(write!(fmt, "{:?}", separator_trait_ref.debug(Interner)))
+ }
+
fn debug_quantified_where_clauses(
clauses: &chalk_ir::QuantifiedWhereClauses<Self>,
fmt: &mut fmt::Formatter<'_>,
@@ -220,6 +228,13 @@ impl chalk_ir::interner::Interner for Interner {
Some(write!(fmt, "{:?}", clauses.as_slice(Interner)))
}
+ fn debug_constraints(
+ _clauses: &chalk_ir::Constraints<Self>,
+ _fmt: &mut fmt::Formatter<'_>,
+ ) -> Option<fmt::Result> {
+ None
+ }
+
fn intern_ty(self, kind: chalk_ir::TyKind<Self>) -> Self::InternedType {
let flags = kind.compute_flags(self);
Interned::new(InternedWrapper(chalk_ir::TyData { kind, flags }))
@@ -251,7 +266,7 @@ impl chalk_ir::interner::Interner for Interner {
c1: &Self::InternedConcreteConst,
c2: &Self::InternedConcreteConst,
) -> bool {
- (c1 == &ConstScalar::Unknown) || (c2 == &ConstScalar::Unknown) || (c1 == c2)
+ !matches!(c1, ConstScalar::Bytes(..)) || !matches!(c2, ConstScalar::Bytes(..)) || (c1 == c2)
}
fn intern_generic_arg(
@@ -272,6 +287,10 @@ impl chalk_ir::interner::Interner for Interner {
Arc::new(goal)
}
+ fn goal_data(self, goal: &Self::InternedGoal) -> &GoalData<Self> {
+ goal
+ }
+
fn intern_goals<E>(
self,
data: impl IntoIterator<Item = Result<Goal<Self>, E>>,
@@ -279,10 +298,6 @@ impl chalk_ir::interner::Interner for Interner {
data.into_iter().collect()
}
- fn goal_data(self, goal: &Self::InternedGoal) -> &GoalData<Self> {
- goal
- }
-
fn goals_data(self, goals: &Self::InternedGoals) -> &[Goal<Interner>] {
goals
}
@@ -367,32 +382,18 @@ impl chalk_ir::interner::Interner for Interner {
) -> &[chalk_ir::CanonicalVarKind<Self>] {
canonical_var_kinds
}
-
fn intern_constraints<E>(
self,
data: impl IntoIterator<Item = Result<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>, E>>,
) -> Result<Self::InternedConstraints, E> {
data.into_iter().collect()
}
-
fn constraints_data(
self,
constraints: &Self::InternedConstraints,
) -> &[chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] {
constraints
}
- fn debug_closure_id(
- _fn_def_id: chalk_ir::ClosureId<Self>,
- _fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- None
- }
- fn debug_constraints(
- _clauses: &chalk_ir::Constraints<Self>,
- _fmt: &mut fmt::Formatter<'_>,
- ) -> Option<fmt::Result> {
- None
- }
fn intern_variances<E>(
self,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs
index 5308c7216..85ed46b96 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs
@@ -1,19 +1,65 @@
//! Functions to detect special lang items
-use hir_def::{lang_item::LangItem, AdtId, HasModule};
+use hir_def::{data::adt::StructFlags, lang_item::LangItem, AdtId};
+use hir_expand::name::Name;
use crate::db::HirDatabase;
-pub fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool {
- let krate = adt.module(db.upcast()).krate();
- let box_adt =
- db.lang_item(krate, LangItem::OwnedBox).and_then(|it| it.as_struct()).map(AdtId::from);
- Some(adt) == box_adt
+pub fn is_box(db: &dyn HirDatabase, adt: AdtId) -> bool {
+ let AdtId::StructId(id) = adt else { return false };
+ db.struct_data(id).flags.contains(StructFlags::IS_BOX)
}
-pub fn is_unsafe_cell(adt: AdtId, db: &dyn HirDatabase) -> bool {
- let krate = adt.module(db.upcast()).krate();
- let box_adt =
- db.lang_item(krate, LangItem::UnsafeCell).and_then(|it| it.as_struct()).map(AdtId::from);
- Some(adt) == box_adt
+pub fn is_unsafe_cell(db: &dyn HirDatabase, adt: AdtId) -> bool {
+ let AdtId::StructId(id) = adt else { return false };
+ db.struct_data(id).flags.contains(StructFlags::IS_UNSAFE_CELL)
+}
+
+pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, LangItem)> {
+ use hir_expand::name;
+ use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering};
+ Some(match op {
+ BinaryOp::LogicOp(_) => return None,
+ BinaryOp::ArithOp(aop) => match aop {
+ ArithOp::Add => (name![add], LangItem::Add),
+ ArithOp::Mul => (name![mul], LangItem::Mul),
+ ArithOp::Sub => (name![sub], LangItem::Sub),
+ ArithOp::Div => (name![div], LangItem::Div),
+ ArithOp::Rem => (name![rem], LangItem::Rem),
+ ArithOp::Shl => (name![shl], LangItem::Shl),
+ ArithOp::Shr => (name![shr], LangItem::Shr),
+ ArithOp::BitXor => (name![bitxor], LangItem::BitXor),
+ ArithOp::BitOr => (name![bitor], LangItem::BitOr),
+ ArithOp::BitAnd => (name![bitand], LangItem::BitAnd),
+ },
+ BinaryOp::Assignment { op: Some(aop) } => match aop {
+ ArithOp::Add => (name![add_assign], LangItem::AddAssign),
+ ArithOp::Mul => (name![mul_assign], LangItem::MulAssign),
+ ArithOp::Sub => (name![sub_assign], LangItem::SubAssign),
+ ArithOp::Div => (name![div_assign], LangItem::DivAssign),
+ ArithOp::Rem => (name![rem_assign], LangItem::RemAssign),
+ ArithOp::Shl => (name![shl_assign], LangItem::ShlAssign),
+ ArithOp::Shr => (name![shr_assign], LangItem::ShrAssign),
+ ArithOp::BitXor => (name![bitxor_assign], LangItem::BitXorAssign),
+ ArithOp::BitOr => (name![bitor_assign], LangItem::BitOrAssign),
+ ArithOp::BitAnd => (name![bitand_assign], LangItem::BitAndAssign),
+ },
+ BinaryOp::CmpOp(cop) => match cop {
+ CmpOp::Eq { negated: false } => (name![eq], LangItem::PartialEq),
+ CmpOp::Eq { negated: true } => (name![ne], LangItem::PartialEq),
+ CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
+ (name![le], LangItem::PartialOrd)
+ }
+ CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
+ (name![lt], LangItem::PartialOrd)
+ }
+ CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
+ (name![ge], LangItem::PartialOrd)
+ }
+ CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
+ (name![gt], LangItem::PartialOrd)
+ }
+ },
+ BinaryOp::Assignment { op: None } => return None,
+ })
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
index b95bb01fc..35d3407c1 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -1,19 +1,23 @@
//! Compute the binary representation of a type
use base_db::CrateId;
-use chalk_ir::{AdtId, TyKind};
+use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy};
use hir_def::{
layout::{
- Abi, FieldsShape, Integer, Layout, LayoutCalculator, LayoutError, Primitive, ReprOptions,
- RustcEnumVariantIdx, Scalar, Size, StructKind, TargetDataLayout, Variants, WrappingRange,
+ Abi, FieldsShape, Integer, LayoutCalculator, LayoutS, Primitive, ReprOptions, Scalar, Size,
+ StructKind, TargetDataLayout, WrappingRange,
},
- LocalFieldId,
+ LocalEnumVariantId, LocalFieldId,
};
+use la_arena::{Idx, RawIdx};
use stdx::never;
+use triomphe::Arc;
-use crate::{consteval::try_const_usize, db::HirDatabase, Interner, Substitution, Ty};
+use crate::{
+ consteval::try_const_usize, db::HirDatabase, infer::normalize, layout::adt::struct_variant_idx,
+ utils::ClosureSubst, Interner, Substitution, TraitEnvironment, Ty,
+};
-use self::adt::struct_variant_idx;
pub use self::{
adt::{layout_of_adt_query, layout_of_adt_recover},
target::target_data_layout_query,
@@ -28,6 +32,34 @@ macro_rules! user_error {
mod adt;
mod target;
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct RustcEnumVariantIdx(pub LocalEnumVariantId);
+
+impl rustc_index::vec::Idx for RustcEnumVariantIdx {
+ fn new(idx: usize) -> Self {
+ RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32)))
+ }
+
+ fn index(self) -> usize {
+ u32::from(self.0.into_raw()) as usize
+ }
+}
+
+pub type Layout = LayoutS<RustcEnumVariantIdx>;
+pub type TagEncoding = hir_def::layout::TagEncoding<RustcEnumVariantIdx>;
+pub type Variants = hir_def::layout::Variants<RustcEnumVariantIdx>;
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum LayoutError {
+ UserError(String),
+ SizeOverflow,
+ TargetLayoutNotAvailable,
+ HasPlaceholder,
+ HasErrorType,
+ NotImplemented,
+ Unknown,
+}
+
struct LayoutCx<'a> {
krate: CrateId,
target: &'a TargetDataLayout,
@@ -45,20 +77,18 @@ impl<'a> LayoutCalculator for LayoutCx<'a> {
}
}
-fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
- Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
-}
-
-fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout {
- Layout::scalar(dl, scalar_unit(dl, value))
-}
-
-pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Layout, LayoutError> {
+pub fn layout_of_ty_query(
+ db: &dyn HirDatabase,
+ ty: Ty,
+ krate: CrateId,
+) -> Result<Arc<Layout>, LayoutError> {
let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
let cx = LayoutCx { krate, target: &target };
let dl = &*cx.current_data_layout();
- Ok(match ty.kind(Interner) {
- TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?,
+ let trait_env = Arc::new(TraitEnvironment::empty(krate));
+ let ty = normalize(db, trait_env, ty.clone());
+ let result = match ty.kind(Interner) {
+ TyKind::Adt(AdtId(def), subst) => return db.layout_of_adt(*def, subst.clone(), krate),
TyKind::Scalar(s) => match s {
chalk_ir::Scalar::Bool => Layout::scalar(
dl,
@@ -78,12 +108,12 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
dl,
Primitive::Int(
match i {
- chalk_ir::IntTy::Isize => dl.ptr_sized_integer(),
- chalk_ir::IntTy::I8 => Integer::I8,
- chalk_ir::IntTy::I16 => Integer::I16,
- chalk_ir::IntTy::I32 => Integer::I32,
- chalk_ir::IntTy::I64 => Integer::I64,
- chalk_ir::IntTy::I128 => Integer::I128,
+ IntTy::Isize => dl.ptr_sized_integer(),
+ IntTy::I8 => Integer::I8,
+ IntTy::I16 => Integer::I16,
+ IntTy::I32 => Integer::I32,
+ IntTy::I64 => Integer::I64,
+ IntTy::I128 => Integer::I128,
},
true,
),
@@ -92,12 +122,12 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
dl,
Primitive::Int(
match i {
- chalk_ir::UintTy::Usize => dl.ptr_sized_integer(),
- chalk_ir::UintTy::U8 => Integer::I8,
- chalk_ir::UintTy::U16 => Integer::I16,
- chalk_ir::UintTy::U32 => Integer::I32,
- chalk_ir::UintTy::U64 => Integer::I64,
- chalk_ir::UintTy::U128 => Integer::I128,
+ UintTy::Usize => dl.ptr_sized_integer(),
+ UintTy::U8 => Integer::I8,
+ UintTy::U16 => Integer::I16,
+ UintTy::U32 => Integer::I32,
+ UintTy::U64 => Integer::I64,
+ UintTy::U128 => Integer::I128,
},
false,
),
@@ -105,8 +135,8 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
chalk_ir::Scalar::Float(f) => scalar(
dl,
match f {
- chalk_ir::FloatTy::F32 => Primitive::F32,
- chalk_ir::FloatTy::F64 => Primitive::F64,
+ FloatTy::F32 => Primitive::F32,
+ FloatTy::F64 => Primitive::F64,
},
),
},
@@ -115,17 +145,17 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
let fields = tys
.iter(Interner)
- .map(|k| layout_of_ty(db, k.assert_ty_ref(Interner), krate))
+ .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), krate))
.collect::<Result<Vec<_>, _>>()?;
- let fields = fields.iter().collect::<Vec<_>>();
+ let fields = fields.iter().map(|x| &**x).collect::<Vec<_>>();
let fields = fields.iter().collect::<Vec<_>>();
cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)?
}
TyKind::Array(element, count) => {
- let count = try_const_usize(&count).ok_or(LayoutError::UserError(
- "mismatched type of const generic parameter".to_string(),
+ let count = try_const_usize(db, &count).ok_or(LayoutError::UserError(
+ "unevaluated or mistyped const generic parameter".to_string(),
))? as u64;
- let element = layout_of_ty(db, element, krate)?;
+ let element = db.layout_of_ty(element.clone(), krate)?;
let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) {
@@ -146,7 +176,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
}
}
TyKind::Slice(element) => {
- let element = layout_of_ty(db, element, krate)?;
+ let element = db.layout_of_ty(element.clone(), krate)?;
Layout {
variants: Variants::Single { index: struct_variant_idx() },
fields: FieldsShape::Array { stride: element.size, count: 0 },
@@ -180,7 +210,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
}
_ => {
// pointee is sized
- return Ok(Layout::scalar(dl, data_ptr));
+ return Ok(Arc::new(Layout::scalar(dl, data_ptr)));
}
};
@@ -222,23 +252,51 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Lay
match impl_trait_id {
crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
let infer = db.infer(func.into());
- layout_of_ty(db, &infer.type_of_rpit[idx], krate)?
+ return db.layout_of_ty(infer.type_of_rpit[idx].clone(), krate);
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
return Err(LayoutError::NotImplemented)
}
}
}
- TyKind::Closure(_, _) | TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
+ TyKind::Closure(c, subst) => {
+ let (def, _) = db.lookup_intern_closure((*c).into());
+ let infer = db.infer(def);
+ let (captures, _) = infer.closure_info(c);
+ let fields = captures
+ .iter()
+ .map(|x| {
+ db.layout_of_ty(
+ x.ty.clone().substitute(Interner, ClosureSubst(subst).parent_subst()),
+ krate,
+ )
+ })
+ .collect::<Result<Vec<_>, _>>()?;
+ let fields = fields.iter().map(|x| &**x).collect::<Vec<_>>();
+ let fields = fields.iter().collect::<Vec<_>>();
+ cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized)
+ .ok_or(LayoutError::Unknown)?
+ }
+ TyKind::Generator(_, _) | TyKind::GeneratorWitness(_, _) => {
return Err(LayoutError::NotImplemented)
}
+ TyKind::Error => return Err(LayoutError::HasErrorType),
TyKind::AssociatedType(_, _)
- | TyKind::Error
| TyKind::Alias(_)
| TyKind::Placeholder(_)
| TyKind::BoundVar(_)
| TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder),
- })
+ };
+ Ok(Arc::new(result))
+}
+
+pub fn layout_of_ty_recover(
+ _: &dyn HirDatabase,
+ _: &[String],
+ _: &Ty,
+ _: &CrateId,
+) -> Result<Arc<Layout>, LayoutError> {
+ user_error!("infinite sized recursive type");
}
fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result<Layout, LayoutError> {
@@ -274,5 +332,13 @@ fn field_ty(
db.field_types(def)[fd].clone().substitute(Interner, subst)
}
+fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
+ Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) }
+}
+
+fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout {
+ Layout::scalar(dl, scalar_unit(dl, value))
+}
+
#[cfg(test)]
mod tests;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
index b22d0fe8d..bd2752a71 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
@@ -1,18 +1,25 @@
//! Compute the binary representation of structs, unions and enums
-use std::ops::Bound;
+use std::{cmp, ops::Bound};
+use base_db::CrateId;
use hir_def::{
- adt::VariantData,
- layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx},
- AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId,
+ data::adt::VariantData,
+ layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout},
+ AdtId, EnumVariantId, LocalEnumVariantId, VariantId,
};
use la_arena::RawIdx;
use smallvec::SmallVec;
+use triomphe::Arc;
-use crate::{db::HirDatabase, lang_items::is_unsafe_cell, layout::field_ty, Substitution};
+use crate::{
+ db::HirDatabase,
+ lang_items::is_unsafe_cell,
+ layout::{field_ty, Layout, LayoutError, RustcEnumVariantIdx},
+ Substitution,
+};
-use super::{layout_of_ty, LayoutCx};
+use super::LayoutCx;
pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx {
RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0)))
@@ -22,29 +29,29 @@ pub fn layout_of_adt_query(
db: &dyn HirDatabase,
def: AdtId,
subst: Substitution,
-) -> Result<Layout, LayoutError> {
- let krate = def.module(db.upcast()).krate();
+ krate: CrateId,
+) -> Result<Arc<Layout>, LayoutError> {
let Some(target) = db.target_data_layout(krate) else { return Err(LayoutError::TargetLayoutNotAvailable) };
let cx = LayoutCx { krate, target: &target };
let dl = cx.current_data_layout();
let handle_variant = |def: VariantId, var: &VariantData| {
var.fields()
.iter()
- .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate))
+ .map(|(fd, _)| db.layout_of_ty(field_ty(db, def, fd, &subst), cx.krate))
.collect::<Result<Vec<_>, _>>()
};
- let (variants, is_enum, is_union, repr) = match def {
+ let (variants, repr) = match def {
AdtId::StructId(s) => {
let data = db.struct_data(s);
let mut r = SmallVec::<[_; 1]>::new();
r.push(handle_variant(s.into(), &data.variant_data)?);
- (r, false, false, data.repr.unwrap_or_default())
+ (r, data.repr.unwrap_or_default())
}
AdtId::UnionId(id) => {
let data = db.union_data(id);
let mut r = SmallVec::new();
r.push(handle_variant(id.into(), &data.variant_data)?);
- (r, false, true, data.repr.unwrap_or_default())
+ (r, data.repr.unwrap_or_default())
}
AdtId::EnumId(e) => {
let data = db.enum_data(e);
@@ -58,22 +65,24 @@ pub fn layout_of_adt_query(
)
})
.collect::<Result<SmallVec<_>, _>>()?;
- (r, true, false, data.repr.unwrap_or_default())
+ (r, data.repr.unwrap_or_default())
}
};
- let variants =
- variants.iter().map(|x| x.iter().collect::<Vec<_>>()).collect::<SmallVec<[_; 1]>>();
+ let variants = variants
+ .iter()
+ .map(|x| x.iter().map(|x| &**x).collect::<Vec<_>>())
+ .collect::<SmallVec<[_; 1]>>();
let variants = variants.iter().map(|x| x.iter().collect()).collect();
- if is_union {
- cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)
+ let result = if matches!(def, AdtId::UnionId(..)) {
+ cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)?
} else {
cx.layout_of_struct_or_enum(
&repr,
&variants,
- is_enum,
- is_unsafe_cell(def, db),
+ matches!(def, AdtId::EnumId(..)),
+ is_unsafe_cell(db, def),
layout_scalar_valid_range(db, def),
- |min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)),
+ |min, max| repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)),
variants.iter_enumerated().filter_map(|(id, _)| {
let AdtId::EnumId(e) = def else { return None };
let d =
@@ -90,15 +99,16 @@ pub fn layout_of_adt_query(
// .iter_enumerated()
// .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32()))
repr.inhibit_enum_layout_opt(),
- !is_enum
+ !matches!(def, AdtId::EnumId(..))
&& variants
.iter()
.next()
.and_then(|x| x.last().map(|x| x.is_unsized()))
.unwrap_or(true),
)
- .ok_or(LayoutError::SizeOverflow)
- }
+ .ok_or(LayoutError::SizeOverflow)?
+ };
+ Ok(Arc::new(result))
}
fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
@@ -122,6 +132,54 @@ pub fn layout_of_adt_recover(
_: &[String],
_: &AdtId,
_: &Substitution,
-) -> Result<Layout, LayoutError> {
+ _: &CrateId,
+) -> Result<Arc<Layout>, LayoutError> {
user_error!("infinite sized recursive type");
}
+
+/// Finds the appropriate Integer type and signedness for the given
+/// signed discriminant range and `#[repr]` attribute.
+/// N.B.: `u128` values above `i128::MAX` will be treated as signed, but
+/// that shouldn't affect anything, other than maybe debuginfo.
+fn repr_discr(
+ dl: &TargetDataLayout,
+ repr: &ReprOptions,
+ min: i128,
+ max: i128,
+) -> Result<(Integer, bool), LayoutError> {
+ // Theoretically, negative values could be larger in unsigned representation
+ // than the unsigned representation of the signed minimum. However, if there
+ // are any negative values, the only valid unsigned representation is u128
+ // which can fit all i128 values, so the result remains unaffected.
+ let unsigned_fit = Integer::fit_unsigned(cmp::max(min as u128, max as u128));
+ let signed_fit = cmp::max(Integer::fit_signed(min), Integer::fit_signed(max));
+
+ if let Some(ity) = repr.int {
+ let discr = Integer::from_attr(dl, ity);
+ let fit = if ity.is_signed() { signed_fit } else { unsigned_fit };
+ if discr < fit {
+ return Err(LayoutError::UserError(
+ "Integer::repr_discr: `#[repr]` hint too small for \
+ discriminant range of enum "
+ .to_string(),
+ ));
+ }
+ return Ok((discr, ity.is_signed()));
+ }
+
+ let at_least = if repr.c() {
+ // This is usually I32, however it can be different on some platforms,
+ // notably hexagon and arm-none/thumb-none
+ dl.c_enum_min_size
+ } else {
+ // repr(Rust) enums try to be as small as possible
+ Integer::I8
+ };
+
+ // If there are no negative values, we can use the unsigned fit.
+ Ok(if min >= 0 {
+ (cmp::max(unsigned_fit, at_least), false)
+ } else {
+ (cmp::max(signed_fit, at_least), true)
+ })
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
index adfae0a1a..04b940afb 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
@@ -1,9 +1,8 @@
//! Target dependent parameters needed for layouts
-use std::sync::Arc;
-
use base_db::CrateId;
use hir_def::layout::TargetDataLayout;
+use triomphe::Arc;
use crate::db::HirDatabase;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
index a8971fde3..0ff8c532d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
@@ -2,49 +2,67 @@ use std::collections::HashMap;
use base_db::fixture::WithFixture;
use chalk_ir::{AdtId, TyKind};
-use hir_def::{
- db::DefDatabase,
+use either::Either;
+use hir_def::db::DefDatabase;
+use triomphe::Arc;
+
+use crate::{
+ db::HirDatabase,
layout::{Layout, LayoutError},
+ test_db::TestDB,
+ Interner, Substitution,
};
-use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution};
-
-use super::layout_of_ty;
+mod closure;
fn current_machine_data_layout() -> String {
project_model::target_data_layout::get(None, None, &HashMap::default()).unwrap()
}
-fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
+fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
let target_data_layout = current_machine_data_layout();
let ra_fixture = format!(
"{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\n{ra_fixture}",
);
- let (db, file_id) = TestDB::with_single_file(&ra_fixture);
- let module_id = db.module_for_file(file_id);
- let def_map = module_id.def_map(&db);
- let scope = &def_map[module_id.local_id].scope;
- let adt_id = scope
- .declarations()
- .find_map(|x| match x {
- hir_def::ModuleDefId::AdtId(x) => {
- let name = match x {
- hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(),
- hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(),
- hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(),
- };
- (name == "Goal").then_some(x)
- }
- _ => None,
+ let (db, file_ids) = TestDB::with_many_files(&ra_fixture);
+ let (adt_or_type_alias_id, module_id) = file_ids
+ .into_iter()
+ .find_map(|file_id| {
+ let module_id = db.module_for_file(file_id);
+ let def_map = module_id.def_map(&db);
+ let scope = &def_map[module_id.local_id].scope;
+ let adt_or_type_alias_id = scope.declarations().find_map(|x| match x {
+ hir_def::ModuleDefId::AdtId(x) => {
+ let name = match x {
+ hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(),
+ hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(),
+ hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(),
+ };
+ (name == "Goal").then_some(Either::Left(x))
+ }
+ hir_def::ModuleDefId::TypeAliasId(x) => {
+ let name = db.type_alias_data(x).name.to_smol_str();
+ (name == "Goal").then_some(Either::Right(x))
+ }
+ _ => None,
+ })?;
+ Some((adt_or_type_alias_id, module_id))
})
.unwrap();
- let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner);
- layout_of_ty(&db, &goal_ty, module_id.krate())
+ let goal_ty = match adt_or_type_alias_id {
+ Either::Left(adt_id) => {
+ TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner)
+ }
+ Either::Right(ty_id) => {
+ db.ty(ty_id.into()).substitute(Interner, &Substitution::empty(Interner))
+ }
+ };
+ db.layout_of_ty(goal_ty, module_id.krate())
}
/// A version of `eval_goal` for types that can not be expressed in ADTs, like closures and `impl Trait`
-fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
+fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Arc<Layout>, LayoutError> {
let target_data_layout = current_machine_data_layout();
let ra_fixture = format!(
"{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\nfn main(){{let goal = {{{ra_fixture}}};}}",
@@ -68,7 +86,7 @@ fn eval_expr(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> {
let b = hir_body.bindings.iter().find(|x| x.1.name.to_smol_str() == "goal").unwrap().0;
let infer = db.infer(adt_id.into());
let goal_ty = infer.type_of_binding[b].clone();
- layout_of_ty(&db, &goal_ty, module_id.krate())
+ db.layout_of_ty(goal_ty, module_id.krate())
}
#[track_caller]
@@ -81,8 +99,8 @@ fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64)
#[track_caller]
fn check_size_and_align_expr(ra_fixture: &str, minicore: &str, size: u64, align: u64) {
let l = eval_expr(ra_fixture, minicore).unwrap();
- assert_eq!(l.size.bytes(), size);
- assert_eq!(l.align.abi.bytes(), align);
+ assert_eq!(l.size.bytes(), size, "size mismatch");
+ assert_eq!(l.align.abi.bytes(), align, "align mismatch");
}
#[track_caller]
@@ -118,13 +136,31 @@ macro_rules! size_and_align {
};
}
+#[macro_export]
macro_rules! size_and_align_expr {
+ (minicore: $($x:tt),*; stmts: [$($s:tt)*] $($t:tt)*) => {
+ {
+ #[allow(dead_code)]
+ #[allow(unused_must_use)]
+ #[allow(path_statements)]
+ {
+ $($s)*
+ let val = { $($t)* };
+ $crate::layout::tests::check_size_and_align_expr(
+ &format!("{{ {} let val = {{ {} }}; val }}", stringify!($($s)*), stringify!($($t)*)),
+ &format!("//- minicore: {}\n", stringify!($($x),*)),
+ ::std::mem::size_of_val(&val) as u64,
+ ::std::mem::align_of_val(&val) as u64,
+ );
+ }
+ }
+ };
($($t:tt)*) => {
{
#[allow(dead_code)]
{
let val = { $($t)* };
- check_size_and_align_expr(
+ $crate::layout::tests::check_size_and_align_expr(
stringify!($($t)*),
"",
::std::mem::size_of_val(&val) as u64,
@@ -197,6 +233,44 @@ fn generic() {
}
#[test]
+fn associated_types() {
+ size_and_align! {
+ trait Tr {
+ type Ty;
+ }
+
+ impl Tr for i32 {
+ type Ty = i64;
+ }
+
+ struct Foo<A: Tr>(<A as Tr>::Ty);
+ struct Bar<A: Tr>(A::Ty);
+ struct Goal(Foo<i32>, Bar<i32>, <i32 as Tr>::Ty);
+ }
+ check_size_and_align(
+ r#"
+//- /b/mod.rs crate:b
+pub trait Tr {
+ type Ty;
+}
+pub struct Foo<A: Tr>(<A as Tr>::Ty);
+
+//- /a/mod.rs crate:a deps:b
+use b::{Tr, Foo};
+
+struct S;
+impl Tr for S {
+ type Ty = i64;
+}
+struct Goal(Foo<S>);
+ "#,
+ "",
+ 8,
+ 8,
+ );
+}
+
+#[test]
fn return_position_impl_trait() {
size_and_align_expr! {
trait T {}
@@ -213,6 +287,45 @@ fn return_position_impl_trait() {
foo()
}
size_and_align_expr! {
+ minicore: iterators;
+ stmts: []
+ trait Tr {}
+ impl Tr for i32 {}
+ fn foo() -> impl Iterator<Item = impl Tr> {
+ [1, 2, 3].into_iter()
+ }
+ let mut iter = foo();
+ let item = iter.next();
+ (iter, item)
+ }
+ size_and_align_expr! {
+ minicore: future;
+ stmts: []
+ use core::{future::Future, task::{Poll, Context}, pin::pin};
+ use std::{task::Wake, sync::Arc};
+ trait Tr {}
+ impl Tr for i32 {}
+ async fn f() -> impl Tr {
+ 2
+ }
+ fn unwrap_fut<T>(inp: impl Future<Output = T>) -> Poll<T> {
+ // In a normal test we could use `loop {}` or `panic!()` here,
+ // but rustc actually runs this code.
+ let pinned = pin!(inp);
+ struct EmptyWaker;
+ impl Wake for EmptyWaker {
+ fn wake(self: Arc<Self>) {
+ }
+ }
+ let waker = Arc::new(EmptyWaker).into();
+ let mut context = Context::from_waker(&waker);
+ let x = pinned.poll(&mut context);
+ x
+ }
+ let x = unwrap_fut(f());
+ x
+ }
+ size_and_align_expr! {
struct Foo<T>(T, T, (T, T));
trait T {}
impl T for Foo<i32> {}
@@ -277,6 +390,27 @@ fn niche_optimization() {
}
#[test]
+fn const_eval() {
+ size_and_align! {
+ struct Goal([i32; 2 + 2]);
+ }
+ size_and_align! {
+ const X: usize = 5;
+ struct Goal([i32; X]);
+ }
+ size_and_align! {
+ mod foo {
+ pub(super) const BAR: usize = 5;
+ }
+ struct Ar<T>([T; foo::BAR]);
+ struct Goal(Ar<Ar<i32>>);
+ }
+ size_and_align! {
+ type Goal = [u8; 2 + 2];
+ }
+}
+
+#[test]
fn enums_with_discriminants() {
size_and_align! {
enum Goal {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs
new file mode 100644
index 000000000..576e7f3fc
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs
@@ -0,0 +1,257 @@
+use crate::size_and_align_expr;
+
+#[test]
+fn zero_capture_simple() {
+ size_and_align_expr! {
+ |x: i32| x + 2
+ }
+}
+
+#[test]
+fn move_simple() {
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: []
+ let y: i32 = 5;
+ move |x: i32| {
+ x + y
+ }
+ }
+}
+
+#[test]
+fn ref_simple() {
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ let y: i32 = 5;
+ ]
+ |x: i32| {
+ x + y
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ let mut y: i32 = 5;
+ ]
+ |x: i32| {
+ y = y + x;
+ y
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy, deref_mut;
+ stmts: [
+ let y: &mut i32 = &mut 5;
+ ]
+ |x: i32| {
+ *y += x;
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ struct X(i32, i64);
+ let x: X = X(2, 6);
+ ]
+ || {
+ x
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy, deref_mut;
+ stmts: [
+ struct X(i32, i64);
+ let x: &mut X = &mut X(2, 6);
+ ]
+ || {
+ (*x).0 as i64 + x.1
+ }
+ }
+}
+
+#[test]
+fn ref_then_mut_then_move() {
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ struct X(i32, i64);
+ let mut x: X = X(2, 6);
+ ]
+ || {
+ &x;
+ &mut x;
+ x;
+ }
+ }
+}
+
+#[test]
+fn nested_closures() {
+ size_and_align_expr! {
+ || {
+ || {
+ || {
+ let x = 2;
+ move || {
+ move || {
+ x
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#[test]
+fn capture_specific_fields2() {
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ let x = &mut 2;
+ ]
+ || {
+ *x = 5;
+ &x;
+ }
+ }
+}
+
+#[test]
+fn capture_specific_fields() {
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ move |x: i64| {
+ y.0 + x + (y.2 .0 as i64)
+ }
+ }
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ move |x: i64| {
+ let _ = &y;
+ y.0 + x + (y.2 .0 as i64)
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ ]
+ let y = &y;
+ move |x: i64| {
+ y.0 + x + (y.2 .0 as i64)
+ }
+ }
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ move |x: i64| {
+ let X(a, _, (b, _)) = y;
+ a + x + (b as i64)
+ }
+ }
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y = &&X(2, 5, (7, 3));
+ move |x: i64| {
+ let X(a, _, (b, _)) = y;
+ *a + x + (*b as i64)
+ }
+ }
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ move |x: i64| {
+ match y {
+ X(a, _, (b, _)) => a + x + (b as i64),
+ }
+ }
+ }
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ move |x: i64| {
+ let X(a @ 2, _, (b, _)) = y else { return 5 };
+ a + x + (b as i64)
+ }
+ }
+}
+
+#[test]
+fn match_pattern() {
+ size_and_align_expr! {
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ move |x: i64| {
+ match y {
+ _ => x,
+ }
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ ]
+ |x: i64| {
+ match y {
+ X(_a, _, _c) => x,
+ }
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ ]
+ |x: i64| {
+ match y {
+ _y => x,
+ }
+ }
+ }
+ size_and_align_expr! {
+ minicore: copy;
+ stmts: [
+ struct X(i64, i32, (u8, i128));
+ let y: X = X(2, 5, (7, 3));
+ ]
+ |x: i64| {
+ match y {
+ ref _y => x,
+ }
+ }
+ }
+}
+
+#[test]
+fn ellipsis_pattern() {
+ size_and_align_expr! {
+ struct X(i8, u16, i32, u64, i128, u8);
+ let y: X = X(1, 2, 3, 4, 5, 6);
+ move |_: i64| {
+ let X(_a, .., _b, _c) = y;
+ }
+ }
+ size_and_align_expr! {
+ struct X { a: i32, b: u8, c: i128}
+ let y: X = X { a: 1, b: 2, c: 3 };
+ move |_: i64| {
+ let X { a, b, .. } = y;
+ _ = (a, b);
+ }
+ }
+ size_and_align_expr! {
+ let y: (&&&(i8, u16, i32, u64, i128, u8), u16, i32, u64, i128, u8) = (&&&(1, 2, 3, 4, 5, 6), 2, 3, 4, 5, 6);
+ move |_: i64| {
+ let ((_a, .., _b, _c), .., _e, _f) = y;
+ }
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index 9c63d67ab..1a4d003bf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -1,6 +1,5 @@
//! The type system. We currently use this to infer types for completion, hover
//! information and various assists.
-
#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
#[allow(unused)]
@@ -8,12 +7,9 @@ macro_rules! eprintln {
($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
}
-mod autoderef;
mod builder;
mod chalk_db;
mod chalk_ext;
-pub mod consteval;
-pub mod mir;
mod infer;
mod inhabitedness;
mod interner;
@@ -21,21 +17,28 @@ mod lower;
mod mapping;
mod tls;
mod utils;
+
+pub mod autoderef;
+pub mod consteval;
pub mod db;
pub mod diagnostics;
pub mod display;
+pub mod lang_items;
+pub mod layout;
pub mod method_resolution;
+pub mod mir;
pub mod primitive;
pub mod traits;
-pub mod layout;
-pub mod lang_items;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod test_db;
-use std::{collections::HashMap, hash::Hash, sync::Arc};
+use std::{
+ collections::{hash_map::Entry, HashMap},
+ hash::Hash,
+};
use chalk_ir::{
fold::{Shift, TypeFoldable},
@@ -44,12 +47,13 @@ use chalk_ir::{
NoSolution, TyData,
};
use either::Either;
-use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId};
+use hir_def::{hir::ExprId, type_ref::Rawness, GeneralConstId, TypeOrConstParamId};
use hir_expand::name;
use la_arena::{Arena, Idx};
-use mir::MirEvalError;
+use mir::{MirEvalError, VTableMap};
use rustc_hash::FxHashSet;
use traits::FnTrait;
+use triomphe::Arc;
use utils::Generics;
use crate::{
@@ -60,6 +64,7 @@ pub use autoderef::autoderef;
pub use builder::{ParamKind, TyBuilder};
pub use chalk_ext::*;
pub use infer::{
+ closure::{CaptureKind, CapturedItem},
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic,
InferenceResult, OverloadedDeref, PointerCast,
};
@@ -148,14 +153,26 @@ pub type Guidance = chalk_solve::Guidance<Interner>;
pub type WhereClause = chalk_ir::WhereClause<Interner>;
/// A constant can have reference to other things. Memory map job is holding
-/// the neccessary bits of memory of the const eval session to keep the constant
+/// the necessary bits of memory of the const eval session to keep the constant
/// meaningful.
#[derive(Debug, Default, Clone, PartialEq, Eq)]
-pub struct MemoryMap(pub HashMap<usize, Vec<u8>>);
+pub struct MemoryMap {
+ pub memory: HashMap<usize, Vec<u8>>,
+ pub vtable: VTableMap,
+}
impl MemoryMap {
fn insert(&mut self, addr: usize, x: Vec<u8>) {
- self.0.insert(addr, x);
+ match self.memory.entry(addr) {
+ Entry::Occupied(mut e) => {
+ if e.get().len() < x.len() {
+ e.insert(x);
+ }
+ }
+ Entry::Vacant(e) => {
+ e.insert(x);
+ }
+ }
}
/// This functions convert each address by a function `f` which gets the byte intervals and assign an address
@@ -165,7 +182,15 @@ impl MemoryMap {
&self,
mut f: impl FnMut(&[u8]) -> Result<usize, MirEvalError>,
) -> Result<HashMap<usize, usize>, MirEvalError> {
- self.0.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
+ self.memory.iter().map(|x| Ok((*x.0, f(x.1)?))).collect()
+ }
+
+ fn get<'a>(&'a self, addr: usize, size: usize) -> Option<&'a [u8]> {
+ if size == 0 {
+ Some(&[])
+ } else {
+ self.memory.get(&addr)?.get(0..size)
+ }
}
}
@@ -173,6 +198,9 @@ impl MemoryMap {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConstScalar {
Bytes(Vec<u8>, MemoryMap),
+ // FIXME: this is a hack to get around chalk not being able to represent unevaluatable
+ // constants
+ UnevaluatedConst(GeneralConstId, Substitution),
/// Case of an unknown value that rustc might know but we don't
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
// constants
@@ -283,16 +311,19 @@ impl CallableSig {
pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig {
CallableSig {
// FIXME: what to do about lifetime params? -> return PolyFnSig
- params_and_return: fn_ptr
- .substitution
- .clone()
- .shifted_out_to(Interner, DebruijnIndex::ONE)
- .expect("unexpected lifetime vars in fn ptr")
- .0
- .as_slice(Interner)
- .iter()
- .map(|arg| arg.assert_ty_ref(Interner).clone())
- .collect(),
+ // FIXME: use `Arc::from_iter` when it becomes available
+ params_and_return: Arc::from(
+ fn_ptr
+ .substitution
+ .clone()
+ .shifted_out_to(Interner, DebruijnIndex::ONE)
+ .expect("unexpected lifetime vars in fn ptr")
+ .0
+ .as_slice(Interner)
+ .iter()
+ .map(|arg| arg.assert_ty_ref(Interner).clone())
+ .collect::<Vec<_>>(),
+ ),
is_varargs: fn_ptr.sig.variadic,
safety: fn_ptr.sig.safety,
}
@@ -576,15 +607,19 @@ where
}
pub fn callable_sig_from_fnonce(
- self_ty: &Ty,
+ mut self_ty: &Ty,
env: Arc<TraitEnvironment>,
db: &dyn HirDatabase,
) -> Option<CallableSig> {
+ if let Some((ty, _, _)) = self_ty.as_reference() {
+ // This will happen when it implements fn or fn mut, since we add a autoborrow adjustment
+ self_ty = ty;
+ }
let krate = env.krate;
let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?;
let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?;
- let mut table = InferenceTable::new(db, env.clone());
+ let mut table = InferenceTable::new(db, env);
let b = TyBuilder::trait_ref(db, fn_once_trait);
if b.remaining() != 2 {
return None;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 23b15087e..9951a1c75 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -8,7 +8,6 @@
use std::{
cell::{Cell, RefCell, RefMut},
iter,
- sync::Arc,
};
use base_db::CrateId;
@@ -18,19 +17,21 @@ use chalk_ir::{
use either::Either;
use hir_def::{
- adt::StructKind,
- body::{Expander, LowerCtx},
builtin_type::BuiltinType,
+ data::adt::StructKind,
+ expander::Expander,
generics::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
},
lang_item::{lang_attr, LangItem},
- path::{GenericArg, ModPath, Path, PathKind, PathSegment, PathSegments},
+ nameres::MacroSubNs,
+ path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
resolver::{HasResolver, Resolver, TypeNs},
- type_ref::{ConstRefOrPath, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
- AdtId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId,
- HasModule, ImplId, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StaticId, StructId,
- TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
+ type_ref::{ConstRef, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef},
+ AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId,
+ GenericDefId, HasModule, ImplId, InTypeConstLoc, ItemContainerId, LocalFieldId, Lookup,
+ ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId,
+ TypeParamId, UnionId, VariantId,
};
use hir_expand::{name::Name, ExpandResult};
use intern::Interned;
@@ -39,20 +40,28 @@ use rustc_hash::FxHashSet;
use smallvec::SmallVec;
use stdx::{impl_from, never};
use syntax::ast;
+use triomphe::Arc;
use crate::{
all_super_traits,
- consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic},
+ consteval::{
+ intern_const_ref, intern_const_scalar, path_to_const, unknown_const,
+ unknown_const_as_generic,
+ },
db::HirDatabase,
make_binders,
mapping::{from_chalk_trait_id, ToChalk},
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
utils::Generics,
- utils::{all_super_trait_refs, associated_type_by_name_including_super_traits, generics},
- AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, DebruijnIndex, DynTy, FnPointer,
- FnSig, FnSubst, GenericArgData, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy,
- QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits,
- Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
+ utils::{
+ all_super_trait_refs, associated_type_by_name_including_super_traits, generics,
+ InTypeConstIdMetadata,
+ },
+ AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
+ FnPointer, FnSig, FnSubst, GenericArgData, ImplTraitId, Interner, ParamKind, PolyFnSig,
+ ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait,
+ ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
+ TyKind, WhereClause,
};
#[derive(Debug)]
@@ -103,8 +112,9 @@ impl ImplTraitLoweringState {
#[derive(Debug)]
pub struct TyLoweringContext<'a> {
pub db: &'a dyn HirDatabase,
- pub resolver: &'a Resolver,
+ resolver: &'a Resolver,
in_binders: DebruijnIndex,
+ owner: TypeOwnerId,
/// Note: Conceptually, it's thinkable that we could be in a location where
/// some type params should be represented as placeholders, and others
/// should be converted to variables. I think in practice, this isn't
@@ -117,13 +127,14 @@ pub struct TyLoweringContext<'a> {
}
impl<'a> TyLoweringContext<'a> {
- pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver) -> Self {
+ pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver, owner: TypeOwnerId) -> Self {
let impl_trait_mode = ImplTraitLoweringState::Disallowed;
let type_param_mode = ParamLoweringMode::Placeholder;
let in_binders = DebruijnIndex::INNERMOST;
Self {
db,
resolver,
+ owner,
in_binders,
impl_trait_mode,
type_param_mode,
@@ -234,6 +245,7 @@ impl<'a> TyLoweringContext<'a> {
let const_len = const_or_path_to_chalk(
self.db,
self.resolver,
+ self.owner,
TyBuilder::usize(),
len,
self.type_param_mode,
@@ -378,10 +390,19 @@ impl<'a> TyLoweringContext<'a> {
};
let ty = {
let macro_call = macro_call.to_node(self.db.upcast());
- match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call) {
+ let resolver = |path| {
+ self.resolver.resolve_path_as_macro(
+ self.db.upcast(),
+ &path,
+ Some(MacroSubNs::Bang),
+ )
+ };
+ match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call, resolver)
+ {
Ok(ExpandResult { value: Some((mark, expanded)), .. }) => {
- let ctx = LowerCtx::new(self.db.upcast(), expander.current_file_id());
- let type_ref = TypeRef::from_ast(&ctx, expanded);
+ let ctx = expander.ctx(self.db.upcast());
+ // FIXME: Report syntax errors in expansion here
+ let type_ref = TypeRef::from_ast(&ctx, expanded.tree());
drop(expander);
let ty = self.lower_ty(&type_ref);
@@ -425,11 +446,10 @@ impl<'a> TyLoweringContext<'a> {
if path.segments().len() > 1 {
return None;
}
- let resolution =
- match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
- Some((it, None)) => it,
- _ => return None,
- };
+ let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
+ Some((it, None)) => it,
+ _ => return None,
+ };
match resolution {
TypeNs::GenericParam(param_id) => Some(param_id.into()),
_ => None,
@@ -608,7 +628,7 @@ impl<'a> TyLoweringContext<'a> {
}
let (resolution, remaining_index) =
- match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) {
+ match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
Some(it) => it,
None => return (TyKind::Error.intern(Interner), None),
};
@@ -716,7 +736,7 @@ impl<'a> TyLoweringContext<'a> {
resolved: ValueTyDefId,
infer_args: bool,
) -> Substitution {
- let last = path.segments().last().expect("path should have at least one segment");
+ let last = path.segments().last();
let (segment, generic_def) = match resolved {
ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
ValueTyDefId::StructId(it) => (last, Some(it.into())),
@@ -732,13 +752,20 @@ impl<'a> TyLoweringContext<'a> {
let len = path.segments().len();
let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx));
let segment = match penultimate {
- Some(segment) if segment.args_and_bindings.is_some() => segment,
+ Some(segment) if segment.args_and_bindings.is_some() => Some(segment),
_ => last,
};
(segment, Some(var.parent.into()))
}
};
- self.substs_from_path_segment(segment, generic_def, infer_args, None)
+ if let Some(segment) = segment {
+ self.substs_from_path_segment(segment, generic_def, infer_args, None)
+ } else if let Some(generic_def) = generic_def {
+ // lang item
+ self.substs_from_args_and_bindings(None, Some(generic_def), infer_args, None)
+ } else {
+ Substitution::empty(Interner)
+ }
}
fn substs_from_path_segment(
@@ -748,6 +775,21 @@ impl<'a> TyLoweringContext<'a> {
infer_args: bool,
explicit_self_ty: Option<Ty>,
) -> Substitution {
+ self.substs_from_args_and_bindings(
+ segment.args_and_bindings,
+ def,
+ infer_args,
+ explicit_self_ty,
+ )
+ }
+
+ fn substs_from_args_and_bindings(
+ &self,
+ args_and_bindings: Option<&GenericArgs>,
+ def: Option<GenericDefId>,
+ infer_args: bool,
+ explicit_self_ty: Option<Ty>,
+ ) -> Substitution {
// Remember that the item's own generic args come before its parent's.
let mut substs = Vec::new();
let def = if let Some(d) = def {
@@ -780,7 +822,7 @@ impl<'a> TyLoweringContext<'a> {
};
let mut had_explicit_args = false;
- if let Some(generic_args) = &segment.args_and_bindings {
+ if let Some(generic_args) = &args_and_bindings {
if !generic_args.has_self_type {
fill_self_params();
}
@@ -809,6 +851,7 @@ impl<'a> TyLoweringContext<'a> {
const_or_path_to_chalk(
self.db,
self.resolver,
+ self.owner,
ty,
c,
self.type_param_mode,
@@ -879,12 +922,11 @@ impl<'a> TyLoweringContext<'a> {
path: &Path,
explicit_self_ty: Option<Ty>,
) -> Option<TraitRef> {
- let resolved =
- match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path.mod_path())? {
- // FIXME(trait_alias): We need to handle trait alias here.
- TypeNs::TraitId(tr) => tr,
- _ => return None,
- };
+ let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? {
+ // FIXME(trait_alias): We need to handle trait alias here.
+ TypeNs::TraitId(tr) => tr,
+ _ => return None,
+ };
let segment = path.segments().last().expect("path should have at least one segment");
Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty))
}
@@ -968,7 +1010,7 @@ impl<'a> TyLoweringContext<'a> {
// ignore `T: Drop` or `T: Destruct` bounds.
// - `T: ~const Drop` has a special meaning in Rust 1.61 that we don't implement.
// (So ideally, we'd only ignore `~const Drop` here)
- // - `Destruct` impls are built-in in 1.62 (current nightlies as of 08-04-2022), so until
+ // - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until
// the builtin impls are supported by Chalk, we ignore them here.
if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) {
if matches!(lang, LangItem::Drop | LangItem::Destruct) {
@@ -1062,23 +1104,23 @@ impl<'a> TyLoweringContext<'a> {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution,
};
- let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity(
+ let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
);
if let Some(type_ref) = &binding.type_ref {
let ty = self.lower_ty(type_ref);
let alias_eq =
AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
- preds.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
+ predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
}
for bound in binding.bounds.iter() {
- preds.extend(self.lower_type_bound(
+ predicates.extend(self.lower_type_bound(
bound,
TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
false,
));
}
- preds
+ predicates
})
}
@@ -1145,7 +1187,7 @@ impl<'a> TyLoweringContext<'a> {
return None;
}
- // As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
+ // As multiple occurrences of the same auto traits *are* permitted, we deduplicate the
// bounds. We shouldn't have repeated elements besides auto traits at this point.
bounds.dedup();
@@ -1326,8 +1368,8 @@ pub(crate) fn field_types_query(
};
let generics = generics(db.upcast(), def);
let mut res = ArenaMap::default();
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, GenericDefId::from(variant_id.adt_id()).into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
for (field_id, field_data) in var_data.fields().iter() {
res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref)));
}
@@ -1349,8 +1391,8 @@ pub(crate) fn generic_predicates_for_param_query(
assoc_name: Option<Name>,
) -> Arc<[Binders<QuantifiedWhereClause>]> {
let resolver = def.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
let generics = generics(db.upcast(), def);
let mut predicates: Vec<_> = resolver
.where_predicates_in_scope()
@@ -1381,9 +1423,7 @@ pub(crate) fn generic_predicates_for_param_query(
Some(it) => it,
None => return true,
};
- let tr = match resolver
- .resolve_path_in_type_ns_fully(db.upcast(), path.mod_path())
- {
+ let tr = match resolver.resolve_path_in_type_ns_fully(db.upcast(), path) {
Some(TypeNs::TraitId(tr)) => tr,
_ => return false,
};
@@ -1420,7 +1460,19 @@ pub(crate) fn generic_predicates_for_param_recover(
_param_id: &TypeOrConstParamId,
_assoc_name: &Option<Name>,
) -> Arc<[Binders<QuantifiedWhereClause>]> {
- Arc::new([])
+ // FIXME: use `Arc::from_iter` when it becomes available
+ Arc::from(vec![])
+}
+
+pub(crate) fn trait_environment_for_body_query(
+ db: &dyn HirDatabase,
+ def: DefWithBodyId,
+) -> Arc<TraitEnvironment> {
+ let Some(def) = def.as_generic_def_id() else {
+ let krate = def.module(db.upcast()).krate();
+ return Arc::new(TraitEnvironment::empty(krate));
+ };
+ db.trait_environment(def)
}
pub(crate) fn trait_environment_query(
@@ -1428,8 +1480,8 @@ pub(crate) fn trait_environment_query(
def: GenericDefId,
) -> Arc<TraitEnvironment> {
let resolver = def.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Placeholder);
+ let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ .with_type_param_mode(ParamLoweringMode::Placeholder);
let mut traits_in_scope = Vec::new();
let mut clauses = Vec::new();
for pred in resolver.where_predicates_in_scope() {
@@ -1478,7 +1530,7 @@ pub(crate) fn trait_environment_query(
let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
- Arc::new(TraitEnvironment { krate, traits_from_clauses: traits_in_scope, env })
+ Arc::new(TraitEnvironment { krate, block: None, traits_from_clauses: traits_in_scope, env })
}
/// Resolve the where clause(s) of an item with generics.
@@ -1487,8 +1539,8 @@ pub(crate) fn generic_predicates_query(
def: GenericDefId,
) -> Arc<[Binders<QuantifiedWhereClause>]> {
let resolver = def.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
let generics = generics(db.upcast(), def);
let mut predicates = resolver
@@ -1542,35 +1594,38 @@ pub(crate) fn generic_defaults_query(
def: GenericDefId,
) -> Arc<[Binders<chalk_ir::GenericArg<Interner>>]> {
let resolver = def.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
let generic_params = generics(db.upcast(), def);
let parent_start_idx = generic_params.len_self();
- let defaults = generic_params
- .iter()
- .enumerate()
- .map(|(idx, (id, p))| {
- let p = match p {
- TypeOrConstParamData::TypeParamData(p) => p,
- TypeOrConstParamData::ConstParamData(_) => {
- // FIXME: implement const generic defaults
- let val = unknown_const_as_generic(
- db.const_param_ty(ConstParamId::from_unchecked(id)),
- );
- return make_binders(db, &generic_params, val);
- }
- };
- let mut ty =
- p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
-
- // Each default can only refer to previous parameters.
- // Type variable default referring to parameter coming
- // after it is forbidden (FIXME: report diagnostic)
- ty = fallback_bound_vars(ty, idx, parent_start_idx);
- crate::make_binders(db, &generic_params, ty.cast(Interner))
- })
- .collect();
+ let defaults = Arc::from(
+ generic_params
+ .iter()
+ .enumerate()
+ .map(|(idx, (id, p))| {
+ let p = match p {
+ TypeOrConstParamData::TypeParamData(p) => p,
+ TypeOrConstParamData::ConstParamData(_) => {
+ // FIXME: implement const generic defaults
+ let val = unknown_const_as_generic(
+ db.const_param_ty(ConstParamId::from_unchecked(id)),
+ );
+ return make_binders(db, &generic_params, val);
+ }
+ };
+ let mut ty =
+ p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
+
+ // Each default can only refer to previous parameters.
+ // Type variable default referring to parameter coming
+ // after it is forbidden (FIXME: report diagnostic)
+ ty = fallback_bound_vars(ty, idx, parent_start_idx);
+ crate::make_binders(db, &generic_params, ty.cast(Interner))
+ })
+ // FIXME: use `Arc::from_iter` when it becomes available
+ .collect::<Vec<_>>(),
+ );
defaults
}
@@ -1583,18 +1638,21 @@ pub(crate) fn generic_defaults_recover(
let generic_params = generics(db.upcast(), *def);
// FIXME: this code is not covered in tests.
// we still need one default per parameter
- let defaults = generic_params
- .iter_id()
- .map(|id| {
- let val = match id {
- Either::Left(_) => {
- GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
- }
- Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
- };
- crate::make_binders(db, &generic_params, val)
- })
- .collect();
+ let defaults = Arc::from(
+ generic_params
+ .iter_id()
+ .map(|id| {
+ let val = match id {
+ Either::Left(_) => {
+ GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
+ }
+ Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
+ };
+ crate::make_binders(db, &generic_params, val)
+ })
+ // FIXME: use `Arc::from_iter` when it becomes available
+ .collect::<Vec<_>>(),
+ );
defaults
}
@@ -1602,11 +1660,11 @@ pub(crate) fn generic_defaults_recover(
fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
let data = db.function_data(def);
let resolver = def.resolver(db.upcast());
- let ctx_params = TyLoweringContext::new(db, &resolver)
+ let ctx_params = TyLoweringContext::new(db, &resolver, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Variable)
.with_type_param_mode(ParamLoweringMode::Variable);
- let params = data.params.iter().map(|(_, tr)| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
- let ctx_ret = TyLoweringContext::new(db, &resolver)
+ let params = data.params.iter().map(|tr| ctx_params.lower_ty(tr)).collect::<Vec<_>>();
+ let ctx_ret = TyLoweringContext::new(db, &resolver, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
let ret = ctx_ret.lower_ty(&data.ret_type);
@@ -1637,8 +1695,8 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
let data = db.const_data(def);
let generics = generics(db.upcast(), def.into());
let resolver = def.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, def.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
make_binders(db, &generics, ctx.lower_ty(&data.type_ref))
}
@@ -1647,7 +1705,7 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders<Ty> {
let data = db.static_data(def);
let resolver = def.resolver(db.upcast());
- let ctx = TyLoweringContext::new(db, &resolver);
+ let ctx = TyLoweringContext::new(db, &resolver, def.into());
Binders::empty(Interner, ctx.lower_ty(&data.type_ref))
}
@@ -1656,8 +1714,8 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS
let struct_data = db.struct_data(def);
let fields = struct_data.variant_data.fields();
let resolver = def.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, AdtId::from(def).into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
@@ -1669,7 +1727,7 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Binders<T
if let StructKind::Unit = struct_data.variant_data.kind() {
return type_for_adt(db, def.into());
}
- let generics = generics(db.upcast(), def.into());
+ let generics = generics(db.upcast(), AdtId::from(def).into());
let substs = generics.bound_vars_subst(db, DebruijnIndex::INNERMOST);
make_binders(
db,
@@ -1683,8 +1741,8 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId)
let var_data = &enum_data.variants[def.local_id];
let fields = var_data.variant_data.fields();
let resolver = def.parent.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, DefWithBodyId::VariantId(def).into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::<Vec<_>>();
let (ret, binders) = type_for_adt(db, def.parent.into()).into_value_and_skipped_binders();
Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe))
@@ -1716,8 +1774,8 @@ fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> {
fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> {
let generics = generics(db.upcast(), t.into());
let resolver = t.resolver(db.upcast());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, t.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
if db.type_alias_data(t).is_extern {
Binders::empty(Interner, TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner))
} else {
@@ -1838,8 +1896,8 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde
"impl_self_ty_query({impl_id:?} -> {impl_loc:?} -> {impl_data:?})"
));
let generics = generics(db.upcast(), impl_id.into());
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, impl_id.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
make_binders(db, &generics, ctx.lower_ty(&impl_data.self_ty))
}
@@ -1848,7 +1906,7 @@ pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> T
let parent_data = db.generic_params(def.parent());
let data = &parent_data.type_or_consts[def.local_id()];
let resolver = def.parent().resolver(db.upcast());
- let ctx = TyLoweringContext::new(db, &resolver);
+ let ctx = TyLoweringContext::new(db, &resolver, def.parent().into());
match data {
TypeOrConstParamData::TypeParamData(_) => {
never!();
@@ -1874,8 +1932,8 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option<
let _cx = stdx::panic_context::enter(format!(
"impl_trait_query({impl_id:?} -> {impl_loc:?} -> {impl_data:?})"
));
- let ctx =
- TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
+ let ctx = TyLoweringContext::new(db, &resolver, impl_id.into())
+ .with_type_param_mode(ParamLoweringMode::Variable);
let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders();
let target_trait = impl_data.target_trait.as_ref()?;
Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, Some(self_ty))?))
@@ -1888,7 +1946,7 @@ pub(crate) fn return_type_impl_traits(
// FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
let data = db.function_data(def);
let resolver = def.resolver(db.upcast());
- let ctx_ret = TyLoweringContext::new(db, &resolver)
+ let ctx_ret = TyLoweringContext::new(db, &resolver, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
let _ret = ctx_ret.lower_ty(&data.ret_type);
@@ -1923,7 +1981,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
arg: &'a GenericArg,
this: &mut T,
for_type: impl FnOnce(&mut T, &TypeRef) -> Ty + 'a,
- for_const: impl FnOnce(&mut T, &ConstRefOrPath, Ty) -> Const + 'a,
+ for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a,
) -> Option<crate::GenericArg> {
let kind = match kind_id {
Either::Left(_) => ParamKind::Type,
@@ -1948,10 +2006,10 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
// as types. Maybe here is not the best place to do it, but
// it works.
if let TypeRef::Path(p) = t {
- let p = p.mod_path();
+ let p = p.mod_path()?;
if p.kind == PathKind::Plain {
if let [n] = p.segments() {
- let c = ConstRefOrPath::Path(n.clone());
+ let c = ConstRef::Path(n.clone());
return Some(
GenericArgData::Const(for_const(this, &c, c_ty)).intern(Interner),
);
@@ -1967,18 +2025,47 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
pub(crate) fn const_or_path_to_chalk(
db: &dyn HirDatabase,
resolver: &Resolver,
+ owner: TypeOwnerId,
expected_ty: Ty,
- value: &ConstRefOrPath,
+ value: &ConstRef,
mode: ParamLoweringMode,
args: impl FnOnce() -> Generics,
debruijn: DebruijnIndex,
) -> Const {
match value {
- ConstRefOrPath::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()),
- ConstRefOrPath::Path(n) => {
+ ConstRef::Scalar(s) => intern_const_ref(db, s, expected_ty, resolver.krate()),
+ ConstRef::Path(n) => {
let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
- path_to_const(db, resolver, &path, mode, args, debruijn)
- .unwrap_or_else(|| unknown_const(expected_ty))
+ path_to_const(
+ db,
+ resolver,
+ &Path::from_known_path_with_no_generic(path),
+ mode,
+ args,
+ debruijn,
+ expected_ty.clone(),
+ )
+ .unwrap_or_else(|| unknown_const(expected_ty))
+ }
+ &ConstRef::Complex(it) => {
+ let crate_data = &db.crate_graph()[owner.module(db.upcast()).krate()];
+ if crate_data.env.get("__ra_is_test_fixture").is_none() && crate_data.origin.is_local()
+ {
+ // FIXME: current `InTypeConstId` is very unstable, so we only use it in non local crate
+ // that are unlikely to be edited.
+ return unknown_const(expected_ty);
+ }
+ let c = db
+ .intern_in_type_const(InTypeConstLoc {
+ id: it,
+ owner,
+ thing: Box::new(InTypeConstIdMetadata(expected_ty.clone())),
+ })
+ .into();
+ intern_const_scalar(
+ ConstScalar::UnevaluatedConst(c, Substitution::empty(Interner)),
+ expected_ty,
+ )
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index f3a27632b..ab6430e8f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -2,25 +2,28 @@
//! For details about how this works in rustc, see the method lookup page in the
//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
-use std::{ops::ControlFlow, sync::Arc};
+use std::ops::ControlFlow;
use base_db::{CrateId, Edition};
-use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex};
+use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause};
use hir_def::{
- data::ImplData, item_scope::ItemScope, lang_item::LangItem, nameres::DefMap, AssocItemId,
- BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId,
- ModuleId, TraitId,
+ data::{adt::StructFlags, ImplData},
+ item_scope::ItemScope,
+ nameres::DefMap,
+ AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup,
+ ModuleDefId, ModuleId, TraitId,
};
use hir_expand::name::Name;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{smallvec, SmallVec};
use stdx::never;
+use triomphe::Arc;
use crate::{
autoderef::{self, AutoderefKind},
db::HirDatabase,
from_chalk_trait_id, from_foreign_def_id,
- infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast},
+ infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast},
primitive::{FloatTy, IntTy, UintTy},
static_lifetime, to_chalk_trait_id,
utils::all_super_traits,
@@ -147,31 +150,30 @@ impl TraitImpls {
Arc::new(impls)
}
- pub(crate) fn trait_impls_in_block_query(
- db: &dyn HirDatabase,
- block: BlockId,
- ) -> Option<Arc<Self>> {
+ pub(crate) fn trait_impls_in_block_query(db: &dyn HirDatabase, block: BlockId) -> Arc<Self> {
let _p = profile::span("trait_impls_in_block_query");
let mut impls = Self { map: FxHashMap::default() };
- let block_def_map = db.block_def_map(block)?;
+ let block_def_map = db.block_def_map(block);
impls.collect_def_map(db, &block_def_map);
impls.shrink_to_fit();
- Some(Arc::new(impls))
+ Arc::new(impls)
}
- pub(crate) fn trait_impls_in_deps_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
+ pub(crate) fn trait_impls_in_deps_query(
+ db: &dyn HirDatabase,
+ krate: CrateId,
+ ) -> Arc<[Arc<Self>]> {
let _p = profile::span("trait_impls_in_deps_query").detail(|| format!("{krate:?}"));
let crate_graph = db.crate_graph();
- let mut res = Self { map: FxHashMap::default() };
-
- for krate in crate_graph.transitive_deps(krate) {
- res.merge(&db.trait_impls_in_crate(krate));
- }
- res.shrink_to_fit();
-
- Arc::new(res)
+ // FIXME: use `Arc::from_iter` when it becomes available
+ Arc::from(
+ crate_graph
+ .transitive_deps(krate)
+ .map(|krate| db.trait_impls_in_crate(krate))
+ .collect::<Vec<_>>(),
+ )
}
fn shrink_to_fit(&mut self) {
@@ -185,6 +187,15 @@ impl TraitImpls {
fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) {
for (_module_id, module_data) in def_map.modules() {
for impl_id in module_data.scope.impls() {
+ // Reservation impls should be ignored during trait resolution, so we never need
+ // them during type analysis. See rust-lang/rust#64631 for details.
+ //
+ // FIXME: Reservation impls should be considered during coherence checks. If we are
+ // (ever) to implement coherence checks, this filtering should be done by the trait
+ // solver.
+ if db.attrs(impl_id.into()).by_key("rustc_reservation_impl").exists() {
+ continue;
+ }
let target_trait = match db.impl_trait(impl_id) {
Some(tr) => tr.skip_binders().hir_trait_id(),
None => continue,
@@ -210,15 +221,6 @@ impl TraitImpls {
}
}
- fn merge(&mut self, other: &Self) {
- for (trait_, other_map) in &other.map {
- let map = self.map.entry(*trait_).or_default();
- for (fp, impls) in other_map {
- map.entry(*fp).or_default().extend(impls);
- }
- }
- }
-
/// Queries all trait impls for the given type.
pub fn for_self_ty_without_blanket_impls(
&self,
@@ -271,6 +273,7 @@ pub struct InherentImpls {
impl InherentImpls {
pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
+ let _p = profile::span("inherent_impls_in_crate_query").detail(|| format!("{krate:?}"));
let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
let crate_def_map = db.crate_def_map(krate);
@@ -280,17 +283,15 @@ impl InherentImpls {
Arc::new(impls)
}
- pub(crate) fn inherent_impls_in_block_query(
- db: &dyn HirDatabase,
- block: BlockId,
- ) -> Option<Arc<Self>> {
+ pub(crate) fn inherent_impls_in_block_query(db: &dyn HirDatabase, block: BlockId) -> Arc<Self> {
+ let _p = profile::span("inherent_impls_in_block_query");
let mut impls = Self { map: FxHashMap::default(), invalid_impls: Vec::default() };
- if let Some(block_def_map) = db.block_def_map(block) {
- impls.collect_def_map(db, &block_def_map);
- impls.shrink_to_fit();
- return Some(Arc::new(impls));
- }
- None
+
+ let block_def_map = db.block_def_map(block);
+ impls.collect_def_map(db, &block_def_map);
+ impls.shrink_to_fit();
+
+ Arc::new(impls)
}
fn shrink_to_fit(&mut self) {
@@ -404,12 +405,14 @@ pub fn def_crates(
match ty.kind(Interner) {
&TyKind::Adt(AdtId(def_id), _) => {
let rustc_has_incoherent_inherent_impls = match def_id {
- hir_def::AdtId::StructId(id) => {
- db.struct_data(id).rustc_has_incoherent_inherent_impls
- }
- hir_def::AdtId::UnionId(id) => {
- db.union_data(id).rustc_has_incoherent_inherent_impls
- }
+ hir_def::AdtId::StructId(id) => db
+ .struct_data(id)
+ .flags
+ .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL),
+ hir_def::AdtId::UnionId(id) => db
+ .union_data(id)
+ .flags
+ .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL),
hir_def::AdtId::EnumId(id) => db.enum_data(id).rustc_has_incoherent_inherent_impls,
};
Some(if rustc_has_incoherent_inherent_impls {
@@ -449,55 +452,6 @@ pub fn def_crates(
}
}
-pub fn lang_items_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, LangItem)> {
- use hir_expand::name;
- use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering};
- Some(match op {
- BinaryOp::LogicOp(_) => return None,
- BinaryOp::ArithOp(aop) => match aop {
- ArithOp::Add => (name![add], LangItem::Add),
- ArithOp::Mul => (name![mul], LangItem::Mul),
- ArithOp::Sub => (name![sub], LangItem::Sub),
- ArithOp::Div => (name![div], LangItem::Div),
- ArithOp::Rem => (name![rem], LangItem::Rem),
- ArithOp::Shl => (name![shl], LangItem::Shl),
- ArithOp::Shr => (name![shr], LangItem::Shr),
- ArithOp::BitXor => (name![bitxor], LangItem::BitXor),
- ArithOp::BitOr => (name![bitor], LangItem::BitOr),
- ArithOp::BitAnd => (name![bitand], LangItem::BitAnd),
- },
- BinaryOp::Assignment { op: Some(aop) } => match aop {
- ArithOp::Add => (name![add_assign], LangItem::AddAssign),
- ArithOp::Mul => (name![mul_assign], LangItem::MulAssign),
- ArithOp::Sub => (name![sub_assign], LangItem::SubAssign),
- ArithOp::Div => (name![div_assign], LangItem::DivAssign),
- ArithOp::Rem => (name![rem_assign], LangItem::RemAssign),
- ArithOp::Shl => (name![shl_assign], LangItem::ShlAssign),
- ArithOp::Shr => (name![shr_assign], LangItem::ShrAssign),
- ArithOp::BitXor => (name![bitxor_assign], LangItem::BitXorAssign),
- ArithOp::BitOr => (name![bitor_assign], LangItem::BitOrAssign),
- ArithOp::BitAnd => (name![bitand_assign], LangItem::BitAndAssign),
- },
- BinaryOp::CmpOp(cop) => match cop {
- CmpOp::Eq { negated: false } => (name![eq], LangItem::PartialEq),
- CmpOp::Eq { negated: true } => (name![ne], LangItem::PartialEq),
- CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
- (name![le], LangItem::PartialOrd)
- }
- CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
- (name![lt], LangItem::PartialOrd)
- }
- CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
- (name![ge], LangItem::PartialOrd)
- }
- CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
- (name![gt], LangItem::PartialOrd)
- }
- },
- BinaryOp::Assignment { op: None } => return None,
- })
-}
-
/// Look up the method with the given name.
pub(crate) fn lookup_method(
db: &dyn HirDatabase,
@@ -600,9 +554,9 @@ impl ReceiverAdjustments {
}
}
if let Some(m) = self.autoref {
- ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner);
- adjust
- .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() });
+ let a = Adjustment::borrow(m, ty);
+ ty = a.target.clone();
+ adjust.push(a);
}
if self.unsize_array {
ty = 'x: {
@@ -616,7 +570,7 @@ impl ReceiverAdjustments {
.intern(Interner);
}
}
- never!("unsize_array with non-reference-to-array {:?}", ty);
+ // FIXME: report diagnostic if array unsizing happens without indirection.
ty
};
adjust.push(Adjustment {
@@ -692,6 +646,39 @@ pub fn lookup_impl_const(
.unwrap_or((const_id, subs))
}
+/// Checks if the self parameter of `Trait` method is the `dyn Trait` and we should
+/// call the method using the vtable.
+pub fn is_dyn_method(
+ db: &dyn HirDatabase,
+ _env: Arc<TraitEnvironment>,
+ func: FunctionId,
+ fn_subst: Substitution,
+) -> Option<usize> {
+ let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
+ return None;
+ };
+ let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
+ let fn_params = fn_subst.len(Interner) - trait_params;
+ let trait_ref = TraitRef {
+ trait_id: to_chalk_trait_id(trait_id),
+ substitution: Substitution::from_iter(Interner, fn_subst.iter(Interner).skip(fn_params)),
+ };
+ let self_ty = trait_ref.self_type_parameter(Interner);
+ if let TyKind::Dyn(d) = self_ty.kind(Interner) {
+ let is_my_trait_in_bounds =
+ d.bounds.skip_binders().as_slice(Interner).iter().any(|x| match x.skip_binders() {
+ // rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter
+ // what the generics are, we are sure that the method is come from the vtable.
+ WhereClause::Implemented(tr) => tr.trait_id == trait_ref.trait_id,
+ _ => false,
+ });
+ if is_my_trait_in_bounds {
+ return Some(fn_params);
+ }
+ }
+ None
+}
+
/// Looks up the impl method that actually runs for the trait method `func`.
///
/// Returns `func` if it's not a method defined in a trait or the lookup failed.
@@ -701,9 +688,8 @@ pub fn lookup_impl_method(
func: FunctionId,
fn_subst: Substitution,
) -> (FunctionId, Substitution) {
- let trait_id = match func.lookup(db.upcast()).container {
- ItemContainerId::TraitId(id) => id,
- _ => return (func, fn_subst),
+ let ItemContainerId::TraitId(trait_id) = func.lookup(db.upcast()).container else {
+ return (func, fn_subst)
};
let trait_params = db.generic_params(trait_id.into()).type_or_consts.len();
let fn_params = fn_subst.len(Interner) - trait_params;
@@ -713,7 +699,7 @@ pub fn lookup_impl_method(
};
let name = &db.function_data(func).name;
- lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
+ let Some((impl_fn, impl_subst)) = lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
.and_then(|assoc| {
if let (AssocItemId::FunctionId(id), subst) = assoc {
Some((id, subst))
@@ -721,7 +707,16 @@ pub fn lookup_impl_method(
None
}
})
- .unwrap_or((func, fn_subst))
+ else {
+ return (func, fn_subst);
+ };
+ (
+ impl_fn,
+ Substitution::from_iter(
+ Interner,
+ fn_subst.iter(Interner).take(fn_params).chain(impl_subst.iter(Interner)),
+ ),
+ )
}
fn lookup_impl_assoc_item_for_trait_ref(
@@ -730,10 +725,20 @@ fn lookup_impl_assoc_item_for_trait_ref(
env: Arc<TraitEnvironment>,
name: &Name,
) -> Option<(AssocItemId, Substitution)> {
+ let hir_trait_id = trait_ref.hir_trait_id();
let self_ty = trait_ref.self_type_parameter(Interner);
let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?;
let impls = db.trait_impls_in_deps(env.krate);
- let impls = impls.for_trait_and_self_ty(trait_ref.hir_trait_id(), self_ty_fp);
+ let self_impls = match self_ty.kind(Interner) {
+ TyKind::Adt(id, _) => {
+ id.0.module(db.upcast()).containing_block().map(|x| db.trait_impls_in_block(x))
+ }
+ _ => None,
+ };
+ let impls = impls
+ .iter()
+ .chain(self_impls.as_ref())
+ .flat_map(|impls| impls.for_trait_and_self_ty(hir_trait_id, self_ty_fp));
let table = InferenceTable::new(db, env);
@@ -759,9 +764,8 @@ fn find_matching_impl(
actual_trait_ref: TraitRef,
) -> Option<(Arc<ImplData>, Substitution)> {
let db = table.db;
- loop {
- let impl_ = impls.next()?;
- let r = table.run_in_snapshot(|table| {
+ impls.find_map(|impl_| {
+ table.run_in_snapshot(|table| {
let impl_data = db.impl_data(impl_);
let impl_substs =
TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build();
@@ -778,12 +782,11 @@ fn find_matching_impl(
.into_iter()
.map(|b| b.cast(Interner));
let goal = crate::Goal::all(Interner, wcs);
- table.try_obligation(goal).map(|_| (impl_data, table.resolve_completely(impl_substs)))
- });
- if r.is_some() {
- break r;
- }
- }
+ table.try_obligation(goal.clone())?;
+ table.register_obligation(goal);
+ Some((impl_data, table.resolve_completely(impl_substs)))
+ })
+ })
}
fn is_inherent_impl_coherent(
@@ -824,12 +827,14 @@ fn is_inherent_impl_coherent(
| TyKind::Scalar(_) => true,
&TyKind::Adt(AdtId(adt), _) => match adt {
- hir_def::AdtId::StructId(it) => {
- db.struct_data(it).rustc_has_incoherent_inherent_impls
- }
- hir_def::AdtId::UnionId(it) => {
- db.union_data(it).rustc_has_incoherent_inherent_impls
- }
+ hir_def::AdtId::StructId(id) => db
+ .struct_data(id)
+ .flags
+ .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL),
+ hir_def::AdtId::UnionId(id) => db
+ .union_data(id)
+ .flags
+ .contains(StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL),
hir_def::AdtId::EnumId(it) => db.enum_data(it).rustc_has_incoherent_inherent_impls,
},
TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| {
@@ -963,7 +968,14 @@ fn iterate_method_candidates_with_autoref(
)
};
- iterate_method_candidates_by_receiver(receiver_ty, first_adjustment.clone())?;
+ let mut maybe_reborrowed = first_adjustment.clone();
+ if let Some((_, _, m)) = receiver_ty.value.as_reference() {
+ // Prefer reborrow of references to move
+ maybe_reborrowed.autoref = Some(m);
+ maybe_reborrowed.autoderefs += 1;
+ }
+
+ iterate_method_candidates_by_receiver(receiver_ty, maybe_reborrowed)?;
let refed = Canonical {
value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone())
@@ -1108,7 +1120,7 @@ fn iterate_trait_method_candidates(
};
if !known_implemented {
let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty);
- if db.trait_solve(env.krate, goal.cast(Interner)).is_none() {
+ if db.trait_solve(env.krate, env.block, goal.cast(Interner)).is_none() {
continue 'traits;
}
}
@@ -1180,23 +1192,19 @@ fn iterate_inherent_methods(
};
while let Some(block_id) = block {
- if let Some(impls) = db.inherent_impls_in_block(block_id) {
- impls_for_self_ty(
- &impls,
- self_ty,
- table,
- name,
- receiver_ty,
- receiver_adjustments.clone(),
- module,
- callback,
- )?;
- }
+ let impls = db.inherent_impls_in_block(block_id);
+ impls_for_self_ty(
+ &impls,
+ self_ty,
+ table,
+ name,
+ receiver_ty,
+ receiver_adjustments.clone(),
+ module,
+ callback,
+ )?;
- block = db
- .block_def_map(block_id)
- .and_then(|map| map.parent())
- .and_then(|module| module.containing_block());
+ block = db.block_def_map(block_id).parent().and_then(|module| module.containing_block());
}
for krate in def_crates {
@@ -1274,7 +1282,7 @@ fn iterate_inherent_methods(
}
/// Returns the receiver type for the index trait call.
-pub fn resolve_indexing_op(
+pub(crate) fn resolve_indexing_op(
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
ty: Canonical<Ty>,
@@ -1284,8 +1292,11 @@ pub fn resolve_indexing_op(
let ty = table.instantiate_canonical(ty);
let deref_chain = autoderef_method_receiver(&mut table, ty);
for (ty, adj) in deref_chain {
- let goal = generic_implements_goal(db, env.clone(), index_trait, &ty);
- if db.trait_solve(env.krate, goal.cast(Interner)).is_some() {
+ let goal = generic_implements_goal(db, table.trait_env.clone(), index_trait, &ty);
+ if db
+ .trait_solve(table.trait_env.krate, table.trait_env.block, goal.cast(Interner))
+ .is_some()
+ {
return Some(adj);
}
}
@@ -1310,14 +1321,12 @@ fn is_valid_candidate(
) -> IsValidCandidate {
let db = table.db;
match item {
- AssocItemId::FunctionId(m) => {
- is_valid_fn_candidate(table, m, name, receiver_ty, self_ty, visible_from_module)
+ AssocItemId::FunctionId(f) => {
+ is_valid_fn_candidate(table, f, name, receiver_ty, self_ty, visible_from_module)
}
AssocItemId::ConstId(c) => {
- let data = db.const_data(c);
check_that!(receiver_ty.is_none());
-
- check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n)));
+ check_that!(name.map_or(true, |n| db.const_data(c).name.as_ref() == Some(n)));
if let Some(from_module) = visible_from_module {
if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
@@ -1441,7 +1450,7 @@ pub fn implements_trait(
trait_: TraitId,
) -> bool {
let goal = generic_implements_goal(db, env.clone(), trait_, ty);
- let solution = db.trait_solve(env.krate, goal.cast(Interner));
+ let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner));
solution.is_some()
}
@@ -1453,7 +1462,7 @@ pub fn implements_trait_unique(
trait_: TraitId,
) -> bool {
let goal = generic_implements_goal(db, env.clone(), trait_, ty);
- let solution = db.trait_solve(env.krate, goal.cast(Interner));
+ let solution = db.trait_solve(env.krate, env.block, goal.cast(Interner));
matches!(solution, Some(crate::Solution::Unique(_)))
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
index 7c1cbbdf5..2345bab0b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
@@ -3,12 +3,15 @@
use std::{fmt::Display, iter};
use crate::{
- infer::PointerCast, Const, ConstScalar, InferenceResult, Interner, MemoryMap, Substitution, Ty,
+ consteval::usize_const, db::HirDatabase, display::HirDisplay, infer::PointerCast,
+ lang_items::is_box, mapping::ToChalk, CallableDefId, ClosureId, Const, ConstScalar,
+ InferenceResult, Interner, MemoryMap, Substitution, Ty, TyKind,
};
+use base_db::CrateId;
use chalk_ir::Mutability;
use hir_def::{
- expr::{BindingId, Expr, ExprId, Ordering, PatId},
- DefWithBodyId, FieldId, UnionId, VariantId,
+ hir::{BindingId, Expr, ExprId, Ordering, PatId},
+ DefWithBodyId, FieldId, StaticId, UnionId, VariantId,
};
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
@@ -16,12 +19,19 @@ mod eval;
mod lower;
mod borrowck;
mod pretty;
+mod monomorphization;
pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
-pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError};
-pub use lower::{lower_to_mir, mir_body_query, mir_body_recover, MirLowerError};
+pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError, VTableMap};
+pub use lower::{
+ lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
+};
+pub use monomorphization::{
+ monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query,
+ monomorphized_mir_body_query, monomorphized_mir_body_recover,
+};
use smallvec::{smallvec, SmallVec};
-use stdx::impl_from;
+use stdx::{impl_from, never};
use super::consteval::{intern_const_scalar, try_const_usize};
@@ -32,7 +42,7 @@ fn return_slot() -> LocalId {
LocalId::from_raw(RawIdx::from(0))
}
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Local {
pub ty: Ty,
}
@@ -52,7 +62,7 @@ pub struct Local {
/// This is what is implemented in miri today. Are these the semantics we want for MIR? Is this
/// something we can even decide without knowing more about Rust's memory model?
///
-/// **Needs clarifiation:** Is loading a place that has its variant index set well-formed? Miri
+/// **Needs clarification:** Is loading a place that has its variant index set well-formed? Miri
/// currently implements it, but it seems like this may be something to check against in the
/// validator.
#[derive(Debug, PartialEq, Eq, Clone)]
@@ -73,6 +83,9 @@ pub enum Operand {
Move(Place),
/// Constants are already semantically values, and remain unchanged.
Constant(Const),
+ /// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc
+ /// handles it with the `Constant` variant somehow.
+ Static(StaticId),
}
impl Operand {
@@ -87,31 +100,141 @@ impl Operand {
fn const_zst(ty: Ty) -> Operand {
Self::from_bytes(vec![], ty)
}
+
+ fn from_fn(
+ db: &dyn HirDatabase,
+ func_id: hir_def::FunctionId,
+ generic_args: Substitution,
+ ) -> Operand {
+ let ty =
+ chalk_ir::TyKind::FnDef(CallableDefId::FunctionId(func_id).to_chalk(db), generic_args)
+ .intern(Interner);
+ Operand::from_bytes(vec![], ty)
+ }
}
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ProjectionElem<V, T> {
Deref,
Field(FieldId),
- TupleField(usize),
+ // FIXME: get rid of this, and use FieldId for tuples and closures
+ TupleOrClosureField(usize),
Index(V),
- ConstantIndex { offset: u64, min_length: u64, from_end: bool },
- Subslice { from: u64, to: u64, from_end: bool },
+ ConstantIndex { offset: u64, from_end: bool },
+ Subslice { from: u64, to: u64 },
//Downcast(Option<Symbol>, VariantIdx),
OpaqueCast(T),
}
+impl<V, T> ProjectionElem<V, T> {
+ pub fn projected_ty(
+ &self,
+ base: Ty,
+ db: &dyn HirDatabase,
+ closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty,
+ krate: CrateId,
+ ) -> Ty {
+ match self {
+ ProjectionElem::Deref => match &base.data(Interner).kind {
+ TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
+ TyKind::Adt(adt, subst) if is_box(db, adt.0) => {
+ subst.at(Interner, 0).assert_ty_ref(Interner).clone()
+ }
+ _ => {
+ never!("Overloaded deref on type {} is not a projection", base.display(db));
+ return TyKind::Error.intern(Interner);
+ }
+ },
+ ProjectionElem::Field(f) => match &base.data(Interner).kind {
+ TyKind::Adt(_, subst) => {
+ db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
+ }
+ _ => {
+ never!("Only adt has field");
+ return TyKind::Error.intern(Interner);
+ }
+ },
+ ProjectionElem::TupleOrClosureField(f) => match &base.data(Interner).kind {
+ TyKind::Tuple(_, subst) => subst
+ .as_slice(Interner)
+ .get(*f)
+ .map(|x| x.assert_ty_ref(Interner))
+ .cloned()
+ .unwrap_or_else(|| {
+ never!("Out of bound tuple field");
+ TyKind::Error.intern(Interner)
+ }),
+ TyKind::Closure(id, subst) => closure_field(*id, subst, *f),
+ _ => {
+ never!("Only tuple or closure has tuple or closure field");
+ return TyKind::Error.intern(Interner);
+ }
+ },
+ ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => {
+ match &base.data(Interner).kind {
+ TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(),
+ _ => {
+ never!("Overloaded index is not a projection");
+ return TyKind::Error.intern(Interner);
+ }
+ }
+ }
+ &ProjectionElem::Subslice { from, to } => match &base.data(Interner).kind {
+ TyKind::Array(inner, c) => {
+ let next_c = usize_const(
+ db,
+ match try_const_usize(db, c) {
+ None => None,
+ Some(x) => x.checked_sub(u128::from(from + to)),
+ },
+ krate,
+ );
+ TyKind::Array(inner.clone(), next_c).intern(Interner)
+ }
+ TyKind::Slice(_) => base.clone(),
+ _ => {
+ never!("Subslice projection should only happen on slice and array");
+ return TyKind::Error.intern(Interner);
+ }
+ },
+ ProjectionElem::OpaqueCast(_) => {
+ never!("We don't emit these yet");
+ return TyKind::Error.intern(Interner);
+ }
+ }
+ }
+}
+
type PlaceElem = ProjectionElem<LocalId, Ty>;
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Place {
pub local: LocalId,
- pub projection: Vec<PlaceElem>,
+ pub projection: Box<[PlaceElem]>,
+}
+
+impl Place {
+ fn is_parent(&self, child: &Place) -> bool {
+ self.local == child.local && child.projection.starts_with(&self.projection)
+ }
+
+ fn iterate_over_parents(&self) -> impl Iterator<Item = Place> + '_ {
+ (0..self.projection.len())
+ .map(|x| &self.projection[0..x])
+ .map(|x| Place { local: self.local, projection: x.to_vec().into() })
+ }
+
+ fn project(&self, projection: PlaceElem) -> Place {
+ Place {
+ local: self.local,
+ projection: self.projection.iter().cloned().chain([projection]).collect(),
+ }
+ }
}
impl From<LocalId> for Place {
fn from(local: LocalId) -> Self {
- Self { local, projection: vec![] }
+ Self { local, projection: vec![].into() }
}
}
@@ -123,7 +246,7 @@ pub enum AggregateKind {
Tuple(Ty),
Adt(VariantId, Substitution),
Union(UnionId, FieldId),
- //Closure(LocalDefId, SubstsRef),
+ Closure(Ty),
//Generator(LocalDefId, SubstsRef, Movability),
}
@@ -197,7 +320,13 @@ impl SwitchTargets {
}
#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum Terminator {
+pub struct Terminator {
+ span: MirSpan,
+ kind: TerminatorKind,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum TerminatorKind {
/// Block has one successor; we continue execution there.
Goto { target: BasicBlockId },
@@ -320,7 +449,7 @@ pub enum Terminator {
/// These are owned by the callee, which is free to modify them.
/// This allows the memory occupied by "by-value" arguments to be
/// reused across function calls without duplicating the contents.
- args: Vec<Operand>,
+ args: Box<[Operand]>,
/// Where the returned value will be written
destination: Place,
/// Where to go after this call returns. If none, the call necessarily diverges.
@@ -418,7 +547,7 @@ pub enum Terminator {
},
}
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
pub enum BorrowKind {
/// Data must be immutable and is aliasable.
Shared,
@@ -564,6 +693,20 @@ pub enum BinOp {
Offset,
}
+impl BinOp {
+ fn run_compare<T: PartialEq + PartialOrd>(&self, l: T, r: T) -> bool {
+ match self {
+ BinOp::Ge => l >= r,
+ BinOp::Gt => l > r,
+ BinOp::Le => l <= r,
+ BinOp::Lt => l < r,
+ BinOp::Eq => l == r,
+ BinOp::Ne => l != r,
+ x => panic!("`run_compare` called on operator {x:?}"),
+ }
+ }
+}
+
impl Display for BinOp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
@@ -588,32 +731,32 @@ impl Display for BinOp {
}
}
-impl From<hir_def::expr::ArithOp> for BinOp {
- fn from(value: hir_def::expr::ArithOp) -> Self {
+impl From<hir_def::hir::ArithOp> for BinOp {
+ fn from(value: hir_def::hir::ArithOp) -> Self {
match value {
- hir_def::expr::ArithOp::Add => BinOp::Add,
- hir_def::expr::ArithOp::Mul => BinOp::Mul,
- hir_def::expr::ArithOp::Sub => BinOp::Sub,
- hir_def::expr::ArithOp::Div => BinOp::Div,
- hir_def::expr::ArithOp::Rem => BinOp::Rem,
- hir_def::expr::ArithOp::Shl => BinOp::Shl,
- hir_def::expr::ArithOp::Shr => BinOp::Shr,
- hir_def::expr::ArithOp::BitXor => BinOp::BitXor,
- hir_def::expr::ArithOp::BitOr => BinOp::BitOr,
- hir_def::expr::ArithOp::BitAnd => BinOp::BitAnd,
+ hir_def::hir::ArithOp::Add => BinOp::Add,
+ hir_def::hir::ArithOp::Mul => BinOp::Mul,
+ hir_def::hir::ArithOp::Sub => BinOp::Sub,
+ hir_def::hir::ArithOp::Div => BinOp::Div,
+ hir_def::hir::ArithOp::Rem => BinOp::Rem,
+ hir_def::hir::ArithOp::Shl => BinOp::Shl,
+ hir_def::hir::ArithOp::Shr => BinOp::Shr,
+ hir_def::hir::ArithOp::BitXor => BinOp::BitXor,
+ hir_def::hir::ArithOp::BitOr => BinOp::BitOr,
+ hir_def::hir::ArithOp::BitAnd => BinOp::BitAnd,
}
}
}
-impl From<hir_def::expr::CmpOp> for BinOp {
- fn from(value: hir_def::expr::CmpOp) -> Self {
+impl From<hir_def::hir::CmpOp> for BinOp {
+ fn from(value: hir_def::hir::CmpOp) -> Self {
match value {
- hir_def::expr::CmpOp::Eq { negated: false } => BinOp::Eq,
- hir_def::expr::CmpOp::Eq { negated: true } => BinOp::Ne,
- hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
- hir_def::expr::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
- hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
- hir_def::expr::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
+ hir_def::hir::CmpOp::Eq { negated: false } => BinOp::Eq,
+ hir_def::hir::CmpOp::Eq { negated: true } => BinOp::Ne,
+ hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: false } => BinOp::Ge,
+ hir_def::hir::CmpOp::Ord { ordering: Ordering::Greater, strict: true } => BinOp::Gt,
+ hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: false } => BinOp::Le,
+ hir_def::hir::CmpOp::Ord { ordering: Ordering::Less, strict: true } => BinOp::Lt,
}
}
}
@@ -642,7 +785,6 @@ pub enum CastKind {
FloatToInt,
FloatToFloat,
IntToFloat,
- PtrToPtr,
FnPtrToPtr,
}
@@ -653,13 +795,8 @@ pub enum Rvalue {
/// Creates an array where each element is the value of the operand.
///
- /// This is the cause of a bug in the case where the repetition count is zero because the value
- /// is not dropped, see [#74836].
- ///
/// Corresponds to source code like `[x; 32]`.
- ///
- /// [#74836]: https://github.com/rust-lang/rust/issues/74836
- //Repeat(Operand, ty::Const),
+ Repeat(Operand, Const),
/// Creates a reference of the indicated kind to the place.
///
@@ -768,7 +905,7 @@ pub enum Rvalue {
///
/// Disallowed after deaggregation for all aggregate kinds except `Array` and `Generator`. After
/// generator lowering, `Generator` aggregate kinds are disallowed too.
- Aggregate(AggregateKind, Vec<Operand>),
+ Aggregate(AggregateKind, Box<[Operand]>),
/// Transmutes a `*mut u8` into shallow-initialized `Box<T>`.
///
@@ -777,6 +914,9 @@ pub enum Rvalue {
/// affects alias analysis.
ShallowInitBox(Operand, Ty),
+ /// NON STANDARD: allocates memory with the type's layout, and shallow init the box with the resulting pointer.
+ ShallowInitBoxWithAlloc(Ty),
+
/// A CopyForDeref is equivalent to a read from a place at the
/// codegen level, but is treated specially by drop elaboration. When such a read happens, it
/// is guaranteed (via nature of the mir_opt `Derefer` in rustc_mir_transform/src/deref_separator)
@@ -816,7 +956,7 @@ pub struct Statement {
pub span: MirSpan,
}
-#[derive(Debug, Default, PartialEq, Eq)]
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct BasicBlock {
/// List of statements in this block.
pub statements: Vec<Statement>,
@@ -838,19 +978,118 @@ pub struct BasicBlock {
pub is_cleanup: bool,
}
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MirBody {
pub basic_blocks: Arena<BasicBlock>,
pub locals: Arena<Local>,
pub start_block: BasicBlockId,
pub owner: DefWithBodyId,
- pub arg_count: usize,
pub binding_locals: ArenaMap<BindingId, LocalId>,
pub param_locals: Vec<LocalId>,
+ /// This field stores the closures directly owned by this body. It is used
+ /// in traversing every mir body.
+ pub closures: Vec<ClosureId>,
}
-fn const_as_usize(c: &Const) -> usize {
- try_const_usize(c).unwrap() as usize
+impl MirBody {
+ fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) {
+ fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) {
+ match op {
+ Operand::Copy(p) | Operand::Move(p) => {
+ f(p);
+ }
+ Operand::Constant(_) | Operand::Static(_) => (),
+ }
+ }
+ for (_, block) in self.basic_blocks.iter_mut() {
+ for statement in &mut block.statements {
+ match &mut statement.kind {
+ StatementKind::Assign(p, r) => {
+ f(p);
+ match r {
+ Rvalue::ShallowInitBoxWithAlloc(_) => (),
+ Rvalue::ShallowInitBox(o, _)
+ | Rvalue::UnaryOp(_, o)
+ | Rvalue::Cast(_, o, _)
+ | Rvalue::Repeat(o, _)
+ | Rvalue::Use(o) => for_operand(o, &mut f),
+ Rvalue::CopyForDeref(p)
+ | Rvalue::Discriminant(p)
+ | Rvalue::Len(p)
+ | Rvalue::Ref(_, p) => f(p),
+ Rvalue::CheckedBinaryOp(_, o1, o2) => {
+ for_operand(o1, &mut f);
+ for_operand(o2, &mut f);
+ }
+ Rvalue::Aggregate(_, ops) => {
+ for op in ops.iter_mut() {
+ for_operand(op, &mut f);
+ }
+ }
+ }
+ }
+ StatementKind::Deinit(p) => f(p),
+ StatementKind::StorageLive(_)
+ | StatementKind::StorageDead(_)
+ | StatementKind::Nop => (),
+ }
+ }
+ match &mut block.terminator {
+ Some(x) => match &mut x.kind {
+ TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f),
+ TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. }
+ | TerminatorKind::Goto { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable => (),
+ TerminatorKind::Drop { place, .. } => {
+ f(place);
+ }
+ TerminatorKind::DropAndReplace { place, value, .. } => {
+ f(place);
+ for_operand(value, &mut f);
+ }
+ TerminatorKind::Call { func, args, destination, .. } => {
+ for_operand(func, &mut f);
+ args.iter_mut().for_each(|x| for_operand(x, &mut f));
+ f(destination);
+ }
+ TerminatorKind::Assert { cond, .. } => {
+ for_operand(cond, &mut f);
+ }
+ TerminatorKind::Yield { value, resume_arg, .. } => {
+ for_operand(value, &mut f);
+ f(resume_arg);
+ }
+ },
+ None => (),
+ }
+ }
+ }
+
+ fn shrink_to_fit(&mut self) {
+ let MirBody {
+ basic_blocks,
+ locals,
+ start_block: _,
+ owner: _,
+ binding_locals,
+ param_locals,
+ closures,
+ } = self;
+ basic_blocks.shrink_to_fit();
+ locals.shrink_to_fit();
+ binding_locals.shrink_to_fit();
+ param_locals.shrink_to_fit();
+ closures.shrink_to_fit();
+ for (_, b) in basic_blocks.iter_mut() {
+ let BasicBlock { statements, terminator: _, is_cleanup: _ } = b;
+ statements.shrink_to_fit();
+ }
+ }
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
index c8729af86..a5dd0182e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
@@ -3,17 +3,20 @@
// Currently it is an ad-hoc implementation, only useful for mutability analysis. Feel free to remove all of these
// if needed for implementing a proper borrow checker.
-use std::sync::Arc;
+use std::iter;
-use hir_def::DefWithBodyId;
+use hir_def::{DefWithBodyId, HasModule};
use la_arena::ArenaMap;
use stdx::never;
+use triomphe::Arc;
-use crate::db::HirDatabase;
+use crate::{
+ db::HirDatabase, mir::Operand, utils::ClosureSubst, ClosureId, Interner, Ty, TyExt, TypeFlags,
+};
use super::{
BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, Place, ProjectionElem,
- Rvalue, StatementKind, Terminator,
+ Rvalue, StatementKind, TerminatorKind,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -24,25 +27,166 @@ pub enum MutabilityReason {
}
#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct MovedOutOfRef {
+ pub ty: Ty,
+ pub span: MirSpan,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BorrowckResult {
pub mir_body: Arc<MirBody>,
pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
+ pub moved_out_of_ref: Vec<MovedOutOfRef>,
+}
+
+fn all_mir_bodies(
+ db: &dyn HirDatabase,
+ def: DefWithBodyId,
+) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
+ fn for_closure(
+ db: &dyn HirDatabase,
+ c: ClosureId,
+ ) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
+ match db.mir_body_for_closure(c) {
+ Ok(body) => {
+ let closures = body.closures.clone();
+ Box::new(
+ iter::once(Ok(body))
+ .chain(closures.into_iter().flat_map(|x| for_closure(db, x))),
+ )
+ }
+ Err(e) => Box::new(iter::once(Err(e))),
+ }
+ }
+ match db.mir_body(def) {
+ Ok(body) => {
+ let closures = body.closures.clone();
+ Box::new(
+ iter::once(Ok(body)).chain(closures.into_iter().flat_map(|x| for_closure(db, x))),
+ )
+ }
+ Err(e) => Box::new(iter::once(Err(e))),
+ }
}
pub fn borrowck_query(
db: &dyn HirDatabase,
def: DefWithBodyId,
-) -> Result<Arc<BorrowckResult>, MirLowerError> {
+) -> Result<Arc<[BorrowckResult]>, MirLowerError> {
let _p = profile::span("borrowck_query");
- let body = db.mir_body(def)?;
- let r = BorrowckResult { mutability_of_locals: mutability_of_locals(&body), mir_body: body };
- Ok(Arc::new(r))
+ let r = all_mir_bodies(db, def)
+ .map(|body| {
+ let body = body?;
+ Ok(BorrowckResult {
+ mutability_of_locals: mutability_of_locals(db, &body),
+ moved_out_of_ref: moved_out_of_ref(db, &body),
+ mir_body: body,
+ })
+ })
+ .collect::<Result<Vec<_>, MirLowerError>>()?;
+ Ok(r.into())
}
-fn is_place_direct(lvalue: &Place) -> bool {
- !lvalue.projection.iter().any(|x| *x == ProjectionElem::Deref)
+fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> {
+ let mut result = vec![];
+ let mut for_operand = |op: &Operand, span: MirSpan| match op {
+ Operand::Copy(p) | Operand::Move(p) => {
+ let mut ty: Ty = body.locals[p.local].ty.clone();
+ let mut is_dereference_of_ref = false;
+ for proj in &*p.projection {
+ if *proj == ProjectionElem::Deref && ty.as_reference().is_some() {
+ is_dereference_of_ref = true;
+ }
+ ty = proj.projected_ty(
+ ty,
+ db,
+ |c, subst, f| {
+ let (def, _) = db.lookup_intern_closure(c.into());
+ let infer = db.infer(def);
+ let (captures, _) = infer.closure_info(&c);
+ let parent_subst = ClosureSubst(subst).parent_subst();
+ captures
+ .get(f)
+ .expect("broken closure field")
+ .ty
+ .clone()
+ .substitute(Interner, parent_subst)
+ },
+ body.owner.module(db.upcast()).krate(),
+ );
+ }
+ if is_dereference_of_ref
+ && !ty.clone().is_copy(db, body.owner)
+ && !ty.data(Interner).flags.intersects(TypeFlags::HAS_ERROR)
+ {
+ result.push(MovedOutOfRef { span, ty });
+ }
+ }
+ Operand::Constant(_) | Operand::Static(_) => (),
+ };
+ for (_, block) in body.basic_blocks.iter() {
+ for statement in &block.statements {
+ match &statement.kind {
+ StatementKind::Assign(_, r) => match r {
+ Rvalue::ShallowInitBoxWithAlloc(_) => (),
+ Rvalue::ShallowInitBox(o, _)
+ | Rvalue::UnaryOp(_, o)
+ | Rvalue::Cast(_, o, _)
+ | Rvalue::Repeat(o, _)
+ | Rvalue::Use(o) => for_operand(o, statement.span),
+ Rvalue::CopyForDeref(_)
+ | Rvalue::Discriminant(_)
+ | Rvalue::Len(_)
+ | Rvalue::Ref(_, _) => (),
+ Rvalue::CheckedBinaryOp(_, o1, o2) => {
+ for_operand(o1, statement.span);
+ for_operand(o2, statement.span);
+ }
+ Rvalue::Aggregate(_, ops) => {
+ for op in ops.iter() {
+ for_operand(op, statement.span);
+ }
+ }
+ },
+ StatementKind::Deinit(_)
+ | StatementKind::StorageLive(_)
+ | StatementKind::StorageDead(_)
+ | StatementKind::Nop => (),
+ }
+ }
+ match &block.terminator {
+ Some(terminator) => match &terminator.kind {
+ TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span),
+ TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. }
+ | TerminatorKind::Goto { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable
+ | TerminatorKind::Drop { .. } => (),
+ TerminatorKind::DropAndReplace { value, .. } => {
+ for_operand(value, terminator.span);
+ }
+ TerminatorKind::Call { func, args, .. } => {
+ for_operand(func, terminator.span);
+ args.iter().for_each(|x| for_operand(x, terminator.span));
+ }
+ TerminatorKind::Assert { cond, .. } => {
+ for_operand(cond, terminator.span);
+ }
+ TerminatorKind::Yield { value, .. } => {
+ for_operand(value, terminator.span);
+ }
+ },
+ None => (),
+ }
+ }
+ result
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ProjectionCase {
/// Projection is a local
Direct,
@@ -52,20 +196,39 @@ enum ProjectionCase {
Indirect,
}
-fn place_case(lvalue: &Place) -> ProjectionCase {
+fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase {
let mut is_part_of = false;
- for proj in lvalue.projection.iter().rev() {
+ let mut ty = body.locals[lvalue.local].ty.clone();
+ for proj in lvalue.projection.iter() {
match proj {
- ProjectionElem::Deref => return ProjectionCase::Indirect, // It's indirect
- ProjectionElem::ConstantIndex { .. }
+ ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw
+ ProjectionElem::Deref // It's direct in case of `Box<T>`
+ | ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. }
| ProjectionElem::Field(_)
- | ProjectionElem::TupleField(_)
+ | ProjectionElem::TupleOrClosureField(_)
| ProjectionElem::Index(_) => {
is_part_of = true;
}
ProjectionElem::OpaqueCast(_) => (),
}
+ ty = proj.projected_ty(
+ ty,
+ db,
+ |c, subst, f| {
+ let (def, _) = db.lookup_intern_closure(c.into());
+ let infer = db.infer(def);
+ let (captures, _) = infer.closure_info(&c);
+ let parent_subst = ClosureSubst(subst).parent_subst();
+ captures
+ .get(f)
+ .expect("broken closure field")
+ .ty
+ .clone()
+ .substitute(Interner, parent_subst)
+ },
+ body.owner.module(db.upcast()).krate(),
+ );
}
if is_part_of {
ProjectionCase::DirectPart
@@ -76,11 +239,15 @@ fn place_case(lvalue: &Place) -> ProjectionCase {
/// Returns a map from basic blocks to the set of locals that might be ever initialized before
/// the start of the block. Only `StorageDead` can remove something from this map, and we ignore
-/// `Uninit` and `drop` and similars after initialization.
-fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
+/// `Uninit` and `drop` and similar after initialization.
+fn ever_initialized_map(
+ db: &dyn HirDatabase,
+ body: &MirBody,
+) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
let mut result: ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> =
body.basic_blocks.iter().map(|x| (x.0, ArenaMap::default())).collect();
fn dfs(
+ db: &dyn HirDatabase,
body: &MirBody,
b: BasicBlockId,
l: LocalId,
@@ -104,29 +271,31 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<Local
}
}
let Some(terminator) = &block.terminator else {
- never!("Terminator should be none only in construction");
+ never!("Terminator should be none only in construction.\nThe body:\n{}", body.pretty_print(db));
return;
};
- let targets = match terminator {
- Terminator::Goto { target } => vec![*target],
- Terminator::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
- Terminator::Resume
- | Terminator::Abort
- | Terminator::Return
- | Terminator::Unreachable => vec![],
- Terminator::Call { target, cleanup, destination, .. } => {
+ let targets = match &terminator.kind {
+ TerminatorKind::Goto { target } => vec![*target],
+ TerminatorKind::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
+ TerminatorKind::Resume
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable => vec![],
+ TerminatorKind::Call { target, cleanup, destination, .. } => {
if destination.projection.len() == 0 && destination.local == l {
is_ever_initialized = true;
}
target.into_iter().chain(cleanup.into_iter()).copied().collect()
}
- Terminator::Drop { .. }
- | Terminator::DropAndReplace { .. }
- | Terminator::Assert { .. }
- | Terminator::Yield { .. }
- | Terminator::GeneratorDrop
- | Terminator::FalseEdge { .. }
- | Terminator::FalseUnwind { .. } => {
+ TerminatorKind::Drop { target, unwind, place: _ } => {
+ Some(target).into_iter().chain(unwind.into_iter()).copied().collect()
+ }
+ TerminatorKind::DropAndReplace { .. }
+ | TerminatorKind::Assert { .. }
+ | TerminatorKind::Yield { .. }
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. } => {
never!("We don't emit these MIR terminators yet");
vec![]
}
@@ -134,37 +303,40 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap<BasicBlockId, ArenaMap<Local
for target in targets {
if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
result[target].insert(l, is_ever_initialized);
- dfs(body, target, l, result);
+ dfs(db, body, target, l, result);
}
}
}
for &l in &body.param_locals {
result[body.start_block].insert(l, true);
- dfs(body, body.start_block, l, &mut result);
+ dfs(db, body, body.start_block, l, &mut result);
}
for l in body.locals.iter().map(|x| x.0) {
if !result[body.start_block].contains_idx(l) {
result[body.start_block].insert(l, false);
- dfs(body, body.start_block, l, &mut result);
+ dfs(db, body, body.start_block, l, &mut result);
}
}
result
}
-fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
+fn mutability_of_locals(
+ db: &dyn HirDatabase,
+ body: &MirBody,
+) -> ArenaMap<LocalId, MutabilityReason> {
let mut result: ArenaMap<LocalId, MutabilityReason> =
body.locals.iter().map(|x| (x.0, MutabilityReason::Not)).collect();
let mut push_mut_span = |local, span| match &mut result[local] {
MutabilityReason::Mut { spans } => spans.push(span),
x @ MutabilityReason::Not => *x = MutabilityReason::Mut { spans: vec![span] },
};
- let ever_init_maps = ever_initialized_map(body);
+ let ever_init_maps = ever_initialized_map(db, body);
for (block_id, mut ever_init_map) in ever_init_maps.into_iter() {
let block = &body.basic_blocks[block_id];
for statement in &block.statements {
match &statement.kind {
StatementKind::Assign(place, value) => {
- match place_case(place) {
+ match place_case(db, body, place) {
ProjectionCase::Direct => {
if ever_init_map.get(place.local).copied().unwrap_or_default() {
push_mut_span(place.local, statement.span);
@@ -179,7 +351,7 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
ProjectionCase::Indirect => (),
}
if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value {
- if is_place_direct(p) {
+ if place_case(db, body, p) != ProjectionCase::Indirect {
push_mut_span(p.local, statement.span);
}
}
@@ -194,21 +366,21 @@ fn mutability_of_locals(body: &MirBody) -> ArenaMap<LocalId, MutabilityReason> {
never!("Terminator should be none only in construction");
continue;
};
- match terminator {
- Terminator::Goto { .. }
- | Terminator::Resume
- | Terminator::Abort
- | Terminator::Return
- | Terminator::Unreachable
- | Terminator::FalseEdge { .. }
- | Terminator::FalseUnwind { .. }
- | Terminator::GeneratorDrop
- | Terminator::SwitchInt { .. }
- | Terminator::Drop { .. }
- | Terminator::DropAndReplace { .. }
- | Terminator::Assert { .. }
- | Terminator::Yield { .. } => (),
- Terminator::Call { destination, .. } => {
+ match &terminator.kind {
+ TerminatorKind::Goto { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable
+ | TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. }
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::SwitchInt { .. }
+ | TerminatorKind::Drop { .. }
+ | TerminatorKind::DropAndReplace { .. }
+ | TerminatorKind::Assert { .. }
+ | TerminatorKind::Yield { .. } => (),
+ TerminatorKind::Call { destination, .. } => {
if destination.projection.len() == 0 {
if ever_init_map.get(destination.local).copied().unwrap_or_default() {
push_mut_span(destination.local, MirSpan::Unknown);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index c5d843d9e..9acf9d39e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -1,41 +1,134 @@
//! This module provides a MIR interpreter, which is used in const eval.
-use std::{borrow::Cow, collections::HashMap, iter};
+use std::{borrow::Cow, collections::HashMap, fmt::Write, iter, ops::Range};
-use base_db::CrateId;
-use chalk_ir::{
- fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
- DebruijnIndex, TyKind,
-};
+use base_db::{CrateId, FileId};
+use chalk_ir::Mutability;
+use either::Either;
use hir_def::{
builtin_type::BuiltinType,
+ data::adt::{StructFlags, VariantData},
lang_item::{lang_attr, LangItem},
- layout::{Layout, LayoutError, RustcEnumVariantIdx, TagEncoding, Variants},
- AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, Lookup, VariantId,
+ layout::{TagEncoding, Variants},
+ AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId,
+ VariantId,
};
+use hir_expand::InFile;
use intern::Interned;
use la_arena::ArenaMap;
+use rustc_hash::{FxHashMap, FxHashSet};
+use stdx::never;
+use syntax::{SyntaxNodePtr, TextRange};
+use triomphe::Arc;
use crate::{
- consteval::{intern_const_scalar, ConstEvalError},
+ consteval::{intern_const_scalar, try_const_usize, ConstEvalError},
db::HirDatabase,
- from_placeholder_idx,
- infer::{normalize, PointerCast},
- layout::layout_of_ty,
+ display::{ClosureStyle, HirDisplay},
+ infer::PointerCast,
+ layout::{Layout, LayoutError, RustcEnumVariantIdx},
mapping::from_chalk,
- method_resolution::lookup_impl_method,
- CallableDefId, Const, ConstScalar, Interner, MemoryMap, Substitution, Ty, TyBuilder, TyExt,
+ method_resolution::{is_dyn_method, lookup_impl_method},
+ name, static_lifetime,
+ traits::FnTrait,
+ utils::{detect_variant_from_bytes, ClosureSubst},
+ CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
+ Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
};
use super::{
- const_as_usize, return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError,
- Operand, Place, ProjectionElem, Rvalue, StatementKind, Terminator, UnOp,
+ return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, Operand,
+ Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp,
};
+mod shim;
+#[cfg(test)]
+mod tests;
+
+macro_rules! from_bytes {
+ ($ty:tt, $value:expr) => {
+ ($ty::from_le_bytes(match ($value).try_into() {
+ Ok(x) => x,
+ Err(_) => return Err(MirEvalError::TypeError(stringify!(mismatched size in constructing $ty))),
+ }))
+ };
+}
+
+macro_rules! not_supported {
+ ($x: expr) => {
+ return Err(MirEvalError::NotSupported(format!($x)))
+ };
+}
+
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
+pub struct VTableMap {
+ ty_to_id: FxHashMap<Ty, usize>,
+ id_to_ty: Vec<Ty>,
+}
+
+impl VTableMap {
+ fn id(&mut self, ty: Ty) -> usize {
+ if let Some(x) = self.ty_to_id.get(&ty) {
+ return *x;
+ }
+ let id = self.id_to_ty.len();
+ self.id_to_ty.push(ty.clone());
+ self.ty_to_id.insert(ty, id);
+ id
+ }
+
+ pub(crate) fn ty(&self, id: usize) -> Result<&Ty> {
+ self.id_to_ty.get(id).ok_or(MirEvalError::InvalidVTableId(id))
+ }
+
+ fn ty_of_bytes(&self, bytes: &[u8]) -> Result<&Ty> {
+ let id = from_bytes!(usize, bytes);
+ self.ty(id)
+ }
+}
+
+#[derive(Debug, Default, Clone, PartialEq, Eq)]
+struct TlsData {
+ keys: Vec<u128>,
+}
+
+impl TlsData {
+ fn create_key(&mut self) -> usize {
+ self.keys.push(0);
+ self.keys.len() - 1
+ }
+
+ fn get_key(&mut self, key: usize) -> Result<u128> {
+ let r = self.keys.get(key).ok_or_else(|| {
+ MirEvalError::UndefinedBehavior(format!("Getting invalid tls key {key}"))
+ })?;
+ Ok(*r)
+ }
+
+ fn set_key(&mut self, key: usize, value: u128) -> Result<()> {
+ let r = self.keys.get_mut(key).ok_or_else(|| {
+ MirEvalError::UndefinedBehavior(format!("Setting invalid tls key {key}"))
+ })?;
+ *r = value;
+ Ok(())
+ }
+}
+
pub struct Evaluator<'a> {
db: &'a dyn HirDatabase,
+ trait_env: Arc<TraitEnvironment>,
stack: Vec<u8>,
heap: Vec<u8>,
+ /// Stores the global location of the statics. We const evaluate every static first time we need it
+ /// and see it's missing, then we add it to this to reuse.
+ static_locations: FxHashMap<StaticId, Address>,
+ /// We don't really have function pointers, i.e. pointers to some assembly instructions that we can run. Instead, we
+ /// store the type as an interned id in place of function and vtable pointers, and we recover back the type at the
+ /// time of use.
+ vtable_map: VTableMap,
+ thread_local_storage: TlsData,
+ stdout: Vec<u8>,
+ stderr: Vec<u8>,
crate_id: CrateId,
// FIXME: This is a workaround, see the comment on `interpret_mir`
assert_placeholder_ty_is_unused: bool,
@@ -45,19 +138,27 @@ pub struct Evaluator<'a> {
stack_depth_limit: usize,
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Address {
Stack(usize),
Heap(usize),
+ Invalid(usize),
}
use Address::*;
+#[derive(Debug, Clone, Copy)]
struct Interval {
addr: Address,
size: usize,
}
+#[derive(Debug, Clone)]
+struct IntervalAndTy {
+ interval: Interval,
+ ty: Ty,
+}
+
impl Interval {
fn new(addr: Address, size: usize) -> Self {
Self { addr, size }
@@ -66,12 +167,49 @@ impl Interval {
fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
memory.read_memory(self.addr, self.size)
}
+
+ fn write_from_bytes(&self, memory: &mut Evaluator<'_>, bytes: &[u8]) -> Result<()> {
+ memory.write_memory(self.addr, bytes)
+ }
+
+ fn write_from_interval(&self, memory: &mut Evaluator<'_>, interval: Interval) -> Result<()> {
+ // FIXME: this could be more efficient
+ let bytes = &interval.get(memory)?.to_vec();
+ memory.write_memory(self.addr, bytes)
+ }
+
+ fn slice(self, range: Range<usize>) -> Interval {
+ Interval { addr: self.addr.offset(range.start), size: range.len() }
+ }
+}
+
+impl IntervalAndTy {
+ fn get<'a>(&self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
+ memory.read_memory(self.interval.addr, self.interval.size)
+ }
+
+ fn new(
+ addr: Address,
+ ty: Ty,
+ evaluator: &Evaluator<'_>,
+ locals: &Locals<'_>,
+ ) -> Result<IntervalAndTy> {
+ let size = evaluator.size_of_sized(&ty, locals, "type of interval")?;
+ Ok(IntervalAndTy { interval: Interval { addr, size }, ty })
+ }
}
enum IntervalOrOwned {
Owned(Vec<u8>),
Borrowed(Interval),
}
+
+impl From<Interval> for IntervalOrOwned {
+ fn from(it: Interval) -> IntervalOrOwned {
+ IntervalOrOwned::Borrowed(it)
+ }
+}
+
impl IntervalOrOwned {
pub(crate) fn to_vec(self, memory: &Evaluator<'_>) -> Result<Vec<u8>> {
Ok(match self {
@@ -79,15 +217,13 @@ impl IntervalOrOwned {
IntervalOrOwned::Borrowed(b) => b.get(memory)?.to_vec(),
})
}
-}
-macro_rules! from_bytes {
- ($ty:tt, $value:expr) => {
- ($ty::from_le_bytes(match ($value).try_into() {
- Ok(x) => x,
- Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
- }))
- };
+ fn get<'a>(&'a self, memory: &'a Evaluator<'a>) -> Result<&'a [u8]> {
+ Ok(match self {
+ IntervalOrOwned::Owned(o) => o,
+ IntervalOrOwned::Borrowed(b) => b.get(memory)?,
+ })
+ }
}
impl Address {
@@ -97,9 +233,11 @@ impl Address {
fn from_usize(x: usize) -> Self {
if x > usize::MAX / 2 {
- Stack(usize::MAX - x)
+ Stack(x - usize::MAX / 2)
+ } else if x > usize::MAX / 4 {
+ Heap(x - usize::MAX / 4)
} else {
- Heap(x)
+ Invalid(x)
}
}
@@ -109,8 +247,9 @@ impl Address {
fn to_usize(&self) -> usize {
let as_num = match self {
- Stack(x) => usize::MAX - *x,
- Heap(x) => *x,
+ Stack(x) => *x + usize::MAX / 2,
+ Heap(x) => *x + usize::MAX / 4,
+ Invalid(x) => *x,
};
as_num
}
@@ -119,6 +258,7 @@ impl Address {
match self {
Stack(x) => Stack(f(*x)),
Heap(x) => Heap(f(*x)),
+ Invalid(x) => Invalid(f(*x)),
}
}
@@ -129,28 +269,123 @@ impl Address {
#[derive(Clone, PartialEq, Eq)]
pub enum MirEvalError {
- ConstEvalError(Box<ConstEvalError>),
+ ConstEvalError(String, Box<ConstEvalError>),
LayoutError(LayoutError, Ty),
/// Means that code had type errors (or mismatched args) and we shouldn't generate mir in first place.
TypeError(&'static str),
/// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected
/// then use this type of error.
- UndefinedBehavior(&'static str),
- Panic,
+ UndefinedBehavior(String),
+ Panic(String),
MirLowerError(FunctionId, MirLowerError),
+ MirLowerErrorForClosure(ClosureId, MirLowerError),
TypeIsUnsized(Ty, &'static str),
NotSupported(String),
InvalidConst(Const),
- InFunction(FunctionId, Box<MirEvalError>),
+ InFunction(Either<FunctionId, ClosureId>, Box<MirEvalError>, MirSpan, DefWithBodyId),
ExecutionLimitExceeded,
StackOverflow,
TargetDataLayoutNotAvailable,
+ InvalidVTableId(usize),
+ CoerceUnsizedError(Ty),
+ LangItemNotFound(LangItem),
+}
+
+impl MirEvalError {
+ pub fn pretty_print(
+ &self,
+ f: &mut String,
+ db: &dyn HirDatabase,
+ span_formatter: impl Fn(FileId, TextRange) -> String,
+ ) -> std::result::Result<(), std::fmt::Error> {
+ writeln!(f, "Mir eval error:")?;
+ let mut err = self;
+ while let MirEvalError::InFunction(func, e, span, def) = err {
+ err = e;
+ match func {
+ Either::Left(func) => {
+ let function_name = db.function_data(*func);
+ writeln!(
+ f,
+ "In function {} ({:?})",
+ function_name.name.display(db.upcast()),
+ func
+ )?;
+ }
+ Either::Right(clos) => {
+ writeln!(f, "In {:?}", clos)?;
+ }
+ }
+ let source_map = db.body_with_source_map(*def).1;
+ let span: InFile<SyntaxNodePtr> = match span {
+ MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
+ Ok(s) => s.map(|x| x.into()),
+ Err(_) => continue,
+ },
+ MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
+ Ok(s) => s.map(|x| match x {
+ Either::Left(e) => e.into(),
+ Either::Right(e) => e.into(),
+ }),
+ Err(_) => continue,
+ },
+ MirSpan::Unknown => continue,
+ };
+ let file_id = span.file_id.original_file(db.upcast());
+ let text_range = span.value.text_range();
+ writeln!(f, "{}", span_formatter(file_id, text_range))?;
+ }
+ match err {
+ MirEvalError::InFunction(..) => unreachable!(),
+ MirEvalError::LayoutError(err, ty) => {
+ write!(
+ f,
+ "Layout for type `{}` is not available due {err:?}",
+ ty.display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string()
+ )?;
+ }
+ MirEvalError::MirLowerError(func, err) => {
+ let function_name = db.function_data(*func);
+ writeln!(
+ f,
+ "MIR lowering for function `{}` ({:?}) failed due:",
+ function_name.name.display(db.upcast()),
+ func
+ )?;
+ err.pretty_print(f, db, span_formatter)?;
+ }
+ MirEvalError::ConstEvalError(name, err) => {
+ MirLowerError::ConstEvalError(name.clone(), err.clone()).pretty_print(
+ f,
+ db,
+ span_formatter,
+ )?;
+ }
+ MirEvalError::TypeError(_)
+ | MirEvalError::UndefinedBehavior(_)
+ | MirEvalError::Panic(_)
+ | MirEvalError::MirLowerErrorForClosure(_, _)
+ | MirEvalError::TypeIsUnsized(_, _)
+ | MirEvalError::NotSupported(_)
+ | MirEvalError::InvalidConst(_)
+ | MirEvalError::ExecutionLimitExceeded
+ | MirEvalError::StackOverflow
+ | MirEvalError::TargetDataLayoutNotAvailable
+ | MirEvalError::CoerceUnsizedError(_)
+ | MirEvalError::LangItemNotFound(_)
+ | MirEvalError::InvalidVTableId(_) => writeln!(f, "{:?}", err)?,
+ }
+ Ok(())
+ }
}
impl std::fmt::Debug for MirEvalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- Self::ConstEvalError(arg0) => f.debug_tuple("ConstEvalError").field(arg0).finish(),
+ Self::ConstEvalError(arg0, arg1) => {
+ f.debug_tuple("ConstEvalError").field(arg0).field(arg1).finish()
+ }
+ Self::LangItemNotFound(arg0) => f.debug_tuple("LangItemNotFound").field(arg0).finish(),
Self::LayoutError(arg0, arg1) => {
f.debug_tuple("LayoutError").field(arg0).field(arg1).finish()
}
@@ -158,7 +393,7 @@ impl std::fmt::Debug for MirEvalError {
Self::UndefinedBehavior(arg0) => {
f.debug_tuple("UndefinedBehavior").field(arg0).finish()
}
- Self::Panic => write!(f, "Panic"),
+ Self::Panic(msg) => write!(f, "Panic with message:\n{msg:?}"),
Self::TargetDataLayoutNotAvailable => write!(f, "TargetDataLayoutNotAvailable"),
Self::TypeIsUnsized(ty, it) => write!(f, "{ty:?} is unsized. {it} should be sized."),
Self::ExecutionLimitExceeded => write!(f, "execution limit exceeded"),
@@ -166,17 +401,24 @@ impl std::fmt::Debug for MirEvalError {
Self::MirLowerError(arg0, arg1) => {
f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
}
+ Self::MirLowerErrorForClosure(arg0, arg1) => {
+ f.debug_tuple("MirLowerError").field(arg0).field(arg1).finish()
+ }
+ Self::CoerceUnsizedError(arg0) => {
+ f.debug_tuple("CoerceUnsizedError").field(arg0).finish()
+ }
+ Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
Self::InvalidConst(arg0) => {
let data = &arg0.data(Interner);
f.debug_struct("InvalidConst").field("ty", &data.ty).field("value", &arg0).finish()
}
- Self::InFunction(func, e) => {
+ Self::InFunction(func, e, span, _) => {
let mut e = &**e;
- let mut stack = vec![*func];
- while let Self::InFunction(f, next_e) = e {
+ let mut stack = vec![(*func, *span)];
+ while let Self::InFunction(f, next_e, span, _) = e {
e = &next_e;
- stack.push(*f);
+ stack.push((*f, *span));
}
f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish()
}
@@ -184,26 +426,33 @@ impl std::fmt::Debug for MirEvalError {
}
}
-macro_rules! not_supported {
- ($x: expr) => {
- return Err(MirEvalError::NotSupported(format!($x)))
- };
+type Result<T> = std::result::Result<T, MirEvalError>;
+
+#[derive(Debug, Default)]
+struct DropFlags {
+ need_drop: FxHashSet<Place>,
}
-impl From<ConstEvalError> for MirEvalError {
- fn from(value: ConstEvalError) -> Self {
- match value {
- _ => MirEvalError::ConstEvalError(Box::new(value)),
+impl DropFlags {
+ fn add_place(&mut self, p: Place) {
+ if p.iterate_over_parents().any(|x| self.need_drop.contains(&x)) {
+ return;
}
+ self.need_drop.retain(|x| !p.is_parent(x));
+ self.need_drop.insert(p);
}
-}
-type Result<T> = std::result::Result<T, MirEvalError>;
+ fn remove_place(&mut self, p: &Place) -> bool {
+ // FIXME: replace parents with parts
+ self.need_drop.remove(p)
+ }
+}
+#[derive(Debug)]
struct Locals<'a> {
- ptr: &'a ArenaMap<LocalId, Address>,
+ ptr: &'a ArenaMap<LocalId, Interval>,
body: &'a MirBody,
- subst: &'a Substitution,
+ drop_flags: DropFlags,
}
pub fn interpret_mir(
@@ -215,38 +464,65 @@ pub fn interpret_mir(
// a zero size, hoping that they are all outside of our current body. Even without a fix for #7434, we can
// (and probably should) do better here, for example by excluding bindings outside of the target expression.
assert_placeholder_ty_is_unused: bool,
-) -> Result<Const> {
+) -> (Result<Const>, String, String) {
let ty = body.locals[return_slot()].ty.clone();
- let mut evaluator =
- Evaluator::new(db, body.owner.module(db.upcast()).krate(), assert_placeholder_ty_is_unused);
- let bytes = evaluator.interpret_mir_with_no_arg(&body)?;
- let memory_map = evaluator.create_memory_map(
- &bytes,
- &ty,
- &Locals { ptr: &ArenaMap::new(), body: &body, subst: &Substitution::empty(Interner) },
- )?;
- return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
+ let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused);
+ let x: Result<Const> = (|| {
+ let bytes = evaluator.interpret_mir(&body, None.into_iter())?;
+ let mut memory_map = evaluator.create_memory_map(
+ &bytes,
+ &ty,
+ &Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() },
+ )?;
+ memory_map.vtable = evaluator.vtable_map.clone();
+ return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
+ })();
+ (
+ x,
+ String::from_utf8_lossy(&evaluator.stdout).into_owned(),
+ String::from_utf8_lossy(&evaluator.stderr).into_owned(),
+ )
}
impl Evaluator<'_> {
pub fn new<'a>(
db: &'a dyn HirDatabase,
- crate_id: CrateId,
+ body: &MirBody,
assert_placeholder_ty_is_unused: bool,
) -> Evaluator<'a> {
+ let crate_id = body.owner.module(db.upcast()).krate();
+ let trait_env = db.trait_environment_for_body(body.owner);
Evaluator {
stack: vec![0],
heap: vec![0],
+ vtable_map: VTableMap::default(),
+ thread_local_storage: TlsData::default(),
+ static_locations: HashMap::default(),
db,
+ trait_env,
crate_id,
+ stdout: vec![],
+ stderr: vec![],
assert_placeholder_ty_is_unused,
stack_depth_limit: 100,
- execution_limit: 100_000,
+ execution_limit: 1000_000,
}
}
fn place_addr(&self, p: &Place, locals: &Locals<'_>) -> Result<Address> {
- Ok(self.place_addr_and_ty(p, locals)?.0)
+ Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0)
+ }
+
+ fn place_interval(&self, p: &Place, locals: &Locals<'_>) -> Result<Interval> {
+ let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?;
+ Ok(Interval {
+ addr: place_addr_and_ty.0,
+ size: self.size_of_sized(
+ &place_addr_and_ty.1,
+ locals,
+ "Type of place that we need its interval",
+ )?,
+ })
}
fn ptr_size(&self) -> usize {
@@ -256,125 +532,170 @@ impl Evaluator<'_> {
}
}
- fn place_addr_and_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<(Address, Ty)> {
- let mut addr = locals.ptr[p.local];
- let mut ty: Ty =
- self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?;
- for proj in &p.projection {
+ fn place_addr_and_ty_and_metadata<'a>(
+ &'a self,
+ p: &Place,
+ locals: &'a Locals<'a>,
+ ) -> Result<(Address, Ty, Option<IntervalOrOwned>)> {
+ let mut addr = locals.ptr[p.local].addr;
+ let mut ty: Ty = locals.body.locals[p.local].ty.clone();
+ let mut metadata: Option<IntervalOrOwned> = None; // locals are always sized
+ for proj in &*p.projection {
+ let prev_ty = ty.clone();
+ ty = proj.projected_ty(
+ ty,
+ self.db,
+ |c, subst, f| {
+ let (def, _) = self.db.lookup_intern_closure(c.into());
+ let infer = self.db.infer(def);
+ let (captures, _) = infer.closure_info(&c);
+ let parent_subst = ClosureSubst(subst).parent_subst();
+ captures
+ .get(f)
+ .expect("broken closure field")
+ .ty
+ .clone()
+ .substitute(Interner, parent_subst)
+ },
+ self.crate_id,
+ );
match proj {
ProjectionElem::Deref => {
- ty = match &ty.data(Interner).kind {
- TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
- _ => {
- return Err(MirEvalError::TypeError(
- "Overloaded deref in MIR is disallowed",
- ))
- }
+ metadata = if self.size_align_of(&ty, locals)?.is_none() {
+ Some(
+ Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() }
+ .into(),
+ )
+ } else {
+ None
};
let x = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?);
addr = Address::from_usize(x);
}
ProjectionElem::Index(op) => {
- let offset =
- from_bytes!(usize, self.read_memory(locals.ptr[*op], self.ptr_size())?);
- match &ty.data(Interner).kind {
- TyKind::Ref(_, _, inner) => match &inner.data(Interner).kind {
- TyKind::Slice(inner) => {
- ty = inner.clone();
- let ty_size = self.size_of_sized(
- &ty,
- locals,
- "slice inner type should be sized",
- )?;
- let value = self.read_memory(addr, self.ptr_size() * 2)?;
- addr = Address::from_bytes(&value[0..8])?.offset(ty_size * offset);
- }
- x => not_supported!("MIR index for ref type {x:?}"),
- },
- TyKind::Array(inner, _) | TyKind::Slice(inner) => {
- ty = inner.clone();
- let ty_size = self.size_of_sized(
- &ty,
- locals,
- "array inner type should be sized",
- )?;
- addr = addr.offset(ty_size * offset);
- }
- x => not_supported!("MIR index for type {x:?}"),
- }
+ let offset = from_bytes!(
+ usize,
+ self.read_memory(locals.ptr[*op].addr, self.ptr_size())?
+ );
+ metadata = None; // Result of index is always sized
+ let ty_size =
+ self.size_of_sized(&ty, locals, "array inner type should be sized")?;
+ addr = addr.offset(ty_size * offset);
}
- &ProjectionElem::TupleField(f) => match &ty.data(Interner).kind {
- TyKind::Tuple(_, subst) => {
- let layout = self.layout(&ty)?;
- ty = subst
- .as_slice(Interner)
- .get(f)
- .ok_or(MirEvalError::TypeError("not enough tuple fields"))?
- .assert_ty_ref(Interner)
- .clone();
- let offset = layout.fields.offset(f).bytes_usize();
- addr = addr.offset(offset);
- }
- _ => return Err(MirEvalError::TypeError("Only tuple has tuple fields")),
- },
- ProjectionElem::Field(f) => match &ty.data(Interner).kind {
- TyKind::Adt(adt, subst) => {
- let layout = self.layout_adt(adt.0, subst.clone())?;
- let variant_layout = match &layout.variants {
- Variants::Single { .. } => &layout,
- Variants::Multiple { variants, .. } => {
- &variants[match f.parent {
- hir_def::VariantId::EnumVariantId(x) => {
- RustcEnumVariantIdx(x.local_id)
- }
- _ => {
- return Err(MirEvalError::TypeError(
- "Multivariant layout only happens for enums",
- ))
- }
- }]
- }
+ &ProjectionElem::ConstantIndex { from_end, offset } => {
+ let offset = if from_end {
+ let len = match prev_ty.kind(Interner) {
+ TyKind::Array(_, c) => match try_const_usize(self.db, c) {
+ Some(x) => x as u64,
+ None => {
+ not_supported!("indexing array with unknown const from end")
+ }
+ },
+ TyKind::Slice(_) => match metadata {
+ Some(x) => from_bytes!(u64, x.get(self)?),
+ None => not_supported!("slice place without metadata"),
+ },
+ _ => not_supported!("bad type for const index"),
};
- ty = self.db.field_types(f.parent)[f.local_id]
- .clone()
- .substitute(Interner, subst);
- let offset = variant_layout
- .fields
- .offset(u32::from(f.local_id.into_raw()) as usize)
- .bytes_usize();
- addr = addr.offset(offset);
- }
- _ => return Err(MirEvalError::TypeError("Only adt has fields")),
- },
- ProjectionElem::ConstantIndex { .. } => {
- not_supported!("constant index")
+ (len - offset - 1) as usize
+ } else {
+ offset as usize
+ };
+ metadata = None; // Result of index is always sized
+ let ty_size =
+ self.size_of_sized(&ty, locals, "array inner type should be sized")?;
+ addr = addr.offset(ty_size * offset);
+ }
+ &ProjectionElem::Subslice { from, to } => {
+ let inner_ty = match &ty.data(Interner).kind {
+ TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(),
+ _ => TyKind::Error.intern(Interner),
+ };
+ metadata = match metadata {
+ Some(x) => {
+ let prev_len = from_bytes!(u64, x.get(self)?);
+ Some(IntervalOrOwned::Owned(
+ (prev_len - from - to).to_le_bytes().to_vec(),
+ ))
+ }
+ None => None,
+ };
+ let ty_size =
+ self.size_of_sized(&inner_ty, locals, "array inner type should be sized")?;
+ addr = addr.offset(ty_size * (from as usize));
+ }
+ &ProjectionElem::TupleOrClosureField(f) => {
+ let layout = self.layout(&prev_ty)?;
+ let offset = layout.fields.offset(f).bytes_usize();
+ addr = addr.offset(offset);
+ metadata = None; // tuple field is always sized
+ }
+ ProjectionElem::Field(f) => {
+ let layout = self.layout(&prev_ty)?;
+ let variant_layout = match &layout.variants {
+ Variants::Single { .. } => &layout,
+ Variants::Multiple { variants, .. } => {
+ &variants[match f.parent {
+ hir_def::VariantId::EnumVariantId(x) => {
+ RustcEnumVariantIdx(x.local_id)
+ }
+ _ => {
+ return Err(MirEvalError::TypeError(
+ "Multivariant layout only happens for enums",
+ ))
+ }
+ }]
+ }
+ };
+ let offset = variant_layout
+ .fields
+ .offset(u32::from(f.local_id.into_raw()) as usize)
+ .bytes_usize();
+ addr = addr.offset(offset);
+ // FIXME: support structs with unsized fields
+ metadata = None;
}
- ProjectionElem::Subslice { .. } => not_supported!("subslice"),
ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"),
}
}
- Ok((addr, ty))
+ Ok((addr, ty, metadata))
}
- fn layout(&self, ty: &Ty) -> Result<Layout> {
- layout_of_ty(self.db, ty, self.crate_id)
+ fn layout(&self, ty: &Ty) -> Result<Arc<Layout>> {
+ self.db
+ .layout_of_ty(ty.clone(), self.crate_id)
.map_err(|e| MirEvalError::LayoutError(e, ty.clone()))
}
- fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<Layout> {
- self.db.layout_of_adt(adt, subst.clone()).map_err(|e| {
+ fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<Arc<Layout>> {
+ self.db.layout_of_adt(adt, subst.clone(), self.crate_id).map_err(|e| {
MirEvalError::LayoutError(e, TyKind::Adt(chalk_ir::AdtId(adt), subst).intern(Interner))
})
}
fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<Ty> {
- Ok(self.place_addr_and_ty(p, locals)?.1)
+ Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1)
}
- fn operand_ty<'a>(&'a self, o: &'a Operand, locals: &'a Locals<'a>) -> Result<Ty> {
+ fn operand_ty(&self, o: &Operand, locals: &Locals<'_>) -> Result<Ty> {
Ok(match o {
Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?,
Operand::Constant(c) => c.data(Interner).ty.clone(),
+ &Operand::Static(s) => {
+ let ty = self.db.infer(s.into())[self.db.body(s.into()).body_expr].clone();
+ TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner)
+ }
+ })
+ }
+
+ fn operand_ty_and_eval(
+ &mut self,
+ o: &Operand,
+ locals: &mut Locals<'_>,
+ ) -> Result<IntervalAndTy> {
+ Ok(IntervalAndTy {
+ interval: self.eval_operand(o, locals)?,
+ ty: self.operand_ty(o, locals)?,
})
}
@@ -382,7 +703,6 @@ impl Evaluator<'_> {
&mut self,
body: &MirBody,
args: impl Iterator<Item = Vec<u8>>,
- subst: Substitution,
) -> Result<Vec<u8>> {
if let Some(x) = self.stack_depth_limit.checked_sub(1) {
self.stack_depth_limit = x;
@@ -390,7 +710,8 @@ impl Evaluator<'_> {
return Err(MirEvalError::StackOverflow);
}
let mut current_block_idx = body.start_block;
- let mut locals = Locals { ptr: &ArenaMap::new(), body: &body, subst: &subst };
+ let mut locals =
+ Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() };
let (locals_ptr, stack_size) = {
let mut stack_ptr = self.stack.len();
let addr = body
@@ -401,7 +722,7 @@ impl Evaluator<'_> {
self.size_of_sized(&x.ty, &locals, "no unsized local in extending stack")?;
let my_ptr = stack_ptr;
stack_ptr += size;
- Ok((id, Stack(my_ptr)))
+ Ok((id, Interval { addr: Stack(my_ptr), size }))
})
.collect::<Result<ArenaMap<LocalId, _>>>()?;
let stack_size = stack_ptr - self.stack.len();
@@ -409,9 +730,10 @@ impl Evaluator<'_> {
};
locals.ptr = &locals_ptr;
self.stack.extend(iter::repeat(0).take(stack_size));
- let mut remain_args = body.arg_count;
- for ((_, addr), value) in locals_ptr.iter().skip(1).zip(args) {
- self.write_memory(*addr, &value)?;
+ let mut remain_args = body.param_locals.len();
+ for ((l, interval), value) in locals_ptr.iter().skip(1).zip(args) {
+ locals.drop_flags.add_place(l.into());
+ interval.write_from_bytes(self, &value)?;
if remain_args == 0 {
return Err(MirEvalError::TypeError("more arguments provided"));
}
@@ -431,8 +753,9 @@ impl Evaluator<'_> {
match &statement.kind {
StatementKind::Assign(l, r) => {
let addr = self.place_addr(l, &locals)?;
- let result = self.eval_rvalue(r, &locals)?.to_vec(&self)?;
+ let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?;
self.write_memory(addr, &result)?;
+ locals.drop_flags.add_place(l.clone());
}
StatementKind::Deinit(_) => not_supported!("de-init statement"),
StatementKind::StorageLive(_)
@@ -443,11 +766,11 @@ impl Evaluator<'_> {
let Some(terminator) = current_block.terminator.as_ref() else {
not_supported!("block without terminator");
};
- match terminator {
- Terminator::Goto { target } => {
+ match &terminator.kind {
+ TerminatorKind::Goto { target } => {
current_block_idx = *target;
}
- Terminator::Call {
+ TerminatorKind::Call {
func,
args,
destination,
@@ -455,155 +778,84 @@ impl Evaluator<'_> {
cleanup: _,
from_hir_call: _,
} => {
+ let destination_interval = self.place_interval(destination, &locals)?;
let fn_ty = self.operand_ty(func, &locals)?;
+ let args = args
+ .iter()
+ .map(|x| self.operand_ty_and_eval(x, &mut locals))
+ .collect::<Result<Vec<_>>>()?;
match &fn_ty.data(Interner).kind {
+ TyKind::Function(_) => {
+ let bytes = self.eval_operand(func, &mut locals)?;
+ self.exec_fn_pointer(
+ bytes,
+ destination_interval,
+ &args,
+ &locals,
+ terminator.span,
+ )?;
+ }
TyKind::FnDef(def, generic_args) => {
- let def: CallableDefId = from_chalk(self.db, *def);
- let generic_args = self.subst_filler(generic_args, &locals);
- match def {
- CallableDefId::FunctionId(def) => {
- let arg_bytes = args
- .iter()
- .map(|x| {
- Ok(self
- .eval_operand(x, &locals)?
- .get(&self)?
- .to_owned())
- })
- .collect::<Result<Vec<_>>>()?
- .into_iter();
- let function_data = self.db.function_data(def);
- let is_intrinsic = match &function_data.abi {
- Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
- None => match def.lookup(self.db.upcast()).container {
- hir_def::ItemContainerId::ExternBlockId(block) => {
- let id = block.lookup(self.db.upcast()).id;
- id.item_tree(self.db.upcast())[id.value]
- .abi
- .as_deref()
- == Some("rust-intrinsic")
- }
- _ => false,
- },
- };
- let result = if is_intrinsic {
- self.exec_intrinsic(
- function_data
- .name
- .as_text()
- .unwrap_or_default()
- .as_str(),
- arg_bytes,
- generic_args,
- &locals,
- )?
- } else if let Some(x) = self.detect_lang_function(def) {
- self.exec_lang_item(x, arg_bytes)?
- } else {
- let trait_env = {
- let Some(d) = body.owner.as_generic_def_id() else {
- not_supported!("trait resolving in non generic def id");
- };
- self.db.trait_environment(d)
- };
- let (imp, generic_args) = lookup_impl_method(
- self.db,
- trait_env,
- def,
- generic_args.clone(),
- );
- let generic_args =
- self.subst_filler(&generic_args, &locals);
- let def = imp.into();
- let mir_body = self
- .db
- .mir_body(def)
- .map_err(|e| MirEvalError::MirLowerError(imp, e))?;
- self.interpret_mir(&mir_body, arg_bytes, generic_args)
- .map_err(|e| {
- MirEvalError::InFunction(imp, Box::new(e))
- })?
- };
- let dest_addr = self.place_addr(destination, &locals)?;
- self.write_memory(dest_addr, &result)?;
- }
- CallableDefId::StructId(id) => {
- let (size, variant_layout, tag) = self.layout_of_variant(
- id.into(),
- generic_args.clone(),
- &locals,
- )?;
- let result = self.make_by_layout(
- size,
- &variant_layout,
- tag,
- args,
- &locals,
- )?;
- let dest_addr = self.place_addr(destination, &locals)?;
- self.write_memory(dest_addr, &result)?;
- }
- CallableDefId::EnumVariantId(id) => {
- let (size, variant_layout, tag) = self.layout_of_variant(
- id.into(),
- generic_args.clone(),
- &locals,
- )?;
- let result = self.make_by_layout(
- size,
- &variant_layout,
- tag,
- args,
- &locals,
- )?;
- let dest_addr = self.place_addr(destination, &locals)?;
- self.write_memory(dest_addr, &result)?;
- }
- }
- current_block_idx =
- target.expect("broken mir, function without target");
+ self.exec_fn_def(
+ *def,
+ generic_args,
+ destination_interval,
+ &args,
+ &locals,
+ terminator.span,
+ )?;
}
- _ => not_supported!("unknown function type"),
+ x => not_supported!("unknown function type {x:?}"),
}
+ locals.drop_flags.add_place(destination.clone());
+ current_block_idx = target.expect("broken mir, function without target");
}
- Terminator::SwitchInt { discr, targets } => {
+ TerminatorKind::SwitchInt { discr, targets } => {
let val = u128::from_le_bytes(pad16(
- self.eval_operand(discr, &locals)?.get(&self)?,
+ self.eval_operand(discr, &mut locals)?.get(&self)?,
false,
));
current_block_idx = targets.target_for_value(val);
}
- Terminator::Return => {
- let ty = body.locals[return_slot()].ty.clone();
+ TerminatorKind::Return => {
self.stack_depth_limit += 1;
- return Ok(self
- .read_memory(
- locals.ptr[return_slot()],
- self.size_of_sized(&ty, &locals, "return type")?,
- )?
- .to_owned());
+ return Ok(locals.ptr[return_slot()].get(self)?.to_vec());
+ }
+ TerminatorKind::Unreachable => {
+ return Err(MirEvalError::UndefinedBehavior("unreachable executed".to_owned()));
}
- Terminator::Unreachable => {
- return Err(MirEvalError::UndefinedBehavior("unreachable executed"))
+ TerminatorKind::Drop { place, target, unwind: _ } => {
+ self.drop_place(place, &mut locals, terminator.span)?;
+ current_block_idx = *target;
}
_ => not_supported!("unknown terminator"),
}
}
}
- fn eval_rvalue<'a>(
- &'a mut self,
- r: &'a Rvalue,
- locals: &'a Locals<'a>,
- ) -> Result<IntervalOrOwned> {
+ fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'_>) -> Result<IntervalOrOwned> {
use IntervalOrOwned::*;
Ok(match r {
Rvalue::Use(x) => Borrowed(self.eval_operand(x, locals)?),
Rvalue::Ref(_, p) => {
- let addr = self.place_addr(p, locals)?;
- Owned(addr.to_bytes())
+ let (addr, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?;
+ let mut r = addr.to_bytes();
+ if let Some(metadata) = metadata {
+ r.extend(metadata.get(self)?);
+ }
+ Owned(r)
+ }
+ Rvalue::Len(p) => {
+ let (_, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?;
+ match metadata {
+ Some(m) => m,
+ None => {
+ return Err(MirEvalError::TypeError(
+ "type without metadata is used for Rvalue::Len",
+ ));
+ }
+ }
}
- Rvalue::Len(_) => not_supported!("rvalue len"),
Rvalue::UnaryOp(op, val) => {
let mut c = self.eval_operand(val, locals)?.get(&self)?;
let mut ty = self.operand_ty(val, locals)?;
@@ -612,27 +864,40 @@ impl Evaluator<'_> {
let size = self.size_of_sized(&ty, locals, "operand of unary op")?;
c = self.read_memory(Address::from_bytes(c)?, size)?;
}
- let mut c = c.to_vec();
- if ty.as_builtin() == Some(BuiltinType::Bool) {
- c[0] = 1 - c[0];
+ if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) {
+ match f {
+ chalk_ir::FloatTy::F32 => {
+ let c = -from_bytes!(f32, c);
+ Owned(c.to_le_bytes().into())
+ }
+ chalk_ir::FloatTy::F64 => {
+ let c = -from_bytes!(f64, c);
+ Owned(c.to_le_bytes().into())
+ }
+ }
} else {
- match op {
- UnOp::Not => c.iter_mut().for_each(|x| *x = !*x),
- UnOp::Neg => {
- c.iter_mut().for_each(|x| *x = !*x);
- for k in c.iter_mut() {
- let o;
- (*k, o) = k.overflowing_add(1);
- if !o {
- break;
+ let mut c = c.to_vec();
+ if ty.as_builtin() == Some(BuiltinType::Bool) {
+ c[0] = 1 - c[0];
+ } else {
+ match op {
+ UnOp::Not => c.iter_mut().for_each(|x| *x = !*x),
+ UnOp::Neg => {
+ c.iter_mut().for_each(|x| *x = !*x);
+ for k in c.iter_mut() {
+ let o;
+ (*k, o) = k.overflowing_add(1);
+ if !o {
+ break;
+ }
}
}
}
}
+ Owned(c)
}
- Owned(c)
}
- Rvalue::CheckedBinaryOp(op, lhs, rhs) => {
+ Rvalue::CheckedBinaryOp(op, lhs, rhs) => 'binary_op: {
let lc = self.eval_operand(lhs, locals)?;
let rc = self.eval_operand(rhs, locals)?;
let mut lc = lc.get(&self)?;
@@ -640,79 +905,170 @@ impl Evaluator<'_> {
let mut ty = self.operand_ty(lhs, locals)?;
while let TyKind::Ref(_, _, z) = ty.kind(Interner) {
ty = z.clone();
- let size = self.size_of_sized(&ty, locals, "operand of binary op")?;
+ let size = if ty.kind(Interner) == &TyKind::Str {
+ if *op != BinOp::Eq {
+ never!("Only eq is builtin for `str`");
+ }
+ let ls = from_bytes!(usize, &lc[self.ptr_size()..self.ptr_size() * 2]);
+ let rs = from_bytes!(usize, &rc[self.ptr_size()..self.ptr_size() * 2]);
+ if ls != rs {
+ break 'binary_op Owned(vec![0]);
+ }
+ lc = &lc[..self.ptr_size()];
+ rc = &rc[..self.ptr_size()];
+ ls
+ } else {
+ self.size_of_sized(&ty, locals, "operand of binary op")?
+ };
lc = self.read_memory(Address::from_bytes(lc)?, size)?;
rc = self.read_memory(Address::from_bytes(rc)?, size)?;
}
- let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
- let l128 = i128::from_le_bytes(pad16(lc, is_signed));
- let r128 = i128::from_le_bytes(pad16(rc, is_signed));
- match op {
- BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => {
- let r = match op {
- BinOp::Ge => l128 >= r128,
- BinOp::Gt => l128 > r128,
- BinOp::Le => l128 <= r128,
- BinOp::Lt => l128 < r128,
- BinOp::Eq => l128 == r128,
- BinOp::Ne => l128 != r128,
- _ => unreachable!(),
- };
- let r = r as u8;
- Owned(vec![r])
+ if let TyKind::Scalar(chalk_ir::Scalar::Float(f)) = ty.kind(Interner) {
+ match f {
+ chalk_ir::FloatTy::F32 => {
+ let l = from_bytes!(f32, lc);
+ let r = from_bytes!(f32, rc);
+ match op {
+ BinOp::Ge
+ | BinOp::Gt
+ | BinOp::Le
+ | BinOp::Lt
+ | BinOp::Eq
+ | BinOp::Ne => {
+ let r = op.run_compare(l, r) as u8;
+ Owned(vec![r])
+ }
+ BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => {
+ let r = match op {
+ BinOp::Add => l + r,
+ BinOp::Sub => l - r,
+ BinOp::Mul => l * r,
+ BinOp::Div => l / r,
+ _ => unreachable!(),
+ };
+ Owned(r.to_le_bytes().into())
+ }
+ x => not_supported!(
+ "invalid binop {x:?} on floating point operators"
+ ),
+ }
+ }
+ chalk_ir::FloatTy::F64 => {
+ let l = from_bytes!(f64, lc);
+ let r = from_bytes!(f64, rc);
+ match op {
+ BinOp::Ge
+ | BinOp::Gt
+ | BinOp::Le
+ | BinOp::Lt
+ | BinOp::Eq
+ | BinOp::Ne => {
+ let r = op.run_compare(l, r) as u8;
+ Owned(vec![r])
+ }
+ BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Div => {
+ let r = match op {
+ BinOp::Add => l + r,
+ BinOp::Sub => l - r,
+ BinOp::Mul => l * r,
+ BinOp::Div => l / r,
+ _ => unreachable!(),
+ };
+ Owned(r.to_le_bytes().into())
+ }
+ x => not_supported!(
+ "invalid binop {x:?} on floating point operators"
+ ),
+ }
+ }
}
- BinOp::BitAnd
- | BinOp::BitOr
- | BinOp::BitXor
- | BinOp::Add
- | BinOp::Mul
- | BinOp::Div
- | BinOp::Rem
- | BinOp::Sub => {
- let r = match op {
- BinOp::Add => l128.overflowing_add(r128).0,
- BinOp::Mul => l128.overflowing_mul(r128).0,
- BinOp::Div => l128.checked_div(r128).ok_or(MirEvalError::Panic)?,
- BinOp::Rem => l128.checked_rem(r128).ok_or(MirEvalError::Panic)?,
- BinOp::Sub => l128.overflowing_sub(r128).0,
- BinOp::BitAnd => l128 & r128,
- BinOp::BitOr => l128 | r128,
- BinOp::BitXor => l128 ^ r128,
- _ => unreachable!(),
- };
+ } else {
+ let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
+ let l128 = i128::from_le_bytes(pad16(lc, is_signed));
+ let r128 = i128::from_le_bytes(pad16(rc, is_signed));
+ let check_overflow = |r: i128| {
+ // FIXME: this is not very correct, and only catches the basic cases.
let r = r.to_le_bytes();
for &k in &r[lc.len()..] {
if k != 0 && (k != 255 || !is_signed) {
- return Err(MirEvalError::Panic);
+ return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
}
}
- Owned(r[0..lc.len()].into())
- }
- BinOp::Shl | BinOp::Shr => {
- let shift_amout = if r128 < 0 {
- return Err(MirEvalError::Panic);
- } else if r128 > 128 {
- return Err(MirEvalError::Panic);
- } else {
- r128 as u8
- };
- let r = match op {
- BinOp::Shl => l128 << shift_amout,
- BinOp::Shr => l128 >> shift_amout,
- _ => unreachable!(),
- };
- Owned(r.to_le_bytes()[0..lc.len()].into())
+ Ok(Owned(r[0..lc.len()].into()))
+ };
+ match op {
+ BinOp::Ge | BinOp::Gt | BinOp::Le | BinOp::Lt | BinOp::Eq | BinOp::Ne => {
+ let r = op.run_compare(l128, r128) as u8;
+ Owned(vec![r])
+ }
+ BinOp::BitAnd
+ | BinOp::BitOr
+ | BinOp::BitXor
+ | BinOp::Add
+ | BinOp::Mul
+ | BinOp::Div
+ | BinOp::Rem
+ | BinOp::Sub => {
+ let r = match op {
+ BinOp::Add => l128.overflowing_add(r128).0,
+ BinOp::Mul => l128.overflowing_mul(r128).0,
+ BinOp::Div => l128.checked_div(r128).ok_or_else(|| {
+ MirEvalError::Panic(format!("Overflow in {op:?}"))
+ })?,
+ BinOp::Rem => l128.checked_rem(r128).ok_or_else(|| {
+ MirEvalError::Panic(format!("Overflow in {op:?}"))
+ })?,
+ BinOp::Sub => l128.overflowing_sub(r128).0,
+ BinOp::BitAnd => l128 & r128,
+ BinOp::BitOr => l128 | r128,
+ BinOp::BitXor => l128 ^ r128,
+ _ => unreachable!(),
+ };
+ check_overflow(r)?
+ }
+ BinOp::Shl | BinOp::Shr => {
+ let r = 'b: {
+ if let Ok(shift_amount) = u32::try_from(r128) {
+ let r = match op {
+ BinOp::Shl => l128.checked_shl(shift_amount),
+ BinOp::Shr => l128.checked_shr(shift_amount),
+ _ => unreachable!(),
+ };
+ if let Some(r) = r {
+ break 'b r;
+ }
+ };
+ return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
+ };
+ check_overflow(r)?
+ }
+ BinOp::Offset => not_supported!("offset binop"),
}
- BinOp::Offset => not_supported!("offset binop"),
}
}
Rvalue::Discriminant(p) => {
let ty = self.place_ty(p, locals)?;
let bytes = self.eval_place(p, locals)?.get(&self)?;
let layout = self.layout(&ty)?;
- match layout.variants {
- Variants::Single { .. } => Owned(0u128.to_le_bytes().to_vec()),
- Variants::Multiple { tag, tag_encoding, .. } => {
+ let enum_id = 'b: {
+ match ty.kind(Interner) {
+ TyKind::Adt(e, _) => match e.0 {
+ AdtId::EnumId(e) => break 'b e,
+ _ => (),
+ },
+ _ => (),
+ }
+ return Ok(Owned(0u128.to_le_bytes().to_vec()));
+ };
+ match &layout.variants {
+ Variants::Single { index } => {
+ let r = self.const_eval_discriminant(EnumVariantId {
+ parent: enum_id,
+ local_id: index.0,
+ })?;
+ Owned(r.to_le_bytes().to_vec())
+ }
+ Variants::Multiple { tag, tag_encoding, variants, .. } => {
let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else {
not_supported!("missing target data layout");
};
@@ -725,120 +1081,142 @@ impl Evaluator<'_> {
}
TagEncoding::Niche { untagged_variant, niche_start, .. } => {
let tag = &bytes[offset..offset + size];
- let candidate_discriminant = i128::from_le_bytes(pad16(tag, false))
- .wrapping_sub(niche_start as i128);
- let enum_id = match ty.kind(Interner) {
- TyKind::Adt(e, _) => match e.0 {
- AdtId::EnumId(e) => e,
- _ => not_supported!("Non enum with multi variant layout"),
- },
- _ => not_supported!("Non adt with multi variant layout"),
- };
- let enum_data = self.db.enum_data(enum_id);
- let result = 'b: {
- for (local_id, _) in enum_data.variants.iter() {
- if candidate_discriminant
- == self.db.const_eval_discriminant(EnumVariantId {
- parent: enum_id,
- local_id,
- })?
- {
- break 'b candidate_discriminant;
- }
- }
- self.db.const_eval_discriminant(EnumVariantId {
- parent: enum_id,
- local_id: untagged_variant.0,
- })?
- };
+ let candidate_tag = i128::from_le_bytes(pad16(tag, false))
+ .wrapping_sub(*niche_start as i128)
+ as usize;
+ let variant = variants
+ .iter_enumerated()
+ .map(|(x, _)| x)
+ .filter(|x| x != untagged_variant)
+ .nth(candidate_tag)
+ .unwrap_or(*untagged_variant)
+ .0;
+ let result = self.const_eval_discriminant(EnumVariantId {
+ parent: enum_id,
+ local_id: variant,
+ })?;
Owned(result.to_le_bytes().to_vec())
}
}
}
}
}
+ Rvalue::Repeat(x, len) => {
+ let len = match try_const_usize(self.db, &len) {
+ Some(x) => x as usize,
+ None => not_supported!("non evaluatable array len in repeat Rvalue"),
+ };
+ let val = self.eval_operand(x, locals)?.get(self)?;
+ let size = len * val.len();
+ Owned(val.iter().copied().cycle().take(size).collect())
+ }
Rvalue::ShallowInitBox(_, _) => not_supported!("shallow init box"),
+ Rvalue::ShallowInitBoxWithAlloc(ty) => {
+ let Some((size, align)) = self.size_align_of(ty, locals)? else {
+ not_supported!("unsized box initialization");
+ };
+ let addr = self.heap_allocate(size, align);
+ Owned(addr.to_bytes())
+ }
Rvalue::CopyForDeref(_) => not_supported!("copy for deref"),
- Rvalue::Aggregate(kind, values) => match kind {
- AggregateKind::Array(_) => {
- let mut r = vec![];
- for x in values {
- let value = self.eval_operand(x, locals)?.get(&self)?;
- r.extend(value);
+ Rvalue::Aggregate(kind, values) => {
+ let values = values
+ .iter()
+ .map(|x| self.eval_operand(x, locals))
+ .collect::<Result<Vec<_>>>()?;
+ match kind {
+ AggregateKind::Array(_) => {
+ let mut r = vec![];
+ for x in values {
+ let value = x.get(&self)?;
+ r.extend(value);
+ }
+ Owned(r)
+ }
+ AggregateKind::Tuple(ty) => {
+ let layout = self.layout(&ty)?;
+ Owned(self.make_by_layout(
+ layout.size.bytes_usize(),
+ &layout,
+ None,
+ values.iter().map(|&x| x.into()),
+ )?)
+ }
+ AggregateKind::Union(x, f) => {
+ let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?;
+ let offset = layout
+ .fields
+ .offset(u32::from(f.local_id.into_raw()) as usize)
+ .bytes_usize();
+ let op = values[0].get(&self)?;
+ let mut result = vec![0; layout.size.bytes_usize()];
+ result[offset..offset + op.len()].copy_from_slice(op);
+ Owned(result)
+ }
+ AggregateKind::Adt(x, subst) => {
+ let (size, variant_layout, tag) =
+ self.layout_of_variant(*x, subst.clone(), locals)?;
+ Owned(self.make_by_layout(
+ size,
+ &variant_layout,
+ tag,
+ values.iter().map(|&x| x.into()),
+ )?)
+ }
+ AggregateKind::Closure(ty) => {
+ let layout = self.layout(&ty)?;
+ Owned(self.make_by_layout(
+ layout.size.bytes_usize(),
+ &layout,
+ None,
+ values.iter().map(|&x| x.into()),
+ )?)
}
- Owned(r)
- }
- AggregateKind::Tuple(ty) => {
- let layout = self.layout(&ty)?;
- Owned(self.make_by_layout(
- layout.size.bytes_usize(),
- &layout,
- None,
- values,
- locals,
- )?)
- }
- AggregateKind::Union(x, f) => {
- let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?;
- let offset = layout
- .fields
- .offset(u32::from(f.local_id.into_raw()) as usize)
- .bytes_usize();
- let op = self.eval_operand(&values[0], locals)?.get(&self)?;
- let mut result = vec![0; layout.size.bytes_usize()];
- result[offset..offset + op.len()].copy_from_slice(op);
- Owned(result)
- }
- AggregateKind::Adt(x, subst) => {
- let (size, variant_layout, tag) =
- self.layout_of_variant(*x, subst.clone(), locals)?;
- Owned(self.make_by_layout(size, &variant_layout, tag, values, locals)?)
}
- },
+ }
Rvalue::Cast(kind, operand, target_ty) => match kind {
- CastKind::PointerExposeAddress => not_supported!("exposing pointer address"),
- CastKind::PointerFromExposedAddress => {
- not_supported!("creating pointer from exposed address")
- }
CastKind::Pointer(cast) => match cast {
- PointerCast::Unsize => {
+ PointerCast::ReifyFnPointer | PointerCast::ClosureFnPointer(_) => {
let current_ty = self.operand_ty(operand, locals)?;
- match &target_ty.data(Interner).kind {
- TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
- match &ty.data(Interner).kind {
- TyKind::Slice(_) => match &current_ty.data(Interner).kind {
- TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
- match &ty.data(Interner).kind {
- TyKind::Array(_, size) => {
- let addr = self
- .eval_operand(operand, locals)?
- .get(&self)?;
- let len = const_as_usize(size);
- let mut r = Vec::with_capacity(16);
- r.extend(addr.iter().copied());
- r.extend(len.to_le_bytes().into_iter());
- Owned(r)
- }
- _ => {
- not_supported!("slice unsizing from non arrays")
- }
- }
- }
- _ => not_supported!("slice unsizing from non pointers"),
- },
- TyKind::Dyn(_) => not_supported!("dyn pointer unsize cast"),
- _ => not_supported!("unknown unsized cast"),
- }
- }
- _ => not_supported!("unsized cast on unknown pointer type"),
+ if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) =
+ &current_ty.data(Interner).kind
+ {
+ let id = self.vtable_map.id(current_ty);
+ let ptr_size = self.ptr_size();
+ Owned(id.to_le_bytes()[0..ptr_size].to_vec())
+ } else {
+ not_supported!(
+ "creating a fn pointer from a non FnDef or Closure type"
+ );
}
}
- x => not_supported!("pointer cast {x:?}"),
+ PointerCast::Unsize => {
+ let current_ty = self.operand_ty(operand, locals)?;
+ let addr = self.eval_operand(operand, locals)?;
+ self.coerce_unsized(addr, &current_ty, target_ty)?
+ }
+ PointerCast::MutToConstPointer | PointerCast::UnsafeFnPointer => {
+ // This is no-op
+ Borrowed(self.eval_operand(operand, locals)?)
+ }
+ PointerCast::ArrayToPointer => {
+ // We should remove the metadata part if the current type is slice
+ Borrowed(self.eval_operand(operand, locals)?.slice(0..self.ptr_size()))
+ }
},
CastKind::DynStar => not_supported!("dyn star cast"),
- CastKind::IntToInt => {
- // FIXME: handle signed cast
- let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, false);
+ CastKind::IntToInt
+ | CastKind::PointerExposeAddress
+ | CastKind::PointerFromExposedAddress => {
+ let current_ty = self.operand_ty(operand, locals)?;
+ let is_signed = match current_ty.kind(Interner) {
+ TyKind::Scalar(s) => match s {
+ chalk_ir::Scalar::Int(_) => true,
+ _ => false,
+ },
+ _ => false,
+ };
+ let current = pad16(self.eval_operand(operand, locals)?.get(&self)?, is_signed);
let dest_size =
self.size_of_sized(target_ty, locals, "destination of int to int cast")?;
Owned(current[0..dest_size].to_vec())
@@ -846,31 +1224,106 @@ impl Evaluator<'_> {
CastKind::FloatToInt => not_supported!("float to int cast"),
CastKind::FloatToFloat => not_supported!("float to float cast"),
CastKind::IntToFloat => not_supported!("float to int cast"),
- CastKind::PtrToPtr => not_supported!("ptr to ptr cast"),
CastKind::FnPtrToPtr => not_supported!("fn ptr to ptr cast"),
},
})
}
+ fn coerce_unsized_look_through_fields<T>(
+ &self,
+ ty: &Ty,
+ goal: impl Fn(&TyKind) -> Option<T>,
+ ) -> Result<T> {
+ let kind = ty.kind(Interner);
+ if let Some(x) = goal(kind) {
+ return Ok(x);
+ }
+ if let TyKind::Adt(id, subst) = kind {
+ if let AdtId::StructId(struct_id) = id.0 {
+ let field_types = self.db.field_types(struct_id.into());
+ let mut field_types = field_types.iter();
+ if let Some(ty) =
+ field_types.next().map(|x| x.1.clone().substitute(Interner, subst))
+ {
+ return self.coerce_unsized_look_through_fields(&ty, goal);
+ }
+ }
+ }
+ Err(MirEvalError::CoerceUnsizedError(ty.clone()))
+ }
+
+ fn coerce_unsized(
+ &mut self,
+ addr: Interval,
+ current_ty: &Ty,
+ target_ty: &Ty,
+ ) -> Result<IntervalOrOwned> {
+ use IntervalOrOwned::*;
+ fn for_ptr(x: &TyKind) -> Option<Ty> {
+ match x {
+ TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()),
+ _ => None,
+ }
+ }
+ Ok(match self.coerce_unsized_look_through_fields(target_ty, for_ptr)? {
+ ty => match &ty.data(Interner).kind {
+ TyKind::Slice(_) => {
+ match self.coerce_unsized_look_through_fields(current_ty, for_ptr)? {
+ ty => match &ty.data(Interner).kind {
+ TyKind::Array(_, size) => {
+ let len = match try_const_usize(self.db, size) {
+ None => not_supported!(
+ "unevaluatble len of array in coerce unsized"
+ ),
+ Some(x) => x as usize,
+ };
+ let mut r = Vec::with_capacity(16);
+ let addr = addr.get(self)?;
+ r.extend(addr.iter().copied());
+ r.extend(len.to_le_bytes().into_iter());
+ Owned(r)
+ }
+ t => {
+ not_supported!("slice unsizing from non array type {t:?}")
+ }
+ },
+ }
+ }
+ TyKind::Dyn(_) => match &current_ty.data(Interner).kind {
+ TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
+ let vtable = self.vtable_map.id(ty.clone());
+ let mut r = Vec::with_capacity(16);
+ let addr = addr.get(self)?;
+ r.extend(addr.iter().copied());
+ r.extend(vtable.to_le_bytes().into_iter());
+ Owned(r)
+ }
+ _ => not_supported!("dyn unsizing from non pointers"),
+ },
+ _ => not_supported!("unknown unsized cast"),
+ },
+ })
+ }
+
fn layout_of_variant(
&mut self,
x: VariantId,
subst: Substitution,
locals: &Locals<'_>,
- ) -> Result<(usize, Layout, Option<(usize, usize, i128)>)> {
+ ) -> Result<(usize, Arc<Layout>, Option<(usize, usize, i128)>)> {
let adt = x.adt_id();
if let DefWithBodyId::VariantId(f) = locals.body.owner {
if let VariantId::EnumVariantId(x) = x {
if AdtId::from(f.parent) == adt {
// Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and
// infinite sized type errors) we use a dummy layout
- let i = self.db.const_eval_discriminant(x)?;
+ let i = self.const_eval_discriminant(x)?;
return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i))));
}
}
}
let layout = self.layout_adt(adt, subst)?;
- Ok(match layout.variants {
+ Ok(match &layout.variants {
Variants::Single { .. } => (layout.size.bytes_usize(), layout, None),
Variants::Multiple { variants, tag, tag_encoding, .. } => {
let cx = self
@@ -882,18 +1335,27 @@ impl Evaluator<'_> {
_ => not_supported!("multi variant layout for non-enums"),
};
let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.local_id);
- let mut discriminant = self.db.const_eval_discriminant(enum_variant_id)?;
+ let mut discriminant = self.const_eval_discriminant(enum_variant_id)?;
let variant_layout = variants[rustc_enum_variant_idx].clone();
let have_tag = match tag_encoding {
TagEncoding::Direct => true,
TagEncoding::Niche { untagged_variant, niche_variants: _, niche_start } => {
- discriminant = discriminant.wrapping_add(niche_start as i128);
- untagged_variant != rustc_enum_variant_idx
+ if *untagged_variant == rustc_enum_variant_idx {
+ false
+ } else {
+ discriminant = (variants
+ .iter_enumerated()
+ .filter(|(x, _)| x != untagged_variant)
+ .position(|(x, _)| x == rustc_enum_variant_idx)
+ .unwrap() as i128)
+ .wrapping_add(*niche_start as i128);
+ true
+ }
}
};
(
layout.size.bytes_usize(),
- variant_layout,
+ Arc::new(variant_layout),
if have_tag {
Some((
layout.fields.offset(0).bytes_usize(),
@@ -910,71 +1372,84 @@ impl Evaluator<'_> {
fn make_by_layout(
&mut self,
- size: usize, // Not neccessarily equal to variant_layout.size
+ size: usize, // Not necessarily equal to variant_layout.size
variant_layout: &Layout,
tag: Option<(usize, usize, i128)>,
- values: &Vec<Operand>,
- locals: &Locals<'_>,
+ values: impl Iterator<Item = IntervalOrOwned>,
) -> Result<Vec<u8>> {
let mut result = vec![0; size];
if let Some((offset, size, value)) = tag {
result[offset..offset + size].copy_from_slice(&value.to_le_bytes()[0..size]);
}
- for (i, op) in values.iter().enumerate() {
+ for (i, op) in values.enumerate() {
let offset = variant_layout.fields.offset(i).bytes_usize();
- let op = self.eval_operand(op, locals)?.get(&self)?;
+ let op = op.get(&self)?;
result[offset..offset + op.len()].copy_from_slice(op);
}
Ok(result)
}
- fn eval_operand(&mut self, x: &Operand, locals: &Locals<'_>) -> Result<Interval> {
+ fn eval_operand(&mut self, x: &Operand, locals: &mut Locals<'_>) -> Result<Interval> {
Ok(match x {
- Operand::Copy(p) | Operand::Move(p) => self.eval_place(p, locals)?,
+ Operand::Copy(p) | Operand::Move(p) => {
+ locals.drop_flags.remove_place(p);
+ self.eval_place(p, locals)?
+ }
+ Operand::Static(st) => {
+ let addr = self.eval_static(*st, locals)?;
+ Interval::new(addr, self.ptr_size())
+ }
Operand::Constant(konst) => {
let data = &konst.data(Interner);
match &data.value {
- chalk_ir::ConstValue::BoundVar(b) => {
- let c = locals
- .subst
- .as_slice(Interner)
- .get(b.index)
- .ok_or(MirEvalError::TypeError("missing generic arg"))?
- .assert_const_ref(Interner);
- self.eval_operand(&Operand::Constant(c.clone()), locals)?
- }
+ chalk_ir::ConstValue::BoundVar(_) => not_supported!("bound var constant"),
chalk_ir::ConstValue::InferenceVar(_) => {
not_supported!("inference var constant")
}
chalk_ir::ConstValue::Placeholder(_) => not_supported!("placeholder constant"),
- chalk_ir::ConstValue::Concrete(c) => match &c.interned {
- ConstScalar::Bytes(v, memory_map) => {
- let mut v: Cow<'_, [u8]> = Cow::Borrowed(v);
- let patch_map = memory_map.transform_addresses(|b| {
- let addr = self.heap_allocate(b.len());
- self.write_memory(addr, b)?;
- Ok(addr.to_usize())
- })?;
- let size = self.size_of(&data.ty, locals)?.unwrap_or(v.len());
- if size != v.len() {
- // Handle self enum
- if size == 16 && v.len() < 16 {
- v = Cow::Owned(pad16(&v, false).to_vec());
- } else if size < 16 && v.len() == 16 {
- v = Cow::Owned(v[0..size].to_vec());
- } else {
- return Err(MirEvalError::InvalidConst(konst.clone()));
- }
- }
- let addr = self.heap_allocate(size);
- self.write_memory(addr, &v)?;
- self.patch_addresses(&patch_map, addr, &data.ty, locals)?;
- Interval::new(addr, size)
- }
- ConstScalar::Unknown => not_supported!("evaluating unknown const"),
- },
+ chalk_ir::ConstValue::Concrete(c) => {
+ self.allocate_const_in_heap(c, &data.ty, locals, konst)?
+ }
+ }
+ }
+ })
+ }
+
+ fn allocate_const_in_heap(
+ &mut self,
+ c: &chalk_ir::ConcreteConst<Interner>,
+ ty: &Ty,
+ locals: &Locals<'_>,
+ konst: &chalk_ir::Const<Interner>,
+ ) -> Result<Interval> {
+ Ok(match &c.interned {
+ ConstScalar::Bytes(v, memory_map) => {
+ let mut v: Cow<'_, [u8]> = Cow::Borrowed(v);
+ let patch_map = memory_map.transform_addresses(|b| {
+ let addr = self.heap_allocate(b.len(), 1); // FIXME: align is wrong
+ self.write_memory(addr, b)?;
+ Ok(addr.to_usize())
+ })?;
+ let (size, align) = self.size_align_of(ty, locals)?.unwrap_or((v.len(), 1));
+ if size != v.len() {
+ // Handle self enum
+ if size == 16 && v.len() < 16 {
+ v = Cow::Owned(pad16(&v, false).to_vec());
+ } else if size < 16 && v.len() == 16 {
+ v = Cow::Owned(v[0..size].to_vec());
+ } else {
+ return Err(MirEvalError::InvalidConst(konst.clone()));
+ }
}
+ let addr = self.heap_allocate(size, align);
+ self.write_memory(addr, &v)?;
+ self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?;
+ Interval::new(addr, size)
+ }
+ ConstScalar::UnevaluatedConst(..) => {
+ not_supported!("unevaluated const present in monomorphized mir");
}
+ ConstScalar::Unknown => not_supported!("evaluating unknown const"),
})
}
@@ -987,197 +1462,213 @@ impl Evaluator<'_> {
}
fn read_memory(&self, addr: Address, size: usize) -> Result<&[u8]> {
+ if size == 0 {
+ return Ok(&[]);
+ }
let (mem, pos) = match addr {
Stack(x) => (&self.stack, x),
Heap(x) => (&self.heap, x),
+ Invalid(x) => {
+ return Err(MirEvalError::UndefinedBehavior(format!(
+ "read invalid memory address {x} with size {size}"
+ )));
+ }
};
- mem.get(pos..pos + size).ok_or(MirEvalError::UndefinedBehavior("out of bound memory read"))
+ mem.get(pos..pos + size)
+ .ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory read".to_string()))
}
fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> {
+ if r.is_empty() {
+ return Ok(());
+ }
let (mem, pos) = match addr {
Stack(x) => (&mut self.stack, x),
Heap(x) => (&mut self.heap, x),
+ Invalid(x) => {
+ return Err(MirEvalError::UndefinedBehavior(format!(
+ "write invalid memory address {x} with content {r:?}"
+ )));
+ }
};
mem.get_mut(pos..pos + r.len())
- .ok_or(MirEvalError::UndefinedBehavior("out of bound memory write"))?
+ .ok_or_else(|| {
+ MirEvalError::UndefinedBehavior("out of bound memory write".to_string())
+ })?
.copy_from_slice(r);
Ok(())
}
- fn size_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result<Option<usize>> {
+ fn size_align_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result<Option<(usize, usize)>> {
if let DefWithBodyId::VariantId(f) = locals.body.owner {
if let Some((adt, _)) = ty.as_adt() {
if AdtId::from(f.parent) == adt {
// Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and
// infinite sized type errors) we use a dummy size
- return Ok(Some(16));
+ return Ok(Some((16, 16)));
}
}
}
- let ty = &self.ty_filler(ty, locals.subst, locals.body.owner)?;
let layout = self.layout(ty);
if self.assert_placeholder_ty_is_unused {
if matches!(layout, Err(MirEvalError::LayoutError(LayoutError::HasPlaceholder, _))) {
- return Ok(Some(0));
+ return Ok(Some((0, 1)));
}
}
let layout = layout?;
- Ok(layout.is_sized().then(|| layout.size.bytes_usize()))
+ Ok(layout
+ .is_sized()
+ .then(|| (layout.size.bytes_usize(), layout.align.abi.bytes() as usize)))
}
/// A version of `self.size_of` which returns error if the type is unsized. `what` argument should
/// be something that complete this: `error: type {ty} was unsized. {what} should be sized`
fn size_of_sized(&self, ty: &Ty, locals: &Locals<'_>, what: &'static str) -> Result<usize> {
- match self.size_of(ty, locals)? {
- Some(x) => Ok(x),
+ match self.size_align_of(ty, locals)? {
+ Some(x) => Ok(x.0),
None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)),
}
}
- /// Uses `ty_filler` to fill an entire subst
- fn subst_filler(&self, subst: &Substitution, locals: &Locals<'_>) -> Substitution {
- Substitution::from_iter(
- Interner,
- subst.iter(Interner).map(|x| match x.data(Interner) {
- chalk_ir::GenericArgData::Ty(ty) => {
- let Ok(ty) = self.ty_filler(ty, locals.subst, locals.body.owner) else {
- return x.clone();
- };
- chalk_ir::GenericArgData::Ty(ty).intern(Interner)
- }
- _ => x.clone(),
- }),
- )
- }
-
- /// This function substitutes placeholders of the body with the provided subst, effectively plays
- /// the rule of monomorphization. In addition to placeholders, it substitutes opaque types (return
- /// position impl traits) with their underlying type.
- fn ty_filler(&self, ty: &Ty, subst: &Substitution, owner: DefWithBodyId) -> Result<Ty> {
- struct Filler<'a> {
- db: &'a dyn HirDatabase,
- subst: &'a Substitution,
- skip_params: usize,
- }
- impl FallibleTypeFolder<Interner> for Filler<'_> {
- type Error = MirEvalError;
-
- fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
- self
- }
-
- fn interner(&self) -> Interner {
- Interner
- }
-
- fn try_fold_ty(
- &mut self,
- ty: Ty,
- outer_binder: DebruijnIndex,
- ) -> std::result::Result<Ty, Self::Error> {
- match ty.kind(Interner) {
- TyKind::OpaqueType(id, subst) => {
- let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into());
- match impl_trait_id {
- crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
- let infer = self.db.infer(func.into());
- let filler = &mut Filler { db: self.db, subst, skip_params: 0 };
- filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
- }
- crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
- not_supported!("async block impl trait");
- }
- }
- }
- _ => ty.try_super_fold_with(self.as_dyn(), outer_binder),
- }
- }
-
- fn try_fold_free_placeholder_ty(
- &mut self,
- idx: chalk_ir::PlaceholderIndex,
- _outer_binder: DebruijnIndex,
- ) -> std::result::Result<Ty, Self::Error> {
- let x = from_placeholder_idx(self.db, idx);
- Ok(self
- .subst
- .as_slice(Interner)
- .get((u32::from(x.local_id.into_raw()) as usize) + self.skip_params)
- .and_then(|x| x.ty(Interner))
- .ok_or(MirEvalError::TypeError("Generic arg not provided"))?
- .clone())
- }
- }
- let filler = &mut Filler { db: self.db, subst, skip_params: 0 };
- Ok(normalize(self.db, owner, ty.clone().try_fold_with(filler, DebruijnIndex::INNERMOST)?))
- }
-
- fn heap_allocate(&mut self, s: usize) -> Address {
+ fn heap_allocate(&mut self, size: usize, _align: usize) -> Address {
let pos = self.heap.len();
- self.heap.extend(iter::repeat(0).take(s));
+ self.heap.extend(iter::repeat(0).take(size));
Address::Heap(pos)
}
- pub fn interpret_mir_with_no_arg(&mut self, body: &MirBody) -> Result<Vec<u8>> {
- self.interpret_mir(&body, vec![].into_iter(), Substitution::empty(Interner))
- }
-
- fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
- let candidate = lang_attr(self.db.upcast(), def)?;
- // filter normal lang functions out
- if [LangItem::IntoIterIntoIter, LangItem::IteratorNext].contains(&candidate) {
+ fn detect_fn_trait(&self, def: FunctionId) -> Option<FnTrait> {
+ use LangItem::*;
+ let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else {
return None;
+ };
+ let l = lang_attr(self.db.upcast(), parent)?;
+ match l {
+ FnOnce => Some(FnTrait::FnOnce),
+ FnMut => Some(FnTrait::FnMut),
+ Fn => Some(FnTrait::Fn),
+ _ => None,
}
- Some(candidate)
}
fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result<MemoryMap> {
- // FIXME: support indirect references
- let mut mm = MemoryMap::default();
- match ty.kind(Interner) {
- TyKind::Ref(_, _, t) => {
- let size = self.size_of(t, locals)?;
- match size {
- Some(size) => {
- let addr_usize = from_bytes!(usize, bytes);
- mm.insert(
- addr_usize,
- self.read_memory(Address::from_usize(addr_usize), size)?.to_vec(),
- )
- }
- None => {
- let element_size = match t.kind(Interner) {
- TyKind::Str => 1,
- TyKind::Slice(t) => {
- self.size_of_sized(t, locals, "slice inner type")?
+ fn rec(
+ this: &Evaluator<'_>,
+ bytes: &[u8],
+ ty: &Ty,
+ locals: &Locals<'_>,
+ mm: &mut MemoryMap,
+ ) -> Result<()> {
+ match ty.kind(Interner) {
+ TyKind::Ref(_, _, t) => {
+ let size = this.size_align_of(t, locals)?;
+ match size {
+ Some((size, _)) => {
+ let addr_usize = from_bytes!(usize, bytes);
+ mm.insert(
+ addr_usize,
+ this.read_memory(Address::from_usize(addr_usize), size)?.to_vec(),
+ )
+ }
+ None => {
+ let mut check_inner = None;
+ let (addr, meta) = bytes.split_at(bytes.len() / 2);
+ let element_size = match t.kind(Interner) {
+ TyKind::Str => 1,
+ TyKind::Slice(t) => {
+ check_inner = Some(t);
+ this.size_of_sized(t, locals, "slice inner type")?
+ }
+ TyKind::Dyn(_) => {
+ let t = this.vtable_map.ty_of_bytes(meta)?;
+ check_inner = Some(t);
+ this.size_of_sized(t, locals, "dyn concrete type")?
+ }
+ _ => return Ok(()),
+ };
+ let count = match t.kind(Interner) {
+ TyKind::Dyn(_) => 1,
+ _ => from_bytes!(usize, meta),
+ };
+ let size = element_size * count;
+ let addr = Address::from_bytes(addr)?;
+ let b = this.read_memory(addr, size)?;
+ mm.insert(addr.to_usize(), b.to_vec());
+ if let Some(ty) = check_inner {
+ for i in 0..count {
+ let offset = element_size * i;
+ rec(this, &b[offset..offset + element_size], &ty, locals, mm)?;
+ }
}
- _ => return Ok(mm), // FIXME: support other kind of unsized types
- };
- let (addr, meta) = bytes.split_at(bytes.len() / 2);
- let size = element_size * from_bytes!(usize, meta);
- let addr = Address::from_bytes(addr)?;
- mm.insert(addr.to_usize(), self.read_memory(addr, size)?.to_vec());
+ }
}
}
+ chalk_ir::TyKind::Tuple(_, subst) => {
+ let layout = this.layout(ty)?;
+ for (id, ty) in subst.iter(Interner).enumerate() {
+ let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
+ let offset = layout.fields.offset(id).bytes_usize();
+ let size = this.layout(ty)?.size.bytes_usize();
+ rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
+ }
+ }
+ chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
+ AdtId::StructId(s) => {
+ let data = this.db.struct_data(s);
+ let layout = this.layout(ty)?;
+ let field_types = this.db.field_types(s.into());
+ for (f, _) in data.variant_data.fields().iter() {
+ let offset = layout
+ .fields
+ .offset(u32::from(f.into_raw()) as usize)
+ .bytes_usize();
+ let ty = &field_types[f].clone().substitute(Interner, subst);
+ let size = this.layout(ty)?.size.bytes_usize();
+ rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
+ }
+ }
+ AdtId::EnumId(e) => {
+ let layout = this.layout(ty)?;
+ if let Some((v, l)) =
+ detect_variant_from_bytes(&layout, this.db, this.crate_id, bytes, e)
+ {
+ let data = &this.db.enum_data(e).variants[v].variant_data;
+ let field_types = this
+ .db
+ .field_types(EnumVariantId { parent: e, local_id: v }.into());
+ for (f, _) in data.fields().iter() {
+ let offset =
+ l.fields.offset(u32::from(f.into_raw()) as usize).bytes_usize();
+ let ty = &field_types[f].clone().substitute(Interner, subst);
+ let size = this.layout(ty)?.size.bytes_usize();
+ rec(this, &bytes[offset..offset + size], ty, locals, mm)?;
+ }
+ }
+ }
+ AdtId::UnionId(_) => (),
+ },
+ _ => (),
}
- _ => (),
+ Ok(())
}
+ let mut mm = MemoryMap::default();
+ rec(self, bytes, ty, locals, &mut mm)?;
Ok(mm)
}
fn patch_addresses(
&mut self,
patch_map: &HashMap<usize, usize>,
+ old_vtable: &VTableMap,
addr: Address,
ty: &Ty,
locals: &Locals<'_>,
) -> Result<()> {
// FIXME: support indirect references
+ let layout = self.layout(ty)?;
let my_size = self.size_of_sized(ty, locals, "value to patch address")?;
match ty.kind(Interner) {
TyKind::Ref(_, _, t) => {
- let size = self.size_of(t, locals)?;
+ let size = self.size_align_of(t, locals)?;
match size {
Some(_) => {
let current = from_bytes!(usize, self.read_memory(addr, my_size)?);
@@ -1193,51 +1684,441 @@ impl Evaluator<'_> {
}
}
}
- _ => (),
+ TyKind::Function(_) => {
+ let ty = old_vtable.ty_of_bytes(self.read_memory(addr, my_size)?)?.clone();
+ let new_id = self.vtable_map.id(ty);
+ self.write_memory(addr, &new_id.to_le_bytes())?;
+ }
+ TyKind::Adt(id, subst) => match id.0 {
+ AdtId::StructId(s) => {
+ for (i, (_, ty)) in self.db.field_types(s.into()).iter().enumerate() {
+ let offset = layout.fields.offset(i).bytes_usize();
+ let ty = ty.clone().substitute(Interner, subst);
+ self.patch_addresses(
+ patch_map,
+ old_vtable,
+ addr.offset(offset),
+ &ty,
+ locals,
+ )?;
+ }
+ }
+ AdtId::UnionId(_) => (),
+ AdtId::EnumId(_) => (),
+ },
+ TyKind::AssociatedType(_, _)
+ | TyKind::Scalar(_)
+ | TyKind::Tuple(_, _)
+ | TyKind::Array(_, _)
+ | TyKind::Slice(_)
+ | TyKind::Raw(_, _)
+ | TyKind::OpaqueType(_, _)
+ | TyKind::FnDef(_, _)
+ | TyKind::Str
+ | TyKind::Never
+ | TyKind::Closure(_, _)
+ | TyKind::Generator(_, _)
+ | TyKind::GeneratorWitness(_, _)
+ | TyKind::Foreign(_)
+ | TyKind::Error
+ | TyKind::Placeholder(_)
+ | TyKind::Dyn(_)
+ | TyKind::Alias(_)
+ | TyKind::BoundVar(_)
+ | TyKind::InferenceVar(_, _) => (),
}
Ok(())
}
- fn exec_intrinsic(
- &self,
- as_str: &str,
- _arg_bytes: impl Iterator<Item = Vec<u8>>,
- generic_args: Substitution,
+ fn exec_fn_pointer(
+ &mut self,
+ bytes: Interval,
+ destination: Interval,
+ args: &[IntervalAndTy],
locals: &Locals<'_>,
- ) -> Result<Vec<u8>> {
- match as_str {
- "size_of" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
- return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
- };
- let size = self.size_of(ty, locals)?;
- match size {
- Some(x) => Ok(x.to_le_bytes().to_vec()),
- None => return Err(MirEvalError::TypeError("size_of arg is unsized")),
+ span: MirSpan,
+ ) -> Result<()> {
+ let id = from_bytes!(usize, bytes.get(self)?);
+ let next_ty = self.vtable_map.ty(id)?.clone();
+ match &next_ty.data(Interner).kind {
+ TyKind::FnDef(def, generic_args) => {
+ self.exec_fn_def(*def, generic_args, destination, args, &locals, span)?;
+ }
+ TyKind::Closure(id, subst) => {
+ self.exec_closure(*id, bytes.slice(0..0), subst, destination, args, locals, span)?;
+ }
+ _ => return Err(MirEvalError::TypeError("function pointer to non function")),
+ }
+ Ok(())
+ }
+
+ fn exec_closure(
+ &mut self,
+ closure: ClosureId,
+ closure_data: Interval,
+ generic_args: &Substitution,
+ destination: Interval,
+ args: &[IntervalAndTy],
+ locals: &Locals<'_>,
+ span: MirSpan,
+ ) -> Result<()> {
+ let mir_body = self
+ .db
+ .monomorphized_mir_body_for_closure(
+ closure,
+ generic_args.clone(),
+ self.trait_env.clone(),
+ )
+ .map_err(|x| MirEvalError::MirLowerErrorForClosure(closure, x))?;
+ let closure_data = if mir_body.locals[mir_body.param_locals[0]].ty.as_reference().is_some()
+ {
+ closure_data.addr.to_bytes()
+ } else {
+ closure_data.get(self)?.to_owned()
+ };
+ let arg_bytes = iter::once(Ok(closure_data))
+ .chain(args.iter().map(|x| Ok(x.get(&self)?.to_owned())))
+ .collect::<Result<Vec<_>>>()?;
+ let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter()).map_err(|e| {
+ MirEvalError::InFunction(Either::Right(closure), Box::new(e), span, locals.body.owner)
+ })?;
+ destination.write_from_bytes(self, &bytes)
+ }
+
+ fn exec_fn_def(
+ &mut self,
+ def: FnDefId,
+ generic_args: &Substitution,
+ destination: Interval,
+ args: &[IntervalAndTy],
+ locals: &Locals<'_>,
+ span: MirSpan,
+ ) -> Result<()> {
+ let def: CallableDefId = from_chalk(self.db, def);
+ let generic_args = generic_args.clone();
+ match def {
+ CallableDefId::FunctionId(def) => {
+ if let Some(_) = self.detect_fn_trait(def) {
+ self.exec_fn_trait(&args, destination, locals, span)?;
+ return Ok(());
}
+ self.exec_fn_with_args(def, args, generic_args, locals, destination, span)?;
+ }
+ CallableDefId::StructId(id) => {
+ let (size, variant_layout, tag) =
+ self.layout_of_variant(id.into(), generic_args, &locals)?;
+ let result = self.make_by_layout(
+ size,
+ &variant_layout,
+ tag,
+ args.iter().map(|x| x.interval.into()),
+ )?;
+ destination.write_from_bytes(self, &result)?;
+ }
+ CallableDefId::EnumVariantId(id) => {
+ let (size, variant_layout, tag) =
+ self.layout_of_variant(id.into(), generic_args, &locals)?;
+ let result = self.make_by_layout(
+ size,
+ &variant_layout,
+ tag,
+ args.iter().map(|x| x.interval.into()),
+ )?;
+ destination.write_from_bytes(self, &result)?;
}
- _ => not_supported!("unknown intrinsic {as_str}"),
}
+ Ok(())
}
- pub(crate) fn exec_lang_item(
- &self,
- x: LangItem,
- mut args: std::vec::IntoIter<Vec<u8>>,
- ) -> Result<Vec<u8>> {
- use LangItem::*;
- match x {
- PanicFmt | BeginPanic => Err(MirEvalError::Panic),
- SliceLen => {
- let arg = args
- .next()
- .ok_or(MirEvalError::TypeError("argument of <[T]>::len() is not provided"))?;
- let ptr_size = arg.len() / 2;
- Ok(arg[ptr_size..].into())
+ fn exec_fn_with_args(
+ &mut self,
+ def: FunctionId,
+ args: &[IntervalAndTy],
+ generic_args: Substitution,
+ locals: &Locals<'_>,
+ destination: Interval,
+ span: MirSpan,
+ ) -> Result<()> {
+ if self.detect_and_exec_special_function(
+ def,
+ args,
+ &generic_args,
+ locals,
+ destination,
+ span,
+ )? {
+ return Ok(());
+ }
+ let arg_bytes =
+ args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::<Result<Vec<_>>>()?;
+ if let Some(self_ty_idx) =
+ is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone())
+ {
+ // In the layout of current possible receiver, which at the moment of writing this code is one of
+ // `&T`, `&mut T`, `Box<T>`, `Rc<T>`, `Arc<T>`, and `Pin<P>` where `P` is one of possible recievers,
+ // the vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on
+ // the type.
+ let ty =
+ self.vtable_map.ty_of_bytes(&arg_bytes[0][self.ptr_size()..self.ptr_size() * 2])?;
+ let mut args_for_target = args.to_vec();
+ args_for_target[0] = IntervalAndTy {
+ interval: args_for_target[0].interval.slice(0..self.ptr_size()),
+ ty: ty.clone(),
+ };
+ let ty = GenericArgData::Ty(ty.clone()).intern(Interner);
+ let generics_for_target =
+ Substitution::from_iter(
+ Interner,
+ generic_args.iter(Interner).enumerate().map(|(i, x)| {
+ if i == self_ty_idx {
+ &ty
+ } else {
+ x
+ }
+ }),
+ );
+ return self.exec_fn_with_args(
+ def,
+ &args_for_target,
+ generics_for_target,
+ locals,
+ destination,
+ span,
+ );
+ }
+ let (imp, generic_args) =
+ lookup_impl_method(self.db, self.trait_env.clone(), def, generic_args);
+ self.exec_looked_up_function(generic_args, locals, imp, arg_bytes, span, destination)
+ }
+
+ fn exec_looked_up_function(
+ &mut self,
+ generic_args: Substitution,
+ locals: &Locals<'_>,
+ imp: FunctionId,
+ arg_bytes: Vec<Vec<u8>>,
+ span: MirSpan,
+ destination: Interval,
+ ) -> Result<()> {
+ let def = imp.into();
+ let mir_body = self
+ .db
+ .monomorphized_mir_body(def, generic_args, self.trait_env.clone())
+ .map_err(|e| {
+ MirEvalError::InFunction(
+ Either::Left(imp),
+ Box::new(MirEvalError::MirLowerError(imp, e)),
+ span,
+ locals.body.owner,
+ )
+ })?;
+ let result = self.interpret_mir(&mir_body, arg_bytes.iter().cloned()).map_err(|e| {
+ MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner)
+ })?;
+ destination.write_from_bytes(self, &result)?;
+ Ok(())
+ }
+
+ fn exec_fn_trait(
+ &mut self,
+ args: &[IntervalAndTy],
+ destination: Interval,
+ locals: &Locals<'_>,
+ span: MirSpan,
+ ) -> Result<()> {
+ let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?;
+ let mut func_ty = func.ty.clone();
+ let mut func_data = func.interval;
+ while let TyKind::Ref(_, _, z) = func_ty.kind(Interner) {
+ func_ty = z.clone();
+ if matches!(func_ty.kind(Interner), TyKind::Dyn(_)) {
+ let id =
+ from_bytes!(usize, &func_data.get(self)?[self.ptr_size()..self.ptr_size() * 2]);
+ func_data = func_data.slice(0..self.ptr_size());
+ func_ty = self.vtable_map.ty(id)?.clone();
+ }
+ let size = self.size_of_sized(&func_ty, locals, "self type of fn trait")?;
+ func_data = Interval { addr: Address::from_bytes(func_data.get(self)?)?, size };
+ }
+ match &func_ty.data(Interner).kind {
+ TyKind::FnDef(def, subst) => {
+ self.exec_fn_def(*def, subst, destination, &args[1..], locals, span)?;
+ }
+ TyKind::Function(_) => {
+ self.exec_fn_pointer(func_data, destination, &args[1..], locals, span)?;
+ }
+ TyKind::Closure(closure, subst) => {
+ self.exec_closure(
+ *closure,
+ func_data,
+ &Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()),
+ destination,
+ &args[1..],
+ locals,
+ span,
+ )?;
+ }
+ x => not_supported!("Call FnTrait methods with type {x:?}"),
+ }
+ Ok(())
+ }
+
+ fn eval_static(&mut self, st: StaticId, locals: &Locals<'_>) -> Result<Address> {
+ if let Some(o) = self.static_locations.get(&st) {
+ return Ok(*o);
+ };
+ let static_data = self.db.static_data(st);
+ let result = if !static_data.is_extern {
+ let konst = self.db.const_eval_static(st).map_err(|e| {
+ MirEvalError::ConstEvalError(
+ static_data.name.as_str().unwrap_or("_").to_owned(),
+ Box::new(e),
+ )
+ })?;
+ let data = &konst.data(Interner);
+ if let chalk_ir::ConstValue::Concrete(c) = &data.value {
+ self.allocate_const_in_heap(&c, &data.ty, locals, &konst)?
+ } else {
+ not_supported!("unevaluatable static");
+ }
+ } else {
+ let ty = &self.db.infer(st.into())[self.db.body(st.into()).body_expr];
+ let Some((size, align)) = self.size_align_of(&ty, locals)? else {
+ not_supported!("unsized extern static");
+ };
+ let addr = self.heap_allocate(size, align);
+ Interval::new(addr, size)
+ };
+ let addr = self.heap_allocate(self.ptr_size(), self.ptr_size());
+ self.write_memory(addr, &result.addr.to_bytes())?;
+ self.static_locations.insert(st, addr);
+ Ok(addr)
+ }
+
+ fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<i128> {
+ let r = self.db.const_eval_discriminant(variant);
+ match r {
+ Ok(r) => Ok(r),
+ Err(e) => {
+ let data = self.db.enum_data(variant.parent);
+ let name = format!(
+ "{}::{}",
+ data.name.display(self.db.upcast()),
+ data.variants[variant.local_id].name.display(self.db.upcast())
+ );
+ Err(MirEvalError::ConstEvalError(name, Box::new(e)))
}
- x => not_supported!("Executing lang item {x:?}"),
}
}
+
+ fn drop_place(&mut self, place: &Place, locals: &mut Locals<'_>, span: MirSpan) -> Result<()> {
+ let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?;
+ if !locals.drop_flags.remove_place(place) {
+ return Ok(());
+ }
+ let metadata = match metadata {
+ Some(x) => x.get(self)?.to_vec(),
+ None => vec![],
+ };
+ self.run_drop_glue_deep(ty, locals, addr, &metadata, span)
+ }
+
+ fn run_drop_glue_deep(
+ &mut self,
+ ty: Ty,
+ locals: &Locals<'_>,
+ addr: Address,
+ _metadata: &[u8],
+ span: MirSpan,
+ ) -> Result<()> {
+ let Some(drop_fn) = (|| {
+ let drop_trait = self.db.lang_item(self.crate_id, LangItem::Drop)?.as_trait()?;
+ self.db.trait_data(drop_trait).method_by_name(&name![drop])
+ })() else {
+ // in some tests we don't have drop trait in minicore, and
+ // we can ignore drop in them.
+ return Ok(());
+ };
+ let (impl_drop_candidate, subst) = lookup_impl_method(
+ self.db,
+ self.trait_env.clone(),
+ drop_fn,
+ Substitution::from1(Interner, ty.clone()),
+ );
+ if impl_drop_candidate != drop_fn {
+ self.exec_looked_up_function(
+ subst,
+ locals,
+ impl_drop_candidate,
+ vec![addr.to_bytes()],
+ span,
+ Interval { addr: Address::Invalid(0), size: 0 },
+ )?;
+ }
+ match ty.kind(Interner) {
+ TyKind::Adt(id, subst) => {
+ match id.0 {
+ AdtId::StructId(s) => {
+ let data = self.db.struct_data(s);
+ if data.flags.contains(StructFlags::IS_MANUALLY_DROP) {
+ return Ok(());
+ }
+ let layout = self.layout_adt(id.0, subst.clone())?;
+ match data.variant_data.as_ref() {
+ VariantData::Record(fields) | VariantData::Tuple(fields) => {
+ let field_types = self.db.field_types(s.into());
+ for (field, _) in fields.iter() {
+ let offset = layout
+ .fields
+ .offset(u32::from(field.into_raw()) as usize)
+ .bytes_usize();
+ let addr = addr.offset(offset);
+ let ty = field_types[field].clone().substitute(Interner, subst);
+ self.run_drop_glue_deep(ty, locals, addr, &[], span)?;
+ }
+ }
+ VariantData::Unit => (),
+ }
+ }
+ AdtId::UnionId(_) => (), // union fields don't need drop
+ AdtId::EnumId(_) => (),
+ }
+ }
+ TyKind::AssociatedType(_, _)
+ | TyKind::Scalar(_)
+ | TyKind::Tuple(_, _)
+ | TyKind::Array(_, _)
+ | TyKind::Slice(_)
+ | TyKind::Raw(_, _)
+ | TyKind::Ref(_, _, _)
+ | TyKind::OpaqueType(_, _)
+ | TyKind::FnDef(_, _)
+ | TyKind::Str
+ | TyKind::Never
+ | TyKind::Closure(_, _)
+ | TyKind::Generator(_, _)
+ | TyKind::GeneratorWitness(_, _)
+ | TyKind::Foreign(_)
+ | TyKind::Error
+ | TyKind::Placeholder(_)
+ | TyKind::Dyn(_)
+ | TyKind::Alias(_)
+ | TyKind::Function(_)
+ | TyKind::BoundVar(_)
+ | TyKind::InferenceVar(_, _) => (),
+ };
+ Ok(())
+ }
+
+ fn write_to_stdout(&mut self, interval: Interval) -> Result<()> {
+ self.stdout.extend(interval.get(self)?.to_vec());
+ Ok(())
+ }
+
+ fn write_to_stderr(&mut self, interval: Interval) -> Result<()> {
+ self.stderr.extend(interval.get(self)?.to_vec());
+ Ok(())
+ }
}
pub fn pad16(x: &[u8], is_signed: bool) -> [u8; 16] {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
new file mode 100644
index 000000000..3b9ef03c3
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
@@ -0,0 +1,792 @@
+//! Interpret intrinsics, lang items and `extern "C"` wellknown functions which their implementation
+//! is not available.
+
+use std::cmp;
+
+use super::*;
+
+macro_rules! from_bytes {
+ ($ty:tt, $value:expr) => {
+ ($ty::from_le_bytes(match ($value).try_into() {
+ Ok(x) => x,
+ Err(_) => return Err(MirEvalError::TypeError("mismatched size")),
+ }))
+ };
+}
+
+macro_rules! not_supported {
+ ($x: expr) => {
+ return Err(MirEvalError::NotSupported(format!($x)))
+ };
+}
+
+impl Evaluator<'_> {
+ pub(super) fn detect_and_exec_special_function(
+ &mut self,
+ def: FunctionId,
+ args: &[IntervalAndTy],
+ generic_args: &Substitution,
+ locals: &Locals<'_>,
+ destination: Interval,
+ span: MirSpan,
+ ) -> Result<bool> {
+ let function_data = self.db.function_data(def);
+ let is_intrinsic = match &function_data.abi {
+ Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
+ None => match def.lookup(self.db.upcast()).container {
+ hir_def::ItemContainerId::ExternBlockId(block) => {
+ let id = block.lookup(self.db.upcast()).id;
+ id.item_tree(self.db.upcast())[id.value].abi.as_deref()
+ == Some("rust-intrinsic")
+ }
+ _ => false,
+ },
+ };
+ if is_intrinsic {
+ self.exec_intrinsic(
+ function_data.name.as_text().unwrap_or_default().as_str(),
+ args,
+ generic_args,
+ destination,
+ &locals,
+ span,
+ )?;
+ return Ok(true);
+ }
+ let is_extern_c = match def.lookup(self.db.upcast()).container {
+ hir_def::ItemContainerId::ExternBlockId(block) => {
+ let id = block.lookup(self.db.upcast()).id;
+ id.item_tree(self.db.upcast())[id.value].abi.as_deref() == Some("C")
+ }
+ _ => false,
+ };
+ if is_extern_c {
+ self.exec_extern_c(
+ function_data.name.as_text().unwrap_or_default().as_str(),
+ args,
+ generic_args,
+ destination,
+ &locals,
+ span,
+ )?;
+ return Ok(true);
+ }
+ let alloc_fn = function_data
+ .attrs
+ .iter()
+ .filter_map(|x| x.path().as_ident())
+ .filter_map(|x| x.as_str())
+ .find(|x| {
+ [
+ "rustc_allocator",
+ "rustc_deallocator",
+ "rustc_reallocator",
+ "rustc_allocator_zeroed",
+ ]
+ .contains(x)
+ });
+ if let Some(alloc_fn) = alloc_fn {
+ self.exec_alloc_fn(alloc_fn, args, destination)?;
+ return Ok(true);
+ }
+ if let Some(x) = self.detect_lang_function(def) {
+ let arg_bytes =
+ args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::<Result<Vec<_>>>()?;
+ let result = self.exec_lang_item(x, generic_args, &arg_bytes, locals, span)?;
+ destination.write_from_bytes(self, &result)?;
+ return Ok(true);
+ }
+ Ok(false)
+ }
+
+ fn exec_alloc_fn(
+ &mut self,
+ alloc_fn: &str,
+ args: &[IntervalAndTy],
+ destination: Interval,
+ ) -> Result<()> {
+ match alloc_fn {
+ "rustc_allocator_zeroed" | "rustc_allocator" => {
+ let [size, align] = args else {
+ return Err(MirEvalError::TypeError("rustc_allocator args are not provided"));
+ };
+ let size = from_bytes!(usize, size.get(self)?);
+ let align = from_bytes!(usize, align.get(self)?);
+ let result = self.heap_allocate(size, align);
+ destination.write_from_bytes(self, &result.to_bytes())?;
+ }
+ "rustc_deallocator" => { /* no-op for now */ }
+ "rustc_reallocator" => {
+ let [ptr, old_size, align, new_size] = args else {
+ return Err(MirEvalError::TypeError("rustc_allocator args are not provided"));
+ };
+ let ptr = Address::from_bytes(ptr.get(self)?)?;
+ let old_size = from_bytes!(usize, old_size.get(self)?);
+ let new_size = from_bytes!(usize, new_size.get(self)?);
+ let align = from_bytes!(usize, align.get(self)?);
+ let result = self.heap_allocate(new_size, align);
+ Interval { addr: result, size: old_size }
+ .write_from_interval(self, Interval { addr: ptr, size: old_size })?;
+ destination.write_from_bytes(self, &result.to_bytes())?;
+ }
+ _ => not_supported!("unknown alloc function"),
+ }
+ Ok(())
+ }
+
+ fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
+ use LangItem::*;
+ let candidate = lang_attr(self.db.upcast(), def)?;
+ // We want to execute these functions with special logic
+ if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
+ return Some(candidate);
+ }
+ None
+ }
+
+ fn exec_lang_item(
+ &mut self,
+ x: LangItem,
+ generic_args: &Substitution,
+ args: &[Vec<u8>],
+ locals: &Locals<'_>,
+ span: MirSpan,
+ ) -> Result<Vec<u8>> {
+ use LangItem::*;
+ let mut args = args.iter();
+ match x {
+ BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_string())),
+ PanicFmt => {
+ let message = (|| {
+ let arguments_struct =
+ self.db.lang_item(self.crate_id, LangItem::FormatArguments)?.as_struct()?;
+ let arguments_layout = self
+ .layout_adt(arguments_struct.into(), Substitution::empty(Interner))
+ .ok()?;
+ let arguments_field_pieces =
+ self.db.struct_data(arguments_struct).variant_data.field(&name![pieces])?;
+ let pieces_offset = arguments_layout
+ .fields
+ .offset(u32::from(arguments_field_pieces.into_raw()) as usize)
+ .bytes_usize();
+ let ptr_size = self.ptr_size();
+ let arg = args.next()?;
+ let pieces_array_addr =
+ Address::from_bytes(&arg[pieces_offset..pieces_offset + ptr_size]).ok()?;
+ let pieces_array_len = usize::from_le_bytes(
+ (&arg[pieces_offset + ptr_size..pieces_offset + 2 * ptr_size])
+ .try_into()
+ .ok()?,
+ );
+ let mut message = "".to_string();
+ for i in 0..pieces_array_len {
+ let piece_ptr_addr = pieces_array_addr.offset(2 * i * ptr_size);
+ let piece_addr =
+ Address::from_bytes(self.read_memory(piece_ptr_addr, ptr_size).ok()?)
+ .ok()?;
+ let piece_len = usize::from_le_bytes(
+ self.read_memory(piece_ptr_addr.offset(ptr_size), ptr_size)
+ .ok()?
+ .try_into()
+ .ok()?,
+ );
+ let piece_data = self.read_memory(piece_addr, piece_len).ok()?;
+ message += &std::string::String::from_utf8_lossy(piece_data);
+ }
+ Some(message)
+ })()
+ .unwrap_or_else(|| "<format-args-evaluation-failed>".to_string());
+ Err(MirEvalError::Panic(message))
+ }
+ SliceLen => {
+ let arg = args
+ .next()
+ .ok_or(MirEvalError::TypeError("argument of <[T]>::len() is not provided"))?;
+ let ptr_size = arg.len() / 2;
+ Ok(arg[ptr_size..].into())
+ }
+ DropInPlace => {
+ let ty =
+ generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)).ok_or(
+ MirEvalError::TypeError(
+ "generic argument of drop_in_place is not provided",
+ ),
+ )?;
+ let arg = args
+ .next()
+ .ok_or(MirEvalError::TypeError("argument of drop_in_place is not provided"))?;
+ self.run_drop_glue_deep(
+ ty.clone(),
+ locals,
+ Address::from_bytes(&arg[0..self.ptr_size()])?,
+ &arg[self.ptr_size()..],
+ span,
+ )?;
+ Ok(vec![])
+ }
+ x => not_supported!("Executing lang item {x:?}"),
+ }
+ }
+
+ fn exec_extern_c(
+ &mut self,
+ as_str: &str,
+ args: &[IntervalAndTy],
+ _generic_args: &Substitution,
+ destination: Interval,
+ locals: &Locals<'_>,
+ _span: MirSpan,
+ ) -> Result<()> {
+ match as_str {
+ "memcmp" => {
+ let [ptr1, ptr2, size] = args else {
+ return Err(MirEvalError::TypeError("memcmp args are not provided"));
+ };
+ let addr1 = Address::from_bytes(ptr1.get(self)?)?;
+ let addr2 = Address::from_bytes(ptr2.get(self)?)?;
+ let size = from_bytes!(usize, size.get(self)?);
+ let slice1 = self.read_memory(addr1, size)?;
+ let slice2 = self.read_memory(addr2, size)?;
+ let r: i128 = match slice1.cmp(slice2) {
+ cmp::Ordering::Less => -1,
+ cmp::Ordering::Equal => 0,
+ cmp::Ordering::Greater => 1,
+ };
+ destination.write_from_bytes(self, &r.to_le_bytes()[..destination.size])
+ }
+ "write" => {
+ let [fd, ptr, len] = args else {
+ return Err(MirEvalError::TypeError("libc::write args are not provided"));
+ };
+ let fd = u128::from_le_bytes(pad16(fd.get(self)?, false));
+ let interval = Interval {
+ addr: Address::from_bytes(ptr.get(self)?)?,
+ size: from_bytes!(usize, len.get(self)?),
+ };
+ match fd {
+ 1 => {
+ self.write_to_stdout(interval)?;
+ }
+ 2 => {
+ self.write_to_stderr(interval)?;
+ }
+ _ => not_supported!("write to arbitrary file descriptor"),
+ }
+ destination.write_from_interval(self, len.interval)?;
+ Ok(())
+ }
+ "pthread_key_create" => {
+ let key = self.thread_local_storage.create_key();
+ let Some(arg0) = args.get(0) else {
+ return Err(MirEvalError::TypeError("pthread_key_create arg0 is not provided"));
+ };
+ let arg0_addr = Address::from_bytes(arg0.get(self)?)?;
+ let key_ty = if let Some((ty, ..)) = arg0.ty.as_reference_or_ptr() {
+ ty
+ } else {
+ return Err(MirEvalError::TypeError(
+ "pthread_key_create arg0 is not a pointer",
+ ));
+ };
+ let arg0_interval = Interval::new(
+ arg0_addr,
+ self.size_of_sized(key_ty, locals, "pthread_key_create key arg")?,
+ );
+ arg0_interval.write_from_bytes(self, &key.to_le_bytes()[0..arg0_interval.size])?;
+ // return 0 as success
+ destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
+ Ok(())
+ }
+ "pthread_getspecific" => {
+ let Some(arg0) = args.get(0) else {
+ return Err(MirEvalError::TypeError("pthread_getspecific arg0 is not provided"));
+ };
+ let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]);
+ let value = self.thread_local_storage.get_key(key)?;
+ destination.write_from_bytes(self, &value.to_le_bytes()[0..destination.size])?;
+ Ok(())
+ }
+ "pthread_setspecific" => {
+ let Some(arg0) = args.get(0) else {
+ return Err(MirEvalError::TypeError("pthread_setspecific arg0 is not provided"));
+ };
+ let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]);
+ let Some(arg1) = args.get(1) else {
+ return Err(MirEvalError::TypeError("pthread_setspecific arg1 is not provided"));
+ };
+ let value = from_bytes!(u128, pad16(arg1.get(self)?, false));
+ self.thread_local_storage.set_key(key, value)?;
+ // return 0 as success
+ destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
+ Ok(())
+ }
+ "pthread_key_delete" => {
+ // we ignore this currently
+ // return 0 as success
+ destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
+ Ok(())
+ }
+ _ => not_supported!("unknown external function {as_str}"),
+ }
+ }
+
+ fn exec_intrinsic(
+ &mut self,
+ name: &str,
+ args: &[IntervalAndTy],
+ generic_args: &Substitution,
+ destination: Interval,
+ locals: &Locals<'_>,
+ span: MirSpan,
+ ) -> Result<()> {
+ if let Some(name) = name.strip_prefix("atomic_") {
+ return self.exec_atomic_intrinsic(name, args, generic_args, destination, locals, span);
+ }
+ if let Some(name) = name.strip_suffix("f64") {
+ let result = match name {
+ "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs"
+ | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64) -> f64"));
+ };
+ let arg = from_bytes!(f64, arg.get(self)?);
+ match name {
+ "sqrt" => arg.sqrt(),
+ "sin" => arg.sin(),
+ "cos" => arg.cos(),
+ "exp" => arg.exp(),
+ "exp2" => arg.exp2(),
+ "log" => arg.ln(),
+ "log10" => arg.log10(),
+ "log2" => arg.log2(),
+ "fabs" => arg.abs(),
+ "floor" => arg.floor(),
+ "ceil" => arg.ceil(),
+ "trunc" => arg.trunc(),
+ // FIXME: these rounds should be different, but only `.round()` is stable now.
+ "rint" => arg.round(),
+ "nearbyint" => arg.round(),
+ "round" => arg.round(),
+ "roundeven" => arg.round(),
+ _ => unreachable!(),
+ }
+ }
+ "pow" | "minnum" | "maxnum" | "copysign" => {
+ let [arg1, arg2] = args else {
+ return Err(MirEvalError::TypeError("f64 intrinsic signature doesn't match fn (f64, f64) -> f64"));
+ };
+ let arg1 = from_bytes!(f64, arg1.get(self)?);
+ let arg2 = from_bytes!(f64, arg2.get(self)?);
+ match name {
+ "pow" => arg1.powf(arg2),
+ "minnum" => arg1.min(arg2),
+ "maxnum" => arg1.max(arg2),
+ "copysign" => arg1.copysign(arg2),
+ _ => unreachable!(),
+ }
+ }
+ "powi" => {
+ let [arg1, arg2] = args else {
+ return Err(MirEvalError::TypeError("powif64 signature doesn't match fn (f64, i32) -> f64"));
+ };
+ let arg1 = from_bytes!(f64, arg1.get(self)?);
+ let arg2 = from_bytes!(i32, arg2.get(self)?);
+ arg1.powi(arg2)
+ }
+ "fma" => {
+ let [arg1, arg2, arg3] = args else {
+ return Err(MirEvalError::TypeError("fmaf64 signature doesn't match fn (f64, f64, f64) -> f64"));
+ };
+ let arg1 = from_bytes!(f64, arg1.get(self)?);
+ let arg2 = from_bytes!(f64, arg2.get(self)?);
+ let arg3 = from_bytes!(f64, arg3.get(self)?);
+ arg1.mul_add(arg2, arg3)
+ }
+ _ => not_supported!("unknown f64 intrinsic {name}"),
+ };
+ return destination.write_from_bytes(self, &result.to_le_bytes());
+ }
+ if let Some(name) = name.strip_suffix("f32") {
+ let result = match name {
+ "sqrt" | "sin" | "cos" | "exp" | "exp2" | "log" | "log10" | "log2" | "fabs"
+ | "floor" | "ceil" | "trunc" | "rint" | "nearbyint" | "round" | "roundeven" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32) -> f32"));
+ };
+ let arg = from_bytes!(f32, arg.get(self)?);
+ match name {
+ "sqrt" => arg.sqrt(),
+ "sin" => arg.sin(),
+ "cos" => arg.cos(),
+ "exp" => arg.exp(),
+ "exp2" => arg.exp2(),
+ "log" => arg.ln(),
+ "log10" => arg.log10(),
+ "log2" => arg.log2(),
+ "fabs" => arg.abs(),
+ "floor" => arg.floor(),
+ "ceil" => arg.ceil(),
+ "trunc" => arg.trunc(),
+ // FIXME: these rounds should be different, but only `.round()` is stable now.
+ "rint" => arg.round(),
+ "nearbyint" => arg.round(),
+ "round" => arg.round(),
+ "roundeven" => arg.round(),
+ _ => unreachable!(),
+ }
+ }
+ "pow" | "minnum" | "maxnum" | "copysign" => {
+ let [arg1, arg2] = args else {
+ return Err(MirEvalError::TypeError("f32 intrinsic signature doesn't match fn (f32, f32) -> f32"));
+ };
+ let arg1 = from_bytes!(f32, arg1.get(self)?);
+ let arg2 = from_bytes!(f32, arg2.get(self)?);
+ match name {
+ "pow" => arg1.powf(arg2),
+ "minnum" => arg1.min(arg2),
+ "maxnum" => arg1.max(arg2),
+ "copysign" => arg1.copysign(arg2),
+ _ => unreachable!(),
+ }
+ }
+ "powi" => {
+ let [arg1, arg2] = args else {
+ return Err(MirEvalError::TypeError("powif32 signature doesn't match fn (f32, i32) -> f32"));
+ };
+ let arg1 = from_bytes!(f32, arg1.get(self)?);
+ let arg2 = from_bytes!(i32, arg2.get(self)?);
+ arg1.powi(arg2)
+ }
+ "fma" => {
+ let [arg1, arg2, arg3] = args else {
+ return Err(MirEvalError::TypeError("fmaf32 signature doesn't match fn (f32, f32, f32) -> f32"));
+ };
+ let arg1 = from_bytes!(f32, arg1.get(self)?);
+ let arg2 = from_bytes!(f32, arg2.get(self)?);
+ let arg3 = from_bytes!(f32, arg3.get(self)?);
+ arg1.mul_add(arg2, arg3)
+ }
+ _ => not_supported!("unknown f32 intrinsic {name}"),
+ };
+ return destination.write_from_bytes(self, &result.to_le_bytes());
+ }
+ match name {
+ "size_of" => {
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
+ };
+ let size = self.size_of_sized(ty, locals, "size_of arg")?;
+ destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size])
+ }
+ "min_align_of" | "pref_align_of" => {
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("align_of generic arg is not provided"));
+ };
+ let align = self.layout(ty)?.align.abi.bytes();
+ destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size])
+ }
+ "needs_drop" => {
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
+ };
+ let result = !ty.clone().is_copy(self.db, locals.body.owner);
+ destination.write_from_bytes(self, &[u8::from(result)])
+ }
+ "ptr_guaranteed_cmp" => {
+ // FIXME: this is wrong for const eval, it should return 2 in some
+ // cases.
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("wrapping_add args are not provided"));
+ };
+ let ans = lhs.get(self)? == rhs.get(self)?;
+ destination.write_from_bytes(self, &[u8::from(ans)])
+ }
+ "saturating_add" => {
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("saturating_add args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.saturating_add(rhs);
+ let bits = destination.size * 8;
+ // FIXME: signed
+ let is_signed = false;
+ let mx: u128 = if is_signed { (1 << (bits - 1)) - 1 } else { (1 << bits) - 1 };
+ // FIXME: signed
+ let mn: u128 = 0;
+ let ans = cmp::min(mx, cmp::max(mn, ans));
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "wrapping_add" | "unchecked_add" => {
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("wrapping_add args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.wrapping_add(rhs);
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "wrapping_sub" | "unchecked_sub" | "ptr_offset_from_unsigned" | "ptr_offset_from" => {
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("wrapping_sub args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.wrapping_sub(rhs);
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "wrapping_mul" | "unchecked_mul" => {
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("wrapping_mul args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.wrapping_mul(rhs);
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "unchecked_rem" => {
+ // FIXME: signed
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("unchecked_rem args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.checked_rem(rhs).ok_or_else(|| {
+ MirEvalError::UndefinedBehavior("unchecked_rem with bad inputs".to_owned())
+ })?;
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "unchecked_div" | "exact_div" => {
+ // FIXME: signed
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("unchecked_div args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.checked_div(rhs).ok_or_else(|| {
+ MirEvalError::UndefinedBehavior("unchecked_rem with bad inputs".to_owned())
+ })?;
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => {
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
+ };
+ let result_ty = TyKind::Tuple(
+ 2,
+ Substitution::from_iter(Interner, [lhs.ty.clone(), TyBuilder::bool()]),
+ )
+ .intern(Interner);
+ let op_size =
+ self.size_of_sized(&lhs.ty, locals, "operand of add_with_overflow")?;
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let (ans, u128overflow) = match name {
+ "add_with_overflow" => lhs.overflowing_add(rhs),
+ "sub_with_overflow" => lhs.overflowing_sub(rhs),
+ "mul_with_overflow" => lhs.overflowing_mul(rhs),
+ _ => unreachable!(),
+ };
+ let is_overflow = u128overflow
+ || ans.to_le_bytes()[op_size..].iter().any(|&x| x != 0 && x != 255);
+ let is_overflow = vec![u8::from(is_overflow)];
+ let layout = self.layout(&result_ty)?;
+ let result = self.make_by_layout(
+ layout.size.bytes_usize(),
+ &layout,
+ None,
+ [ans.to_le_bytes()[0..op_size].to_vec(), is_overflow]
+ .into_iter()
+ .map(IntervalOrOwned::Owned),
+ )?;
+ destination.write_from_bytes(self, &result)
+ }
+ "copy" | "copy_nonoverlapping" => {
+ let [src, dst, offset] = args else {
+ return Err(MirEvalError::TypeError("copy_nonoverlapping args are not provided"));
+ };
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("copy_nonoverlapping generic arg is not provided"));
+ };
+ let src = Address::from_bytes(src.get(self)?)?;
+ let dst = Address::from_bytes(dst.get(self)?)?;
+ let offset = from_bytes!(usize, offset.get(self)?);
+ let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?;
+ let size = offset * size;
+ let src = Interval { addr: src, size };
+ let dst = Interval { addr: dst, size };
+ dst.write_from_interval(self, src)
+ }
+ "offset" | "arith_offset" => {
+ let [ptr, offset] = args else {
+ return Err(MirEvalError::TypeError("offset args are not provided"));
+ };
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("offset generic arg is not provided"));
+ };
+ let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false));
+ let offset = u128::from_le_bytes(pad16(offset.get(self)?, false));
+ let size = self.size_of_sized(ty, locals, "offset ptr type")? as u128;
+ let ans = ptr + offset * size;
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "assert_inhabited" | "assert_zero_valid" | "assert_uninit_valid" | "assume" => {
+ // FIXME: We should actually implement these checks
+ Ok(())
+ }
+ "forget" => {
+ // We don't call any drop glue yet, so there is nothing here
+ Ok(())
+ }
+ "transmute" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("trasmute arg is not provided"));
+ };
+ destination.write_from_interval(self, arg.interval)
+ }
+ "likely" | "unlikely" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("likely arg is not provided"));
+ };
+ destination.write_from_interval(self, arg.interval)
+ }
+ "ctpop" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("likely arg is not provided"));
+ };
+ let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).count_ones();
+ destination
+ .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
+ }
+ "cttz" | "cttz_nonzero" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("likely arg is not provided"));
+ };
+ let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).trailing_zeros();
+ destination
+ .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
+ }
+ "const_eval_select" => {
+ let [tuple, const_fn, _] = args else {
+ return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
+ };
+ let mut args = vec![const_fn.clone()];
+ let TyKind::Tuple(_, fields) = tuple.ty.kind(Interner) else {
+ return Err(MirEvalError::TypeError("const_eval_select arg[0] is not a tuple"));
+ };
+ let layout = self.layout(&tuple.ty)?;
+ for (i, field) in fields.iter(Interner).enumerate() {
+ let field = field.assert_ty_ref(Interner).clone();
+ let offset = layout.fields.offset(i).bytes_usize();
+ let addr = tuple.interval.addr.offset(offset);
+ args.push(IntervalAndTy::new(addr, field, self, locals)?);
+ }
+ self.exec_fn_trait(&args, destination, locals, span)
+ }
+ _ => not_supported!("unknown intrinsic {name}"),
+ }
+ }
+
+ fn exec_atomic_intrinsic(
+ &mut self,
+ name: &str,
+ args: &[IntervalAndTy],
+ generic_args: &Substitution,
+ destination: Interval,
+ locals: &Locals<'_>,
+ _span: MirSpan,
+ ) -> Result<()> {
+ // We are a single threaded runtime with no UB checking and no optimization, so
+ // we can implement these as normal functions.
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)) else {
+ return Err(MirEvalError::TypeError("atomic intrinsic generic arg is not provided"));
+ };
+ let Some(arg0) = args.get(0) else {
+ return Err(MirEvalError::TypeError("atomic intrinsic arg0 is not provided"));
+ };
+ let arg0_addr = Address::from_bytes(arg0.get(self)?)?;
+ let arg0_interval =
+ Interval::new(arg0_addr, self.size_of_sized(ty, locals, "atomic intrinsic type arg")?);
+ if name.starts_with("load_") {
+ return destination.write_from_interval(self, arg0_interval);
+ }
+ let Some(arg1) = args.get(1) else {
+ return Err(MirEvalError::TypeError("atomic intrinsic arg1 is not provided"));
+ };
+ if name.starts_with("store_") {
+ return arg0_interval.write_from_interval(self, arg1.interval);
+ }
+ if name.starts_with("xchg_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ return arg0_interval.write_from_interval(self, arg1.interval);
+ }
+ if name.starts_with("xadd_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
+ let ans = lhs.wrapping_add(rhs);
+ return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
+ }
+ if name.starts_with("xsub_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
+ let ans = lhs.wrapping_sub(rhs);
+ return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
+ }
+ if name.starts_with("and_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
+ let ans = lhs & rhs;
+ return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
+ }
+ if name.starts_with("or_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
+ let ans = lhs | rhs;
+ return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
+ }
+ if name.starts_with("xor_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
+ let ans = lhs ^ rhs;
+ return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
+ }
+ if name.starts_with("nand_") {
+ destination.write_from_interval(self, arg0_interval)?;
+ let lhs = u128::from_le_bytes(pad16(arg0_interval.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(arg1.get(self)?, false));
+ let ans = !(lhs & rhs);
+ return arg0_interval.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]);
+ }
+ let Some(arg2) = args.get(2) else {
+ return Err(MirEvalError::TypeError("atomic intrinsic arg2 is not provided"));
+ };
+ if name.starts_with("cxchg_") || name.starts_with("cxchgweak_") {
+ let dest = if arg1.get(self)? == arg0_interval.get(self)? {
+ arg0_interval.write_from_interval(self, arg2.interval)?;
+ (arg1.interval, true)
+ } else {
+ (arg0_interval, false)
+ };
+ let result_ty = TyKind::Tuple(
+ 2,
+ Substitution::from_iter(Interner, [ty.clone(), TyBuilder::bool()]),
+ )
+ .intern(Interner);
+ let layout = self.layout(&result_ty)?;
+ let result = self.make_by_layout(
+ layout.size.bytes_usize(),
+ &layout,
+ None,
+ [IntervalOrOwned::Borrowed(dest.0), IntervalOrOwned::Owned(vec![u8::from(dest.1)])]
+ .into_iter(),
+ )?;
+ return destination.write_from_bytes(self, &result);
+ }
+ not_supported!("unknown atomic intrinsic {name}");
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
new file mode 100644
index 000000000..ca4268b8f
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
@@ -0,0 +1,676 @@
+use base_db::{fixture::WithFixture, FileId};
+use hir_def::db::DefDatabase;
+use syntax::{TextRange, TextSize};
+
+use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution};
+
+use super::{interpret_mir, MirEvalError};
+
+fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalError> {
+ let module_id = db.module_for_file(file_id);
+ let def_map = module_id.def_map(db);
+ let scope = &def_map[module_id.local_id].scope;
+ let func_id = scope
+ .declarations()
+ .find_map(|x| match x {
+ hir_def::ModuleDefId::FunctionId(x) => {
+ if db.function_data(x).name.display(db).to_string() == "main" {
+ Some(x)
+ } else {
+ None
+ }
+ }
+ _ => None,
+ })
+ .expect("no main function found");
+ let body = db
+ .monomorphized_mir_body(
+ func_id.into(),
+ Substitution::empty(Interner),
+ db.trait_environment(func_id.into()),
+ )
+ .map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?;
+ let (result, stdout, stderr) = interpret_mir(db, &body, false);
+ result?;
+ Ok((stdout, stderr))
+}
+
+fn check_pass(ra_fixture: &str) {
+ check_pass_and_stdio(ra_fixture, "", "");
+}
+
+fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr: &str) {
+ let (db, file_ids) = TestDB::with_many_files(ra_fixture);
+ let file_id = *file_ids.last().unwrap();
+ let x = eval_main(&db, file_id);
+ match x {
+ Err(e) => {
+ let mut err = String::new();
+ let line_index = |size: TextSize| {
+ let mut size = u32::from(size) as usize;
+ let mut lines = ra_fixture.lines().enumerate();
+ while let Some((i, l)) = lines.next() {
+ if let Some(x) = size.checked_sub(l.len()) {
+ size = x;
+ } else {
+ return (i, size);
+ }
+ }
+ (usize::MAX, size)
+ };
+ let span_formatter = |file, range: TextRange| {
+ format!("{:?} {:?}..{:?}", file, line_index(range.start()), line_index(range.end()))
+ };
+ e.pretty_print(&mut err, &db, span_formatter).unwrap();
+ panic!("Error in interpreting: {err}");
+ }
+ Ok((stdout, stderr)) => {
+ assert_eq!(stdout, expected_stdout);
+ assert_eq!(stderr, expected_stderr);
+ }
+ }
+}
+
+#[test]
+fn function_with_extern_c_abi() {
+ check_pass(
+ r#"
+extern "C" fn foo(a: i32, b: i32) -> i32 {
+ a + b
+}
+
+fn main() {
+ let x = foo(2, 3);
+}
+ "#,
+ );
+}
+
+#[test]
+fn drop_basic() {
+ check_pass(
+ r#"
+//- minicore: drop, add
+
+struct X<'a>(&'a mut i32);
+impl<'a> Drop for X<'a> {
+ fn drop(&mut self) {
+ *self.0 += 1;
+ }
+}
+
+struct NestedX<'a> { f1: X<'a>, f2: X<'a> }
+
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn my_drop2(x: X<'_>) {
+ return;
+}
+
+fn my_drop(x: X<'_>) {
+ drop(x);
+}
+
+fn main() {
+ let mut s = 10;
+ let mut x = X(&mut s);
+ my_drop(x);
+ x = X(&mut s);
+ my_drop2(x);
+ X(&mut s); // dropped immediately
+ let x = X(&mut s);
+ NestedX { f1: x, f2: X(&mut s) };
+ if s != 15 {
+ should_not_reach();
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn drop_if_let() {
+ check_pass(
+ r#"
+//- minicore: drop, add, option, cell, builtin_impls
+
+use core::cell::Cell;
+
+struct X<'a>(&'a Cell<i32>);
+impl<'a> Drop for X<'a> {
+ fn drop(&mut self) {
+ self.0.set(self.0.get() + 1)
+ }
+}
+
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+#[test]
+fn main() {
+ let s = Cell::new(0);
+ let x = Some(X(&s));
+ if let Some(y) = x {
+ if s.get() != 0 {
+ should_not_reach();
+ }
+ if s.get() != 0 {
+ should_not_reach();
+ }
+ } else {
+ should_not_reach();
+ }
+ if s.get() != 1 {
+ should_not_reach();
+ }
+ let x = Some(X(&s));
+ if let None = x {
+ should_not_reach();
+ } else {
+ if s.get() != 1 {
+ should_not_reach();
+ }
+ }
+ if s.get() != 1 {
+ should_not_reach();
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn drop_in_place() {
+ check_pass(
+ r#"
+//- minicore: drop, add, coerce_unsized
+use core::ptr::drop_in_place;
+
+struct X<'a>(&'a mut i32);
+impl<'a> Drop for X<'a> {
+ fn drop(&mut self) {
+ *self.0 += 1;
+ }
+}
+
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+ let mut s = 2;
+ let x = X(&mut s);
+ drop_in_place(&mut x);
+ drop(x);
+ if s != 4 {
+ should_not_reach();
+ }
+ let p: &mut [X] = &mut [X(&mut 2)];
+ drop_in_place(p);
+}
+ "#,
+ );
+}
+
+#[test]
+fn manually_drop() {
+ check_pass(
+ r#"
+//- minicore: manually_drop
+use core::mem::ManuallyDrop;
+
+struct X;
+impl Drop for X {
+ fn drop(&mut self) {
+ should_not_reach();
+ }
+}
+
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+ let x = ManuallyDrop::new(X);
+}
+ "#,
+ );
+}
+
+#[test]
+fn generic_impl_for_trait_with_generic_method() {
+ check_pass(
+ r#"
+//- minicore: drop
+struct S<T>(T);
+
+trait Tr {
+ fn f<F>(&self, x: F);
+}
+
+impl<T> Tr for S<T> {
+ fn f<F>(&self, x: F) {
+ }
+}
+
+fn main() {
+ let s = S(1u8);
+ s.f(5i64);
+}
+ "#,
+ );
+}
+
+#[test]
+fn index_of_slice_should_preserve_len() {
+ check_pass(
+ r#"
+//- minicore: index, slice, coerce_unsized
+
+struct X;
+
+impl core::ops::Index<X> for [i32] {
+ type Output = i32;
+
+ fn index(&self, _: X) -> &i32 {
+ if self.len() != 3 {
+ should_not_reach();
+ }
+ &self[0]
+ }
+}
+
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+ let x: &[i32] = &[1, 2, 3];
+ &x[X];
+}
+ "#,
+ );
+}
+
+#[test]
+fn memcmp() {
+ check_pass(
+ r#"
+//- minicore: slice, coerce_unsized, index
+
+fn should_not_reach() -> bool {
+ _ // FIXME: replace this function with panic when that works
+}
+
+extern "C" {
+ fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32;
+}
+
+fn my_cmp(x: &[u8], y: &[u8]) -> i32 {
+ memcmp(x as *const u8, y as *const u8, x.len())
+}
+
+fn main() {
+ if my_cmp(&[1, 2, 3], &[1, 2, 3]) != 0 {
+ should_not_reach();
+ }
+ if my_cmp(&[1, 20, 3], &[1, 2, 3]) <= 0 {
+ should_not_reach();
+ }
+ if my_cmp(&[1, 2, 3], &[1, 20, 3]) >= 0 {
+ should_not_reach();
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn unix_write_stdout() {
+ check_pass_and_stdio(
+ r#"
+//- minicore: slice, index, coerce_unsized
+
+type pthread_key_t = u32;
+type c_void = u8;
+type c_int = i32;
+
+extern "C" {
+ pub fn write(fd: i32, buf: *const u8, count: usize) -> usize;
+}
+
+fn main() {
+ let stdout = b"stdout";
+ let stderr = b"stderr";
+ write(1, &stdout[0], 6);
+ write(2, &stderr[0], 6);
+}
+ "#,
+ "stdout",
+ "stderr",
+ );
+}
+
+#[test]
+fn closure_layout_in_rpit() {
+ check_pass(
+ r#"
+//- minicore: fn
+
+fn f<F: Fn()>(x: F) {
+ fn g(x: impl Fn()) -> impl FnOnce() {
+ move || {
+ x();
+ }
+ }
+ g(x)();
+}
+
+fn main() {
+ f(|| {});
+}
+ "#,
+ );
+}
+
+#[test]
+fn from_fn() {
+ check_pass(
+ r#"
+//- minicore: fn, iterator
+struct FromFn<F>(F);
+
+impl<T, F: FnMut() -> Option<T>> Iterator for FromFn<F> {
+ type Item = T;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ (self.0)()
+ }
+}
+
+fn main() {
+ let mut tokenize = {
+ FromFn(move || Some(2))
+ };
+ let s = tokenize.next();
+}
+ "#,
+ );
+}
+
+#[test]
+fn for_loop() {
+ check_pass(
+ r#"
+//- minicore: iterator, add
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+struct X;
+struct XIter(i32);
+
+impl IntoIterator for X {
+ type Item = i32;
+
+ type IntoIter = XIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ XIter(0)
+ }
+}
+
+impl Iterator for XIter {
+ type Item = i32;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.0 == 5 {
+ None
+ } else {
+ self.0 += 1;
+ Some(self.0)
+ }
+ }
+}
+
+fn main() {
+ let mut s = 0;
+ for x in X {
+ s += x;
+ }
+ if s != 15 {
+ should_not_reach();
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn field_with_associated_type() {
+ check_pass(
+ r#"
+//- /b/mod.rs crate:b
+pub trait Tr {
+ fn f(self);
+}
+
+pub trait Tr2 {
+ type Ty: Tr;
+}
+
+pub struct S<T: Tr2> {
+ pub t: T::Ty,
+}
+
+impl<T: Tr2> S<T> {
+ pub fn g(&self) {
+ let k = (self.t, self.t);
+ self.t.f();
+ }
+}
+
+//- /a/mod.rs crate:a deps:b
+use b::{Tr, Tr2, S};
+
+struct A(i32);
+struct B(u8);
+
+impl Tr for A {
+ fn f(&self) {
+ }
+}
+
+impl Tr2 for B {
+ type Ty = A;
+}
+
+#[test]
+fn main() {
+ let s: S<B> = S { t: A(2) };
+ s.g();
+}
+ "#,
+ );
+}
+
+#[test]
+fn specialization_array_clone() {
+ check_pass(
+ r#"
+//- minicore: copy, derive, slice, index, coerce_unsized
+impl<T: Clone, const N: usize> Clone for [T; N] {
+ #[inline]
+ fn clone(&self) -> Self {
+ SpecArrayClone::clone(self)
+ }
+}
+
+trait SpecArrayClone: Clone {
+ fn clone<const N: usize>(array: &[Self; N]) -> [Self; N];
+}
+
+impl<T: Clone> SpecArrayClone for T {
+ #[inline]
+ default fn clone<const N: usize>(array: &[T; N]) -> [T; N] {
+ // FIXME: panic here when we actually implement specialization.
+ from_slice(array)
+ }
+}
+
+fn from_slice<T, const N: usize>(s: &[T]) -> [T; N] {
+ [s[0]; N]
+}
+
+impl<T: Copy> SpecArrayClone for T {
+ #[inline]
+ fn clone<const N: usize>(array: &[T; N]) -> [T; N] {
+ *array
+ }
+}
+
+#[derive(Clone, Copy)]
+struct X(i32);
+
+fn main() {
+ let ar = [X(1), X(2)];
+ ar.clone();
+}
+ "#,
+ );
+}
+
+#[test]
+fn short_circuit_operator() {
+ check_pass(
+ r#"
+fn should_not_reach() -> bool {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+ if false && should_not_reach() {
+ should_not_reach();
+ }
+ true || should_not_reach();
+
+}
+ "#,
+ );
+}
+
+#[test]
+fn closure_state() {
+ check_pass(
+ r#"
+//- minicore: fn, add, copy
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+ let mut x = 2;
+ let mut c = move || {
+ x += 1;
+ x
+ };
+ c();
+ c();
+ c();
+ if x != 2 {
+ should_not_reach();
+ }
+ if c() != 6 {
+ should_not_reach();
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn closure_capture_array_const_generic() {
+ check_pass(
+ r#"
+//- minicore: fn, add, copy
+struct X(i32);
+
+fn f<const N: usize>(mut x: [X; N]) { // -> impl FnOnce() {
+ let c = || {
+ x;
+ };
+ c();
+}
+
+fn main() {
+ let s = f([X(1)]);
+ //s();
+}
+ "#,
+ );
+}
+
+#[test]
+fn posix_tls() {
+ check_pass(
+ r#"
+//- minicore: option
+
+type pthread_key_t = u32;
+type c_void = u8;
+type c_int = i32;
+
+extern "C" {
+ pub fn pthread_key_create(
+ key: *mut pthread_key_t,
+ dtor: Option<unsafe extern "C" fn(*mut c_void)>,
+ ) -> c_int;
+ pub fn pthread_key_delete(key: pthread_key_t) -> c_int;
+ pub fn pthread_getspecific(key: pthread_key_t) -> *mut c_void;
+ pub fn pthread_setspecific(key: pthread_key_t, value: *const c_void) -> c_int;
+}
+
+fn main() {
+ let mut key = 2;
+ pthread_key_create(&mut key, None);
+}
+ "#,
+ );
+}
+
+#[test]
+fn regression_14966() {
+ check_pass(
+ r#"
+//- minicore: fn, copy, coerce_unsized
+trait A<T> {
+ fn a(&self) {}
+}
+impl A<()> for () {}
+
+struct B;
+impl B {
+ pub fn b<T>(s: &dyn A<T>) -> Self {
+ B
+ }
+}
+struct C;
+impl C {
+ fn c<T>(a: &dyn A<T>) -> Self {
+ let mut c = C;
+ let b = B::b(a);
+ c.d(|| a.a());
+ c
+ }
+ fn d(&mut self, f: impl FnOnce()) {}
+}
+
+fn main() {
+ C::c(&());
+}
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index c4dd7c0ac..2cb29b4ab 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -1,60 +1,88 @@
//! This module generates a polymorphic MIR from a hir body
-use std::{iter, mem, sync::Arc};
+use std::{fmt::Write, iter, mem};
+use base_db::FileId;
use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind};
use hir_def::{
body::Body,
- expr::{
- Array, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm, Pat, PatId,
- RecordLitField,
+ data::adt::{StructKind, VariantData},
+ hir::{
+ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal,
+ LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField,
},
lang_item::{LangItem, LangItemTarget},
- layout::LayoutError,
path::Path,
- resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
- DefWithBodyId, EnumVariantId, HasModule,
+ resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs},
+ AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId,
+ TraitId, TypeOrConstParamId,
};
use hir_expand::name::Name;
use la_arena::ArenaMap;
+use rustc_hash::FxHashMap;
+use syntax::TextRange;
+use triomphe::Arc;
use crate::{
- consteval::ConstEvalError, db::HirDatabase, display::HirDisplay, infer::TypeMismatch,
- inhabitedness::is_ty_uninhabited_from, layout::layout_of_ty, mapping::ToChalk, static_lifetime,
- utils::generics, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
+ consteval::ConstEvalError,
+ db::HirDatabase,
+ display::HirDisplay,
+ infer::{CaptureKind, CapturedItem, TypeMismatch},
+ inhabitedness::is_ty_uninhabited_from,
+ layout::LayoutError,
+ mapping::ToChalk,
+ static_lifetime,
+ traits::FnTrait,
+ utils::{generics, ClosureSubst},
+ Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
};
use super::*;
mod as_place;
+mod pattern_matching;
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
struct LoopBlocks {
begin: BasicBlockId,
/// `None` for loops that are not terminating
end: Option<BasicBlockId>,
+ place: Place,
+ drop_scope_index: usize,
+}
+
+#[derive(Debug, Clone, Default)]
+struct DropScope {
+ /// locals, in order of definition (so we should run drop glues in reverse order)
+ locals: Vec<LocalId>,
}
struct MirLowerCtx<'a> {
result: MirBody,
owner: DefWithBodyId,
current_loop_blocks: Option<LoopBlocks>,
+ labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>,
discr_temp: Option<Place>,
db: &'a dyn HirDatabase,
body: &'a Body,
infer: &'a InferenceResult,
+ drop_scopes: Vec<DropScope>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MirLowerError {
- ConstEvalError(Box<ConstEvalError>),
+ ConstEvalError(String, Box<ConstEvalError>),
LayoutError(LayoutError),
IncompleteExpr,
+ IncompletePattern,
+ /// Trying to lower a trait function, instead of an implementation
+ TraitFunctionDefinition(TraitId, Name),
UnresolvedName(String),
RecordLiteralWithoutPath,
- UnresolvedMethod,
+ UnresolvedMethod(String),
UnresolvedField,
- MissingFunctionDefinition,
+ UnsizedTemporary(Ty),
+ MissingFunctionDefinition(DefWithBodyId, ExprId),
TypeMismatch(TypeMismatch),
/// This should be never happen. Type mismatch should catch everything.
TypeError(&'static str),
@@ -63,9 +91,114 @@ pub enum MirLowerError {
BreakWithoutLoop,
Loop,
/// Something that should never happen and is definitely a bug, but we don't want to panic if it happened
- ImplementationError(&'static str),
+ ImplementationError(String),
LangItemNotFound(LangItem),
MutatingRvalue,
+ UnresolvedLabel,
+ UnresolvedUpvar(Place),
+ UnaccessableLocal,
+
+ // monomorphization errors:
+ GenericArgNotProvided(TypeOrConstParamId, Substitution),
+}
+
+/// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves.
+struct DropScopeToken;
+impl DropScopeToken {
+ fn pop_and_drop(self, ctx: &mut MirLowerCtx<'_>, current: BasicBlockId) -> BasicBlockId {
+ std::mem::forget(self);
+ ctx.pop_drop_scope_internal(current)
+ }
+
+ /// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop
+ /// code. Either when the control flow is diverging (so drop code doesn't reached) or when drop is handled
+ /// for us (for example a block that ended with a return statement. Return will drop everything, so the block shouldn't
+ /// do anything)
+ fn pop_assume_dropped(self, ctx: &mut MirLowerCtx<'_>) {
+ std::mem::forget(self);
+ ctx.pop_drop_scope_assume_dropped_internal();
+ }
+}
+
+// Uncomment this to make `DropScopeToken` a drop bomb. Unfortunately we can't do this in release, since
+// in cases that mir lowering fails, we don't handle (and don't need to handle) drop scopes so it will be
+// actually reached. `pop_drop_scope_assert_finished` will also detect this case, but doesn't show useful
+// stack trace.
+//
+// impl Drop for DropScopeToken {
+// fn drop(&mut self) {
+// never!("Drop scope doesn't popped");
+// }
+// }
+
+impl MirLowerError {
+ pub fn pretty_print(
+ &self,
+ f: &mut String,
+ db: &dyn HirDatabase,
+ span_formatter: impl Fn(FileId, TextRange) -> String,
+ ) -> std::result::Result<(), std::fmt::Error> {
+ match self {
+ MirLowerError::ConstEvalError(name, e) => {
+ writeln!(f, "In evaluating constant {name}")?;
+ match &**e {
+ ConstEvalError::MirLowerError(e) => e.pretty_print(f, db, span_formatter)?,
+ ConstEvalError::MirEvalError(e) => e.pretty_print(f, db, span_formatter)?,
+ }
+ }
+ MirLowerError::MissingFunctionDefinition(owner, x) => {
+ let body = db.body(*owner);
+ writeln!(
+ f,
+ "Missing function definition for {}",
+ body.pretty_print_expr(db.upcast(), *owner, *x)
+ )?;
+ }
+ MirLowerError::TypeMismatch(e) => {
+ writeln!(
+ f,
+ "Type mismatch: Expected {}, found {}",
+ e.expected.display(db),
+ e.actual.display(db),
+ )?;
+ }
+ MirLowerError::GenericArgNotProvided(id, subst) => {
+ let parent = id.parent;
+ let param = &db.generic_params(parent).type_or_consts[id.local_id];
+ writeln!(
+ f,
+ "Generic arg not provided for {}",
+ param.name().unwrap_or(&Name::missing()).display(db.upcast())
+ )?;
+ writeln!(f, "Provided args: [")?;
+ for g in subst.iter(Interner) {
+ write!(f, " {},", g.display(db).to_string())?;
+ }
+ writeln!(f, "]")?;
+ }
+ MirLowerError::LayoutError(_)
+ | MirLowerError::UnsizedTemporary(_)
+ | MirLowerError::IncompleteExpr
+ | MirLowerError::IncompletePattern
+ | MirLowerError::UnaccessableLocal
+ | MirLowerError::TraitFunctionDefinition(_, _)
+ | MirLowerError::UnresolvedName(_)
+ | MirLowerError::RecordLiteralWithoutPath
+ | MirLowerError::UnresolvedMethod(_)
+ | MirLowerError::UnresolvedField
+ | MirLowerError::TypeError(_)
+ | MirLowerError::NotSupported(_)
+ | MirLowerError::ContinueWithoutLoop
+ | MirLowerError::BreakWithoutLoop
+ | MirLowerError::Loop
+ | MirLowerError::ImplementationError(_)
+ | MirLowerError::LangItemNotFound(_)
+ | MirLowerError::MutatingRvalue
+ | MirLowerError::UnresolvedLabel
+ | MirLowerError::UnresolvedUpvar(_) => writeln!(f, "{:?}", self)?,
+ }
+ Ok(())
+ }
}
macro_rules! not_supported {
@@ -76,20 +209,11 @@ macro_rules! not_supported {
macro_rules! implementation_error {
($x: expr) => {{
- ::stdx::never!("MIR lower implementation bug: {}", $x);
- return Err(MirLowerError::ImplementationError($x));
+ ::stdx::never!("MIR lower implementation bug: {}", format!($x));
+ return Err(MirLowerError::ImplementationError(format!($x)));
}};
}
-impl From<ConstEvalError> for MirLowerError {
- fn from(value: ConstEvalError) -> Self {
- match value {
- ConstEvalError::MirLowerError(e) => e,
- _ => MirLowerError::ConstEvalError(Box::new(value)),
- }
- }
-}
-
impl From<LayoutError> for MirLowerError {
fn from(value: LayoutError) -> Self {
MirLowerError::LayoutError(value)
@@ -104,12 +228,51 @@ impl MirLowerError {
type Result<T> = std::result::Result<T, MirLowerError>;
-impl MirLowerCtx<'_> {
- fn temp(&mut self, ty: Ty) -> Result<LocalId> {
+impl<'ctx> MirLowerCtx<'ctx> {
+ fn new(
+ db: &'ctx dyn HirDatabase,
+ owner: DefWithBodyId,
+ body: &'ctx Body,
+ infer: &'ctx InferenceResult,
+ ) -> Self {
+ let mut basic_blocks = Arena::new();
+ let start_block = basic_blocks.alloc(BasicBlock {
+ statements: vec![],
+ terminator: None,
+ is_cleanup: false,
+ });
+ let locals = Arena::new();
+ let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
+ let mir = MirBody {
+ basic_blocks,
+ locals,
+ start_block,
+ binding_locals,
+ param_locals: vec![],
+ owner,
+ closures: vec![],
+ };
+ let ctx = MirLowerCtx {
+ result: mir,
+ db,
+ infer,
+ body,
+ owner,
+ current_loop_blocks: None,
+ labeled_loop_blocks: Default::default(),
+ discr_temp: None,
+ drop_scopes: vec![DropScope::default()],
+ };
+ ctx
+ }
+
+ fn temp(&mut self, ty: Ty, current: BasicBlockId, span: MirSpan) -> Result<LocalId> {
if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
- implementation_error!("unsized temporaries");
+ return Err(MirLowerError::UnsizedTemporary(ty));
}
- Ok(self.result.locals.alloc(Local { ty }))
+ let l = self.result.locals.alloc(Local { ty });
+ self.push_storage_live_for_local(l, current, span)?;
+ Ok(l)
}
fn lower_expr_to_some_operand(
@@ -120,7 +283,7 @@ impl MirLowerCtx<'_> {
if !self.has_adjustments(expr_id) {
match &self.body.exprs[expr_id] {
Expr::Literal(l) => {
- let ty = self.expr_ty(expr_id);
+ let ty = self.expr_ty_without_adjust(expr_id);
return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
}
_ => (),
@@ -142,7 +305,8 @@ impl MirLowerCtx<'_> {
match adjustments.split_last() {
Some((last, rest)) => match &last.kind {
Adjust::NeverToAny => {
- let temp = self.temp(TyKind::Never.intern(Interner))?;
+ let temp =
+ self.temp(TyKind::Never.intern(Interner), current, MirSpan::Unknown)?;
self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest)
}
Adjust::Deref(_) => {
@@ -200,65 +364,82 @@ impl MirLowerCtx<'_> {
mut current: BasicBlockId,
) -> Result<Option<BasicBlockId>> {
match &self.body.exprs[expr_id] {
- Expr::Missing => Err(MirLowerError::IncompleteExpr),
+ Expr::Missing => {
+ if let DefWithBodyId::FunctionId(f) = self.owner {
+ let assoc = self.db.lookup_intern_function(f);
+ if let ItemContainerId::TraitId(t) = assoc.container {
+ let name = &self.db.function_data(f).name;
+ return Err(MirLowerError::TraitFunctionDefinition(t, name.clone()));
+ }
+ }
+ Err(MirLowerError::IncompleteExpr)
+ },
Expr::Path(p) => {
- let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
- let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
- let pr = resolver
- .resolve_path_in_value_ns(self.db.upcast(), p.mod_path())
- .ok_or_else(unresolved_name)?;
- let pr = match pr {
- ResolveValueResult::ValueNs(v) => v,
- ResolveValueResult::Partial(..) => {
- if let Some(assoc) = self
- .infer
- .assoc_resolutions_for_expr(expr_id)
- {
- match assoc.0 {
- hir_def::AssocItemId::ConstId(c) => {
- self.lower_const(c, current, place, expr_id.into())?;
- return Ok(Some(current))
- },
- _ => not_supported!("associated functions and types"),
- }
- } else if let Some(variant) = self
- .infer
- .variant_resolution_for_expr(expr_id)
- {
- match variant {
- VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
- VariantId::StructId(s) => ValueNs::StructId(s),
- VariantId::UnionId(_) => implementation_error!("Union variant as path"),
- }
- } else {
- return Err(unresolved_name());
+ let pr = if let Some((assoc, subst)) = self
+ .infer
+ .assoc_resolutions_for_expr(expr_id)
+ {
+ match assoc {
+ hir_def::AssocItemId::ConstId(c) => {
+ self.lower_const(c.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
+ return Ok(Some(current))
+ },
+ hir_def::AssocItemId::FunctionId(_) => {
+ // FnDefs are zero sized, no action is needed.
+ return Ok(Some(current))
}
+ hir_def::AssocItemId::TypeAliasId(_) => {
+ // FIXME: If it is unreachable, use proper error instead of `not_supported`.
+ not_supported!("associated functions and types")
+ },
}
+ } else if let Some(variant) = self
+ .infer
+ .variant_resolution_for_expr(expr_id)
+ {
+ match variant {
+ VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e),
+ VariantId::StructId(s) => ValueNs::StructId(s),
+ VariantId::UnionId(_) => implementation_error!("Union variant as path"),
+ }
+ } else {
+ let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
+ let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
+ resolver
+ .resolve_path_in_value_ns_fully(self.db.upcast(), p)
+ .ok_or_else(unresolved_name)?
};
match pr {
- ValueNs::LocalBinding(pat_id) => {
+ ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => {
+ let Some((temp, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, false)? else {
+ return Ok(None);
+ };
self.push_assignment(
current,
place,
- Operand::Copy(self.result.binding_locals[pat_id].into()).into(),
+ Operand::Copy(temp).into(),
expr_id.into(),
);
Ok(Some(current))
}
ValueNs::ConstId(const_id) => {
- self.lower_const(const_id, current, place, expr_id.into())?;
+ self.lower_const(const_id.into(), current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
Ok(Some(current))
}
ValueNs::EnumVariantId(variant_id) => {
- let ty = self.infer.type_of_expr[expr_id].clone();
- let current = self.lower_enum_variant(
- variant_id,
- current,
- place,
- ty,
- vec![],
- expr_id.into(),
- )?;
+ let variant_data = &self.db.enum_data(variant_id.parent).variants[variant_id.local_id];
+ if variant_data.variant_data.kind() == StructKind::Unit {
+ let ty = self.infer.type_of_expr[expr_id].clone();
+ current = self.lower_enum_variant(
+ variant_id,
+ current,
+ place,
+ ty,
+ Box::new([]),
+ expr_id.into(),
+ )?;
+ }
+ // Otherwise its a tuple like enum, treated like a zero sized function, so no action is needed
Ok(Some(current))
}
ValueNs::GenericParam(p) => {
@@ -266,7 +447,7 @@ impl MirLowerCtx<'_> {
not_supported!("owner without generic def id");
};
let gen = generics(self.db.upcast(), def);
- let ty = self.expr_ty(expr_id);
+ let ty = self.expr_ty_without_adjust(expr_id);
self.push_assignment(
current,
place,
@@ -287,7 +468,7 @@ impl MirLowerCtx<'_> {
);
Ok(Some(current))
}
- ValueNs::StructId(_) => {
+ ValueNs::FunctionId(_) | ValueNs::StructId(_) => {
// It's probably a unit struct or a zero sized function, so no action is needed.
Ok(Some(current))
}
@@ -311,12 +492,13 @@ impl MirLowerCtx<'_> {
};
self.set_terminator(
current,
- Terminator::SwitchInt {
+ TerminatorKind::SwitchInt {
discr,
targets: SwitchTargets::static_if(1, start_of_then, start_of_else),
},
+ expr_id.into(),
);
- Ok(self.merge_blocks(end_of_then, end_of_else))
+ Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()))
}
Expr::Let { pat, expr } => {
let Some((cond_place, current)) = self.lower_expr_as_place(current, *expr, true)? else {
@@ -326,9 +508,7 @@ impl MirLowerCtx<'_> {
current,
None,
cond_place,
- self.expr_ty_after_adjustments(*expr),
*pat,
- BindingAnnotation::Unannotated,
)?;
self.write_bytes_to_place(
then_target,
@@ -346,141 +526,107 @@ impl MirLowerCtx<'_> {
MirSpan::Unknown,
)?;
}
- Ok(self.merge_blocks(Some(then_target), else_target))
+ Ok(self.merge_blocks(Some(then_target), else_target, expr_id.into()))
}
Expr::Unsafe { id: _, statements, tail } => {
- self.lower_block_to_place(None, statements, current, *tail, place)
+ self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
}
Expr::Block { id: _, statements, tail, label } => {
- self.lower_block_to_place(*label, statements, current, *tail, place)
+ if let Some(label) = label {
+ self.lower_loop(current, place.clone(), Some(*label), expr_id.into(), |this, begin| {
+ if let Some(current) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? {
+ let end = this.current_loop_end()?;
+ this.set_goto(current, end, expr_id.into());
+ }
+ Ok(())
+ })
+ } else {
+ self.lower_block_to_place(statements, current, *tail, place, expr_id.into())
+ }
}
- Expr::Loop { body, label } => self.lower_loop(current, *label, |this, begin| {
- if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? {
- this.set_goto(block, begin);
+ Expr::Loop { body, label } => self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {
+ let scope = this.push_drop_scope();
+ if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? {
+ current = scope.pop_and_drop(this, current);
+ this.set_goto(current, begin, expr_id.into());
+ } else {
+ scope.pop_assume_dropped(this);
}
Ok(())
}),
Expr::While { condition, body, label } => {
- self.lower_loop(current, *label, |this, begin| {
+ self.lower_loop(current, place, *label, expr_id.into(),|this, begin| {
+ let scope = this.push_drop_scope();
let Some((discr, to_switch)) = this.lower_expr_to_some_operand(*condition, begin)? else {
return Ok(());
};
- let end = this.current_loop_end()?;
+ let fail_cond = this.new_basic_block();
let after_cond = this.new_basic_block();
this.set_terminator(
to_switch,
- Terminator::SwitchInt {
+ TerminatorKind::SwitchInt {
discr,
- targets: SwitchTargets::static_if(1, after_cond, end),
+ targets: SwitchTargets::static_if(1, after_cond, fail_cond),
},
+ expr_id.into(),
);
+ let fail_cond = this.drop_until_scope(this.drop_scopes.len() - 1, fail_cond);
+ let end = this.current_loop_end()?;
+ this.set_goto(fail_cond, end, expr_id.into());
if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? {
- this.set_goto(block, begin);
+ let block = scope.pop_and_drop(this, block);
+ this.set_goto(block, begin, expr_id.into());
+ } else {
+ scope.pop_assume_dropped(this);
}
Ok(())
})
}
- &Expr::For { iterable, pat, body, label } => {
- let into_iter_fn = self.resolve_lang_item(LangItem::IntoIterIntoIter)?
- .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IntoIterIntoIter))?;
- let iter_next_fn = self.resolve_lang_item(LangItem::IteratorNext)?
- .as_function().ok_or(MirLowerError::LangItemNotFound(LangItem::IteratorNext))?;
- let option_some = self.resolve_lang_item(LangItem::OptionSome)?
- .as_enum_variant().ok_or(MirLowerError::LangItemNotFound(LangItem::OptionSome))?;
- let option = option_some.parent;
- let into_iter_fn_op = Operand::const_zst(
- TyKind::FnDef(
- self.db.intern_callable_def(CallableDefId::FunctionId(into_iter_fn)).into(),
- Substitution::from1(Interner, self.expr_ty(iterable))
- ).intern(Interner));
- let iter_next_fn_op = Operand::const_zst(
- TyKind::FnDef(
- self.db.intern_callable_def(CallableDefId::FunctionId(iter_next_fn)).into(),
- Substitution::from1(Interner, self.expr_ty(iterable))
- ).intern(Interner));
- let &Some(iterator_ty) = &self.infer.type_of_for_iterator.get(&expr_id) else {
- return Err(MirLowerError::TypeError("unknown for loop iterator type"));
- };
- let ref_mut_iterator_ty = TyKind::Ref(Mutability::Mut, static_lifetime(), iterator_ty.clone()).intern(Interner);
- let item_ty = &self.infer.type_of_pat[pat];
- let option_item_ty = TyKind::Adt(chalk_ir::AdtId(option.into()), Substitution::from1(Interner, item_ty.clone())).intern(Interner);
- let iterator_place: Place = self.temp(iterator_ty.clone())?.into();
- let option_item_place: Place = self.temp(option_item_ty.clone())?.into();
- let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into();
- let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false)?
- else {
- return Ok(None);
- };
- self.push_assignment(current, ref_mut_iterator_place.clone(), Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, iterator_place), expr_id.into());
- self.lower_loop(current, label, |this, begin| {
- let Some(current) = this.lower_call(iter_next_fn_op, vec![Operand::Copy(ref_mut_iterator_place)], option_item_place.clone(), begin, false)?
- else {
- return Ok(());
- };
- let end = this.current_loop_end()?;
- let (current, _) = this.pattern_matching_variant(
- option_item_ty.clone(),
- BindingAnnotation::Unannotated,
- option_item_place.into(),
- option_some.into(),
- current,
- pat.into(),
- Some(end),
- &[pat], &None)?;
- if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? {
- this.set_goto(block, begin);
- }
- Ok(())
- })
- },
Expr::Call { callee, args, .. } => {
+ if let Some((func_id, generic_args)) =
+ self.infer.method_resolution(expr_id) {
+ let ty = chalk_ir::TyKind::FnDef(
+ CallableDefId::FunctionId(func_id).to_chalk(self.db),
+ generic_args,
+ )
+ .intern(Interner);
+ let func = Operand::from_bytes(vec![], ty);
+ return self.lower_call_and_args(
+ func,
+ iter::once(*callee).chain(args.iter().copied()),
+ place,
+ current,
+ self.is_uninhabited(expr_id),
+ expr_id.into(),
+ );
+ }
let callee_ty = self.expr_ty_after_adjustments(*callee);
match &callee_ty.data(Interner).kind {
chalk_ir::TyKind::FnDef(..) => {
let func = Operand::from_bytes(vec![], callee_ty.clone());
- self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id))
+ self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into())
}
- TyKind::Scalar(_)
- | TyKind::Tuple(_, _)
- | TyKind::Array(_, _)
- | TyKind::Adt(_, _)
- | TyKind::Str
- | TyKind::Foreign(_)
- | TyKind::Slice(_) => {
- return Err(MirLowerError::TypeError("function call on data type"))
+ chalk_ir::TyKind::Function(_) => {
+ let Some((func, current)) = self.lower_expr_to_some_operand(*callee, current)? else {
+ return Ok(None);
+ };
+ self.lower_call_and_args(func, args.iter().copied(), place, current, self.is_uninhabited(expr_id), expr_id.into())
}
- TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition),
- TyKind::AssociatedType(_, _)
- | TyKind::Raw(_, _)
- | TyKind::Ref(_, _, _)
- | TyKind::OpaqueType(_, _)
- | TyKind::Never
- | TyKind::Closure(_, _)
- | TyKind::Generator(_, _)
- | TyKind::GeneratorWitness(_, _)
- | TyKind::Placeholder(_)
- | TyKind::Dyn(_)
- | TyKind::Alias(_)
- | TyKind::Function(_)
- | TyKind::BoundVar(_)
- | TyKind::InferenceVar(_, _) => not_supported!("dynamic function call"),
+ TyKind::Error => return Err(MirLowerError::MissingFunctionDefinition(self.owner, expr_id)),
+ _ => return Err(MirLowerError::TypeError("function call on bad type")),
}
}
- Expr::MethodCall { receiver, args, .. } => {
+ Expr::MethodCall { receiver, args, method_name, .. } => {
let (func_id, generic_args) =
- self.infer.method_resolution(expr_id).ok_or(MirLowerError::UnresolvedMethod)?;
- let ty = chalk_ir::TyKind::FnDef(
- CallableDefId::FunctionId(func_id).to_chalk(self.db),
- generic_args,
- )
- .intern(Interner);
- let func = Operand::from_bytes(vec![], ty);
+ self.infer.method_resolution(expr_id).ok_or_else(|| MirLowerError::UnresolvedMethod(method_name.display(self.db.upcast()).to_string()))?;
+ let func = Operand::from_fn(self.db, func_id, generic_args);
self.lower_call_and_args(
func,
iter::once(*receiver).chain(args.iter().copied()),
place,
current,
self.is_uninhabited(expr_id),
+ expr_id.into(),
)
}
Expr::Match { expr, arms } => {
@@ -488,23 +634,27 @@ impl MirLowerCtx<'_> {
else {
return Ok(None);
};
- let cond_ty = self.expr_ty_after_adjustments(*expr);
let mut end = None;
for MatchArm { pat, guard, expr } in arms.iter() {
- if guard.is_some() {
- not_supported!("pattern matching with guard");
- }
- let (then, otherwise) = self.pattern_match(
+ let (then, mut otherwise) = self.pattern_match(
current,
None,
cond_place.clone(),
- cond_ty.clone(),
*pat,
- BindingAnnotation::Unannotated,
)?;
+ let then = if let &Some(guard) = guard {
+ let next = self.new_basic_block();
+ let o = otherwise.get_or_insert_with(|| self.new_basic_block());
+ if let Some((discr, c)) = self.lower_expr_to_some_operand(guard, then)? {
+ self.set_terminator(c, TerminatorKind::SwitchInt { discr, targets: SwitchTargets::static_if(1, next, *o) }, expr_id.into());
+ }
+ next
+ } else {
+ then
+ };
if let Some(block) = self.lower_expr_to_place(*expr, place.clone(), then)? {
let r = end.get_or_insert_with(|| self.new_basic_block());
- self.set_goto(block, *r);
+ self.set_goto(block, *r, expr_id.into());
}
match otherwise {
Some(o) => current = o,
@@ -516,32 +666,43 @@ impl MirLowerCtx<'_> {
}
}
if self.is_unterminated(current) {
- self.set_terminator(current, Terminator::Unreachable);
+ self.set_terminator(current, TerminatorKind::Unreachable, expr_id.into());
}
Ok(end)
}
- Expr::Continue { label } => match label {
- Some(_) => not_supported!("continue with label"),
- None => {
- let loop_data =
- self.current_loop_blocks.ok_or(MirLowerError::ContinueWithoutLoop)?;
- self.set_goto(current, loop_data.begin);
- Ok(None)
- }
+ Expr::Continue { label } => {
+ let loop_data = match label {
+ Some(l) => self.labeled_loop_blocks.get(l).ok_or(MirLowerError::UnresolvedLabel)?,
+ None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::ContinueWithoutLoop)?,
+ };
+ let begin = loop_data.begin;
+ current = self.drop_until_scope(loop_data.drop_scope_index, current);
+ self.set_goto(current, begin, expr_id.into());
+ Ok(None)
},
- Expr::Break { expr, label } => {
- if expr.is_some() {
- not_supported!("break with value");
+ &Expr::Break { expr, label } => {
+ if let Some(expr) = expr {
+ let loop_data = match label {
+ Some(l) => self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?,
+ None => self.current_loop_blocks.as_ref().ok_or(MirLowerError::BreakWithoutLoop)?,
+ };
+ let Some(c) = self.lower_expr_to_place(expr, loop_data.place.clone(), current)? else {
+ return Ok(None);
+ };
+ current = c;
}
- match label {
- Some(_) => not_supported!("break with label"),
+ let (end, drop_scope) = match label {
+ Some(l) => {
+ let loop_blocks = self.labeled_loop_blocks.get(&l).ok_or(MirLowerError::UnresolvedLabel)?;
+ (loop_blocks.end.expect("We always generate end for labeled loops"), loop_blocks.drop_scope_index)
+ },
None => {
- let end =
- self.current_loop_end()?;
- self.set_goto(current, end);
- Ok(None)
- }
- }
+ (self.current_loop_end()?, self.current_loop_blocks.as_ref().unwrap().drop_scope_index)
+ },
+ };
+ current = self.drop_until_scope(drop_scope, current);
+ self.set_goto(current, end, expr_id.into());
+ Ok(None)
}
Expr::Return { expr } => {
if let Some(expr) = expr {
@@ -551,11 +712,22 @@ impl MirLowerCtx<'_> {
return Ok(None);
}
}
- self.set_terminator(current, Terminator::Return);
+ current = self.drop_until_scope(0, current);
+ self.set_terminator(current, TerminatorKind::Return, expr_id.into());
Ok(None)
}
Expr::Yield { .. } => not_supported!("yield"),
- Expr::RecordLit { fields, path, .. } => {
+ Expr::RecordLit { fields, path, spread, ellipsis: _, is_assignee_expr: _ } => {
+ let spread_place = match spread {
+ &Some(x) => {
+ let Some((p, c)) = self.lower_expr_as_place(current, x, true)? else {
+ return Ok(None);
+ };
+ current = c;
+ Some(p)
+ },
+ None => None,
+ };
let variant_id = self
.infer
.variant_resolution_for_expr(expr_id)
@@ -563,7 +735,7 @@ impl MirLowerCtx<'_> {
Some(p) => MirLowerError::UnresolvedName(p.display(self.db).to_string()),
None => MirLowerError::RecordLiteralWithoutPath,
})?;
- let subst = match self.expr_ty(expr_id).kind(Interner) {
+ let subst = match self.expr_ty_without_adjust(expr_id).kind(Interner) {
TyKind::Adt(_, s) => s.clone(),
_ => not_supported!("Non ADT record literal"),
};
@@ -585,9 +757,23 @@ impl MirLowerCtx<'_> {
place,
Rvalue::Aggregate(
AggregateKind::Adt(variant_id, subst),
- operands.into_iter().map(|x| x).collect::<Option<_>>().ok_or(
- MirLowerError::TypeError("missing field in record literal"),
- )?,
+ match spread_place {
+ Some(sp) => operands.into_iter().enumerate().map(|(i, x)| {
+ match x {
+ Some(x) => x,
+ None => {
+ let p = sp.project(ProjectionElem::Field(FieldId {
+ parent: variant_id,
+ local_id: LocalFieldId::from_raw(RawIdx::from(i as u32)),
+ }));
+ Operand::Copy(p)
+ },
+ }
+ }).collect(),
+ None => operands.into_iter().collect::<Option<_>>().ok_or(
+ MirLowerError::TypeError("missing field in record literal"),
+ )?,
+ },
),
expr_id.into(),
);
@@ -599,20 +785,19 @@ impl MirLowerCtx<'_> {
};
let local_id =
variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?;
- let mut place = place;
- place
- .projection
- .push(PlaceElem::Field(FieldId { parent: union_id.into(), local_id }));
+ let place = place.project(PlaceElem::Field(FieldId { parent: union_id.into(), local_id }));
self.lower_expr_to_place(*expr, place, current)
}
}
}
Expr::Await { .. } => not_supported!("await"),
- Expr::Try { .. } => not_supported!("? operator"),
Expr::Yeet { .. } => not_supported!("yeet"),
- Expr::TryBlock { .. } => not_supported!("try block"),
Expr::Async { .. } => not_supported!("async block"),
- Expr::Const { .. } => not_supported!("anonymous const block"),
+ &Expr::Const(id) => {
+ let subst = self.placeholder_subst();
+ self.lower_const(id.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?;
+ Ok(Some(current))
+ },
Expr::Cast { expr, type_ref: _ } => {
let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
return Ok(None);
@@ -635,21 +820,30 @@ impl MirLowerCtx<'_> {
self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
Ok(Some(current))
}
- Expr::Box { .. } => not_supported!("box expression"),
- Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::expr::UnaryOp::Deref, .. } => {
+ Expr::Box { expr } => {
+ let ty = self.expr_ty_after_adjustments(*expr);
+ self.push_assignment(current, place.clone(), Rvalue::ShallowInitBoxWithAlloc(ty), expr_id.into());
+ let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
+ return Ok(None);
+ };
+ let p = place.project(ProjectionElem::Deref);
+ self.push_assignment(current, p, operand.into(), expr_id.into());
+ Ok(Some(current))
+ },
+ Expr::Field { .. } | Expr::Index { .. } | Expr::UnaryOp { op: hir_def::hir::UnaryOp::Deref, .. } => {
let Some((p, current)) = self.lower_expr_as_place_without_adjust(current, expr_id, true)? else {
return Ok(None);
};
self.push_assignment(current, place, Operand::Copy(p).into(), expr_id.into());
Ok(Some(current))
}
- Expr::UnaryOp { expr, op: op @ (hir_def::expr::UnaryOp::Not | hir_def::expr::UnaryOp::Neg) } => {
+ Expr::UnaryOp { expr, op: op @ (hir_def::hir::UnaryOp::Not | hir_def::hir::UnaryOp::Neg) } => {
let Some((operand, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
return Ok(None);
};
let operation = match op {
- hir_def::expr::UnaryOp::Not => UnOp::Not,
- hir_def::expr::UnaryOp::Neg => UnOp::Neg,
+ hir_def::hir::UnaryOp::Not => UnOp::Not,
+ hir_def::hir::UnaryOp::Neg => UnOp::Neg,
_ => unreachable!(),
};
self.push_assignment(
@@ -662,24 +856,93 @@ impl MirLowerCtx<'_> {
},
Expr::BinaryOp { lhs, rhs, op } => {
let op = op.ok_or(MirLowerError::IncompleteExpr)?;
- if let hir_def::expr::BinaryOp::Assignment { op } = op {
- if op.is_some() {
- not_supported!("assignment with arith op (like +=)");
+ let is_builtin = 'b: {
+ // Without adjust here is a hack. We assume that we know every possible adjustment
+ // for binary operator, and use without adjust to simplify our conditions.
+ let lhs_ty = self.expr_ty_without_adjust(*lhs);
+ let rhs_ty = self.expr_ty_without_adjust(*rhs);
+ if matches!(op ,BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. })) {
+ if lhs_ty.as_raw_ptr().is_some() && rhs_ty.as_raw_ptr().is_some() {
+ break 'b true;
+ }
}
- let Some((lhs_place, current)) =
+ let builtin_inequal_impls = matches!(
+ op,
+ BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) }
+ );
+ lhs_ty.is_scalar() && rhs_ty.is_scalar() && (lhs_ty == rhs_ty || builtin_inequal_impls)
+ };
+ if !is_builtin {
+ if let Some((func_id, generic_args)) = self.infer.method_resolution(expr_id) {
+ let func = Operand::from_fn(self.db, func_id, generic_args);
+ return self.lower_call_and_args(
+ func,
+ [*lhs, *rhs].into_iter(),
+ place,
+ current,
+ self.is_uninhabited(expr_id),
+ expr_id.into(),
+ );
+ }
+ }
+ if let hir_def::hir::BinaryOp::Assignment { op } = op {
+ if let Some(op) = op {
+ // last adjustment is `&mut` which we don't want it.
+ let adjusts = self
+ .infer
+ .expr_adjustments
+ .get(lhs)
+ .and_then(|x| x.split_last())
+ .map(|x| x.1)
+ .ok_or(MirLowerError::TypeError("adjustment of binary op was missing"))?;
+ let Some((lhs_place, current)) =
+ self.lower_expr_as_place_with_adjust(current, *lhs, false, adjusts)?
+ else {
+ return Ok(None);
+ };
+ let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
+ return Ok(None);
+ };
+ let r_value = Rvalue::CheckedBinaryOp(op.into(), Operand::Copy(lhs_place.clone()), rhs_op);
+ self.push_assignment(current, lhs_place, r_value, expr_id.into());
+ return Ok(Some(current));
+ } else {
+ let Some((lhs_place, current)) =
self.lower_expr_as_place(current, *lhs, false)?
- else {
- return Ok(None);
- };
- let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
- return Ok(None);
- };
- self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
- return Ok(Some(current));
+ else {
+ return Ok(None);
+ };
+ let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
+ return Ok(None);
+ };
+ self.push_assignment(current, lhs_place, rhs_op.into(), expr_id.into());
+ return Ok(Some(current));
+ }
}
let Some((lhs_op, current)) = self.lower_expr_to_some_operand(*lhs, current)? else {
return Ok(None);
};
+ if let hir_def::hir::BinaryOp::LogicOp(op) = op {
+ let value_to_short = match op {
+ syntax::ast::LogicOp::And => 0,
+ syntax::ast::LogicOp::Or => 1,
+ };
+ let start_of_then = self.new_basic_block();
+ self.push_assignment(start_of_then, place.clone(), lhs_op.clone().into(), expr_id.into());
+ let end_of_then = Some(start_of_then);
+ let start_of_else = self.new_basic_block();
+ let end_of_else =
+ self.lower_expr_to_place(*rhs, place, start_of_else)?;
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr: lhs_op,
+ targets: SwitchTargets::static_if(value_to_short, start_of_then, start_of_else),
+ },
+ expr_id.into(),
+ );
+ return Ok(self.merge_blocks(end_of_then, end_of_else, expr_id.into()));
+ }
let Some((rhs_op, current)) = self.lower_expr_to_some_operand(*rhs, current)? else {
return Ok(None);
};
@@ -688,13 +951,13 @@ impl MirLowerCtx<'_> {
place,
Rvalue::CheckedBinaryOp(
match op {
- hir_def::expr::BinaryOp::LogicOp(op) => match op {
- hir_def::expr::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
- hir_def::expr::LogicOp::Or => BinOp::BitOr,
+ hir_def::hir::BinaryOp::LogicOp(op) => match op {
+ hir_def::hir::LogicOp::And => BinOp::BitAnd, // FIXME: make these short circuit
+ hir_def::hir::LogicOp::Or => BinOp::BitOr,
},
- hir_def::expr::BinaryOp::ArithOp(op) => BinOp::from(op),
- hir_def::expr::BinaryOp::CmpOp(op) => BinOp::from(op),
- hir_def::expr::BinaryOp::Assignment { .. } => unreachable!(), // handled above
+ hir_def::hir::BinaryOp::ArithOp(op) => BinOp::from(op),
+ hir_def::hir::BinaryOp::CmpOp(op) => BinOp::from(op),
+ hir_def::hir::BinaryOp::Assignment { .. } => unreachable!(), // handled above
},
lhs_op,
rhs_op,
@@ -703,8 +966,96 @@ impl MirLowerCtx<'_> {
);
Ok(Some(current))
}
- Expr::Range { .. } => not_supported!("range"),
- Expr::Closure { .. } => not_supported!("closure"),
+ &Expr::Range { lhs, rhs, range_type: _ } => {
+ let ty = self.expr_ty_without_adjust(expr_id);
+ let Some((adt, subst)) = ty.as_adt() else {
+ return Err(MirLowerError::TypeError("Range type is not adt"));
+ };
+ let AdtId::StructId(st) = adt else {
+ return Err(MirLowerError::TypeError("Range type is not struct"));
+ };
+ let mut lp = None;
+ let mut rp = None;
+ if let Some(x) = lhs {
+ let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else {
+ return Ok(None);
+ };
+ lp = Some(o);
+ current = c;
+ }
+ if let Some(x) = rhs {
+ let Some((o, c)) = self.lower_expr_to_some_operand(x, current)? else {
+ return Ok(None);
+ };
+ rp = Some(o);
+ current = c;
+ }
+ self.push_assignment(
+ current,
+ place,
+ Rvalue::Aggregate(
+ AggregateKind::Adt(st.into(), subst.clone()),
+ self.db.struct_data(st).variant_data.fields().iter().map(|x| {
+ let o = match x.1.name.as_str() {
+ Some("start") => lp.take(),
+ Some("end") => rp.take(),
+ Some("exhausted") => Some(Operand::from_bytes(vec![0], TyBuilder::bool())),
+ _ => None,
+ };
+ o.ok_or(MirLowerError::UnresolvedField)
+ }).collect::<Result<_>>()?,
+ ),
+ expr_id.into(),
+ );
+ Ok(Some(current))
+ },
+ Expr::Closure { .. } => {
+ let ty = self.expr_ty_without_adjust(expr_id);
+ let TyKind::Closure(id, _) = ty.kind(Interner) else {
+ not_supported!("closure with non closure type");
+ };
+ self.result.closures.push(*id);
+ let (captures, _) = self.infer.closure_info(id);
+ let mut operands = vec![];
+ for capture in captures.iter() {
+ let p = Place {
+ local: self.binding_local(capture.place.local)?,
+ projection: capture.place.projections.clone().into_iter().map(|x| {
+ match x {
+ ProjectionElem::Deref => ProjectionElem::Deref,
+ ProjectionElem::Field(x) => ProjectionElem::Field(x),
+ ProjectionElem::TupleOrClosureField(x) => ProjectionElem::TupleOrClosureField(x),
+ ProjectionElem::ConstantIndex { offset, from_end } => ProjectionElem::ConstantIndex { offset, from_end },
+ ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to },
+ ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x),
+ ProjectionElem::Index(x) => match x { },
+ }
+ }).collect(),
+ };
+ match &capture.kind {
+ CaptureKind::ByRef(bk) => {
+ let placeholder_subst = self.placeholder_subst();
+ let tmp_ty = capture.ty.clone().substitute(Interner, &placeholder_subst);
+ let tmp: Place = self.temp(tmp_ty, current, capture.span)?.into();
+ self.push_assignment(
+ current,
+ tmp.clone(),
+ Rvalue::Ref(bk.clone(), p),
+ capture.span,
+ );
+ operands.push(Operand::Move(tmp));
+ },
+ CaptureKind::ByValue => operands.push(Operand::Move(p)),
+ }
+ }
+ self.push_assignment(
+ current,
+ place,
+ Rvalue::Aggregate(AggregateKind::Closure(ty), operands.into()),
+ expr_id.into(),
+ );
+ Ok(Some(current))
+ },
Expr::Tuple { exprs, is_assignee_expr: _ } => {
let Some(values) = exprs
.iter()
@@ -720,7 +1071,7 @@ impl MirLowerCtx<'_> {
return Ok(None);
};
let r = Rvalue::Aggregate(
- AggregateKind::Tuple(self.expr_ty(expr_id)),
+ AggregateKind::Tuple(self.expr_ty_without_adjust(expr_id)),
values,
);
self.push_assignment(current, place, r, expr_id.into());
@@ -728,7 +1079,7 @@ impl MirLowerCtx<'_> {
}
Expr::Array(l) => match l {
Array::ElementList { elements, .. } => {
- let elem_ty = match &self.expr_ty(expr_id).data(Interner).kind {
+ let elem_ty = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind {
TyKind::Array(ty, _) => ty.clone(),
_ => {
return Err(MirLowerError::TypeError(
@@ -756,10 +1107,25 @@ impl MirLowerCtx<'_> {
self.push_assignment(current, place, r, expr_id.into());
Ok(Some(current))
}
- Array::Repeat { .. } => not_supported!("array repeat"),
+ Array::Repeat { initializer, .. } => {
+ let Some((init, current)) = self.lower_expr_to_some_operand(*initializer, current)? else {
+ return Ok(None);
+ };
+ let len = match &self.expr_ty_without_adjust(expr_id).data(Interner).kind {
+ TyKind::Array(_, len) => len.clone(),
+ _ => {
+ return Err(MirLowerError::TypeError(
+ "Array repeat expression with non array type",
+ ))
+ }
+ };
+ let r = Rvalue::Repeat(init, len);
+ self.push_assignment(current, place, r, expr_id.into());
+ Ok(Some(current))
+ },
},
Expr::Literal(l) => {
- let ty = self.expr_ty(expr_id);
+ let ty = self.expr_ty_without_adjust(expr_id);
let op = self.lower_literal_to_operand(ty, l)?;
self.push_assignment(current, place, op.into(), expr_id.into());
Ok(Some(current))
@@ -768,17 +1134,25 @@ impl MirLowerCtx<'_> {
}
}
+ fn placeholder_subst(&mut self) -> Substitution {
+ let placeholder_subst = match self.owner.as_generic_def_id() {
+ Some(x) => TyBuilder::placeholder_subst(self.db, x),
+ None => Substitution::empty(Interner),
+ };
+ placeholder_subst
+ }
+
fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> {
if let Expr::Field { expr, name } = &self.body[expr_id] {
if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) {
let index = name
.as_tuple_index()
.ok_or(MirLowerError::TypeError("named field on tuple"))?;
- place.projection.push(ProjectionElem::TupleField(index))
+ *place = place.project(ProjectionElem::TupleOrClosureField(index))
} else {
let field =
self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?;
- place.projection.push(ProjectionElem::Field(field));
+ *place = place.project(ProjectionElem::Field(field));
}
} else {
not_supported!("")
@@ -786,33 +1160,75 @@ impl MirLowerCtx<'_> {
Ok(())
}
+ fn lower_literal_or_const_to_operand(
+ &mut self,
+ ty: Ty,
+ loc: &LiteralOrConst,
+ ) -> Result<Operand> {
+ match loc {
+ LiteralOrConst::Literal(l) => self.lower_literal_to_operand(ty, l),
+ LiteralOrConst::Const(c) => {
+ let unresolved_name = || MirLowerError::unresolved_path(self.db, c);
+ let resolver = self.owner.resolver(self.db.upcast());
+ let pr = resolver
+ .resolve_path_in_value_ns(self.db.upcast(), c)
+ .ok_or_else(unresolved_name)?;
+ match pr {
+ ResolveValueResult::ValueNs(v) => {
+ if let ValueNs::ConstId(c) = v {
+ self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty)
+ } else {
+ not_supported!("bad path in range pattern");
+ }
+ }
+ ResolveValueResult::Partial(_, _) => {
+ not_supported!("associated constants in range pattern")
+ }
+ }
+ }
+ }
+ }
+
fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> {
- let size = layout_of_ty(self.db, &ty, self.owner.module(self.db.upcast()).krate())?
+ let size = self
+ .db
+ .layout_of_ty(ty.clone(), self.owner.module(self.db.upcast()).krate())?
.size
.bytes_usize();
let bytes = match l {
- hir_def::expr::Literal::String(b) => {
+ hir_def::hir::Literal::String(b) => {
let b = b.as_bytes();
- let mut data = vec![];
+ let mut data = Vec::with_capacity(mem::size_of::<usize>() * 2);
data.extend(0usize.to_le_bytes());
data.extend(b.len().to_le_bytes());
let mut mm = MemoryMap::default();
mm.insert(0, b.to_vec());
return Ok(Operand::from_concrete_const(data, mm, ty));
}
- hir_def::expr::Literal::ByteString(b) => {
- let mut data = vec![];
+ hir_def::hir::Literal::CString(b) => {
+ let b = b.as_bytes();
+ let bytes = b.iter().copied().chain(iter::once(0)).collect::<Vec<_>>();
+
+ let mut data = Vec::with_capacity(mem::size_of::<usize>() * 2);
+ data.extend(0usize.to_le_bytes());
+ data.extend(bytes.len().to_le_bytes());
+ let mut mm = MemoryMap::default();
+ mm.insert(0, bytes);
+ return Ok(Operand::from_concrete_const(data, mm, ty));
+ }
+ hir_def::hir::Literal::ByteString(b) => {
+ let mut data = Vec::with_capacity(mem::size_of::<usize>() * 2);
data.extend(0usize.to_le_bytes());
data.extend(b.len().to_le_bytes());
let mut mm = MemoryMap::default();
mm.insert(0, b.to_vec());
return Ok(Operand::from_concrete_const(data, mm, ty));
}
- hir_def::expr::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
- hir_def::expr::Literal::Bool(b) => vec![*b as u8],
- hir_def::expr::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
- hir_def::expr::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
- hir_def::expr::Literal::Float(f, _) => match size {
+ hir_def::hir::Literal::Char(c) => u32::from(*c).to_le_bytes().into(),
+ hir_def::hir::Literal::Bool(b) => vec![*b as u8],
+ hir_def::hir::Literal::Int(x, _) => x.to_le_bytes()[0..size].into(),
+ hir_def::hir::Literal::Uint(x, _) => x.to_le_bytes()[0..size].into(),
+ hir_def::hir::Literal::Float(f, _) => match size {
8 => f.into_f64().to_le_bytes().into(),
4 => f.into_f32().to_le_bytes().into(),
_ => {
@@ -829,24 +1245,34 @@ impl MirLowerCtx<'_> {
fn lower_const(
&mut self,
- const_id: hir_def::ConstId,
+ const_id: GeneralConstId,
prev_block: BasicBlockId,
place: Place,
+ subst: Substitution,
span: MirSpan,
+ ty: Ty,
) -> Result<()> {
- let c = self.db.const_eval(const_id)?;
- self.write_const_to_place(c, prev_block, place, span)
+ let c = self.lower_const_to_operand(subst, const_id, ty)?;
+ self.push_assignment(prev_block, place, c.into(), span);
+ Ok(())
}
- fn write_const_to_place(
+ fn lower_const_to_operand(
&mut self,
- c: Const,
- prev_block: BasicBlockId,
- place: Place,
- span: MirSpan,
- ) -> Result<()> {
- self.push_assignment(prev_block, place, Operand::Constant(c).into(), span);
- Ok(())
+ subst: Substitution,
+ const_id: GeneralConstId,
+ ty: Ty,
+ ) -> Result<Operand> {
+ let c = if subst.len(Interner) != 0 {
+ // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering.
+ intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty)
+ } else {
+ let name = const_id.name(self.db.upcast());
+ self.db
+ .const_eval(const_id.into(), subst)
+ .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))?
+ };
+ Ok(Operand::Constant(c))
}
fn write_bytes_to_place(
@@ -867,12 +1293,12 @@ impl MirLowerCtx<'_> {
prev_block: BasicBlockId,
place: Place,
ty: Ty,
- fields: Vec<Operand>,
+ fields: Box<[Operand]>,
span: MirSpan,
) -> Result<BasicBlockId> {
let subst = match ty.kind(Interner) {
TyKind::Adt(_, subst) => subst.clone(),
- _ => not_supported!("Non ADT enum"),
+ _ => implementation_error!("Non ADT enum"),
};
self.push_assignment(
prev_block,
@@ -890,6 +1316,7 @@ impl MirLowerCtx<'_> {
place: Place,
mut current: BasicBlockId,
is_uninhabited: bool,
+ span: MirSpan,
) -> Result<Option<BasicBlockId>> {
let Some(args) = args
.map(|arg| {
@@ -904,21 +1331,22 @@ impl MirLowerCtx<'_> {
else {
return Ok(None);
};
- self.lower_call(func, args, place, current, is_uninhabited)
+ self.lower_call(func, args.into(), place, current, is_uninhabited, span)
}
fn lower_call(
&mut self,
func: Operand,
- args: Vec<Operand>,
+ args: Box<[Operand]>,
place: Place,
current: BasicBlockId,
is_uninhabited: bool,
+ span: MirSpan,
) -> Result<Option<BasicBlockId>> {
let b = if is_uninhabited { None } else { Some(self.new_basic_block()) };
self.set_terminator(
current,
- Terminator::Call {
+ TerminatorKind::Call {
func,
args,
destination: place,
@@ -926,6 +1354,7 @@ impl MirLowerCtx<'_> {
cleanup: None,
from_hir_call: true,
},
+ span,
);
Ok(b)
}
@@ -934,15 +1363,15 @@ impl MirLowerCtx<'_> {
self.result.basic_blocks[source].terminator.is_none()
}
- fn set_terminator(&mut self, source: BasicBlockId, terminator: Terminator) {
- self.result.basic_blocks[source].terminator = Some(terminator);
+ fn set_terminator(&mut self, source: BasicBlockId, terminator: TerminatorKind, span: MirSpan) {
+ self.result.basic_blocks[source].terminator = Some(Terminator { span, kind: terminator });
}
- fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId) {
- self.set_terminator(source, Terminator::Goto { target });
+ fn set_goto(&mut self, source: BasicBlockId, target: BasicBlockId, span: MirSpan) {
+ self.set_terminator(source, TerminatorKind::Goto { target }, span);
}
- fn expr_ty(&self, e: ExprId) -> Ty {
+ fn expr_ty_without_adjust(&self, e: ExprId) -> Ty {
self.infer[e].clone()
}
@@ -953,7 +1382,7 @@ impl MirLowerCtx<'_> {
ty = Some(x.target.clone());
}
}
- ty.unwrap_or_else(|| self.expr_ty(e))
+ ty.unwrap_or_else(|| self.expr_ty_without_adjust(e))
}
fn push_statement(&mut self, block: BasicBlockId, statement: Statement) {
@@ -970,293 +1399,14 @@ impl MirLowerCtx<'_> {
self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span));
}
- /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if
- /// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which
- /// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the
- /// mismatched path block is `None`.
- ///
- /// By default, it will create a new block for mismatched path. If you already have one, you can provide it with
- /// `current_else` argument to save an unneccessary jump. If `current_else` isn't `None`, the result mismatched path
- /// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block,
- /// so it should be an empty block.
- fn pattern_match(
- &mut self,
- mut current: BasicBlockId,
- mut current_else: Option<BasicBlockId>,
- mut cond_place: Place,
- mut cond_ty: Ty,
- pattern: PatId,
- mut binding_mode: BindingAnnotation,
- ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
- Ok(match &self.body.pats[pattern] {
- Pat::Missing => return Err(MirLowerError::IncompleteExpr),
- Pat::Wild => (current, current_else),
- Pat::Tuple { args, ellipsis } => {
- pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
- let subst = match cond_ty.kind(Interner) {
- TyKind::Tuple(_, s) => s,
- _ => {
- return Err(MirLowerError::TypeError(
- "non tuple type matched with tuple pattern",
- ))
- }
- };
- self.pattern_match_tuple_like(
- current,
- current_else,
- args.iter().enumerate().map(|(i, x)| {
- (
- PlaceElem::TupleField(i),
- *x,
- subst.at(Interner, i).assert_ty_ref(Interner).clone(),
- )
- }),
- *ellipsis,
- &cond_place,
- binding_mode,
- )?
- }
- Pat::Or(pats) => {
- let then_target = self.new_basic_block();
- let mut finished = false;
- for pat in &**pats {
- let (next, next_else) = self.pattern_match(
- current,
- None,
- cond_place.clone(),
- cond_ty.clone(),
- *pat,
- binding_mode,
- )?;
- self.set_goto(next, then_target);
- match next_else {
- Some(t) => {
- current = t;
- }
- None => {
- finished = true;
- break;
- }
- }
- }
- if !finished {
- let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
- self.set_goto(current, ce);
- }
- (then_target, current_else)
- }
- Pat::Record { .. } => not_supported!("record pattern"),
- Pat::Range { .. } => not_supported!("range pattern"),
- Pat::Slice { .. } => not_supported!("slice pattern"),
- Pat::Path(_) => {
- let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
- not_supported!("unresolved variant");
- };
- self.pattern_matching_variant(
- cond_ty,
- binding_mode,
- cond_place,
- variant,
- current,
- pattern.into(),
- current_else,
- &[],
- &None,
- )?
- }
- Pat::Lit(l) => {
- let then_target = self.new_basic_block();
- let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
- match &self.body.exprs[*l] {
- Expr::Literal(l) => match l {
- hir_def::expr::Literal::Int(x, _) => {
- self.set_terminator(
- current,
- Terminator::SwitchInt {
- discr: Operand::Copy(cond_place),
- targets: SwitchTargets::static_if(
- *x as u128,
- then_target,
- else_target,
- ),
- },
- );
- }
- hir_def::expr::Literal::Uint(x, _) => {
- self.set_terminator(
- current,
- Terminator::SwitchInt {
- discr: Operand::Copy(cond_place),
- targets: SwitchTargets::static_if(*x, then_target, else_target),
- },
- );
- }
- _ => not_supported!("non int path literal"),
- },
- _ => not_supported!("expression path literal"),
- }
- (then_target, Some(else_target))
- }
- Pat::Bind { id, subpat } => {
- let target_place = self.result.binding_locals[*id];
- let mode = self.body.bindings[*id].mode;
- if let Some(subpat) = subpat {
- (current, current_else) = self.pattern_match(
- current,
- current_else,
- cond_place.clone(),
- cond_ty,
- *subpat,
- binding_mode,
- )?
- }
- if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
- binding_mode = mode;
- }
- self.push_storage_live(*id, current);
- self.push_assignment(
- current,
- target_place.into(),
- match binding_mode {
- BindingAnnotation::Unannotated | BindingAnnotation::Mutable => {
- Operand::Copy(cond_place).into()
- }
- BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place),
- BindingAnnotation::RefMut => Rvalue::Ref(
- BorrowKind::Mut { allow_two_phase_borrow: false },
- cond_place,
- ),
- },
- pattern.into(),
- );
- (current, current_else)
- }
- Pat::TupleStruct { path: _, args, ellipsis } => {
- let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
- not_supported!("unresolved variant");
- };
- self.pattern_matching_variant(
- cond_ty,
- binding_mode,
- cond_place,
- variant,
- current,
- pattern.into(),
- current_else,
- args,
- ellipsis,
- )?
- }
- Pat::Ref { .. } => not_supported!("& pattern"),
- Pat::Box { .. } => not_supported!("box pattern"),
- Pat::ConstBlock(_) => not_supported!("const block pattern"),
- })
- }
-
- fn pattern_matching_variant(
- &mut self,
- mut cond_ty: Ty,
- mut binding_mode: BindingAnnotation,
- mut cond_place: Place,
- variant: VariantId,
- current: BasicBlockId,
- span: MirSpan,
- current_else: Option<BasicBlockId>,
- args: &[PatId],
- ellipsis: &Option<usize>,
- ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
- pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place);
- let subst = match cond_ty.kind(Interner) {
- TyKind::Adt(_, s) => s,
- _ => return Err(MirLowerError::TypeError("non adt type matched with tuple struct")),
- };
- let fields_type = self.db.field_types(variant);
- Ok(match variant {
- VariantId::EnumVariantId(v) => {
- let e = self.db.const_eval_discriminant(v)? as u128;
- let next = self.new_basic_block();
- let tmp = self.discr_temp_place();
- self.push_assignment(
- current,
- tmp.clone(),
- Rvalue::Discriminant(cond_place.clone()),
- span,
- );
- let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
- self.set_terminator(
- current,
- Terminator::SwitchInt {
- discr: Operand::Copy(tmp),
- targets: SwitchTargets::static_if(e, next, else_target),
- },
- );
- let enum_data = self.db.enum_data(v.parent);
- let fields =
- enum_data.variants[v.local_id].variant_data.fields().iter().map(|(x, _)| {
- (
- PlaceElem::Field(FieldId { parent: v.into(), local_id: x }),
- fields_type[x].clone().substitute(Interner, subst),
- )
- });
- self.pattern_match_tuple_like(
- next,
- Some(else_target),
- args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
- *ellipsis,
- &cond_place,
- binding_mode,
- )?
- }
- VariantId::StructId(s) => {
- let struct_data = self.db.struct_data(s);
- let fields = struct_data.variant_data.fields().iter().map(|(x, _)| {
- (
- PlaceElem::Field(FieldId { parent: s.into(), local_id: x }),
- fields_type[x].clone().substitute(Interner, subst),
- )
- });
- self.pattern_match_tuple_like(
- current,
- current_else,
- args.iter().zip(fields).map(|(x, y)| (y.0, *x, y.1)),
- *ellipsis,
- &cond_place,
- binding_mode,
- )?
- }
- VariantId::UnionId(_) => {
- return Err(MirLowerError::TypeError("pattern matching on union"))
- }
- })
- }
-
- fn pattern_match_tuple_like(
- &mut self,
- mut current: BasicBlockId,
- mut current_else: Option<BasicBlockId>,
- args: impl Iterator<Item = (PlaceElem, PatId, Ty)>,
- ellipsis: Option<usize>,
- cond_place: &Place,
- binding_mode: BindingAnnotation,
- ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
- if ellipsis.is_some() {
- not_supported!("tuple like pattern with ellipsis");
- }
- for (proj, arg, ty) in args {
- let mut cond_place = cond_place.clone();
- cond_place.projection.push(proj);
- (current, current_else) =
- self.pattern_match(current, current_else, cond_place, ty, arg, binding_mode)?;
- }
- Ok((current, current_else))
- }
-
- fn discr_temp_place(&mut self) -> Place {
+ fn discr_temp_place(&mut self, current: BasicBlockId) -> Place {
match &self.discr_temp {
Some(x) => x.clone(),
None => {
- let tmp: Place =
- self.temp(TyBuilder::discr_ty()).expect("discr_ty is never unsized").into();
+ let tmp: Place = self
+ .temp(TyBuilder::discr_ty(), current, MirSpan::Unknown)
+ .expect("discr_ty is never unsized")
+ .into();
self.discr_temp = Some(tmp.clone());
tmp
}
@@ -1266,19 +1416,34 @@ impl MirLowerCtx<'_> {
fn lower_loop(
&mut self,
prev_block: BasicBlockId,
+ place: Place,
label: Option<LabelId>,
+ span: MirSpan,
f: impl FnOnce(&mut MirLowerCtx<'_>, BasicBlockId) -> Result<()>,
) -> Result<Option<BasicBlockId>> {
- if label.is_some() {
- not_supported!("loop with label");
- }
let begin = self.new_basic_block();
- let prev =
- mem::replace(&mut self.current_loop_blocks, Some(LoopBlocks { begin, end: None }));
- self.set_goto(prev_block, begin);
+ let prev = mem::replace(
+ &mut self.current_loop_blocks,
+ Some(LoopBlocks { begin, end: None, place, drop_scope_index: self.drop_scopes.len() }),
+ );
+ let prev_label = if let Some(label) = label {
+ // We should generate the end now, to make sure that it wouldn't change later. It is
+ // bad as we may emit end (unnecessary unreachable block) for unterminating loop, but
+ // it should not affect correctness.
+ self.current_loop_end()?;
+ self.labeled_loop_blocks
+ .insert(label, self.current_loop_blocks.as_ref().unwrap().clone())
+ } else {
+ None
+ };
+ self.set_goto(prev_block, begin, span);
f(self, begin)?;
- let my = mem::replace(&mut self.current_loop_blocks, prev)
- .ok_or(MirLowerError::ImplementationError("current_loop_blocks is corrupt"))?;
+ let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or(
+ MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()),
+ )?;
+ if let Some(prev) = prev_label {
+ self.labeled_loop_blocks.insert(label.unwrap(), prev);
+ }
Ok(my.end)
}
@@ -1290,14 +1455,15 @@ impl MirLowerCtx<'_> {
&mut self,
b1: Option<BasicBlockId>,
b2: Option<BasicBlockId>,
+ span: MirSpan,
) -> Option<BasicBlockId> {
match (b1, b2) {
(None, None) => None,
(None, Some(b)) | (Some(b), None) => Some(b),
(Some(b1), Some(b2)) => {
let bm = self.new_basic_block();
- self.set_goto(b1, bm);
- self.set_goto(b2, bm);
+ self.set_goto(b1, bm, span);
+ self.set_goto(b2, bm, span);
Some(bm)
}
}
@@ -1307,7 +1473,9 @@ impl MirLowerCtx<'_> {
let r = match self
.current_loop_blocks
.as_mut()
- .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
+ .ok_or(MirLowerError::ImplementationError(
+ "Current loop access out of loop".to_string(),
+ ))?
.end
{
Some(x) => x,
@@ -1315,7 +1483,9 @@ impl MirLowerCtx<'_> {
let s = self.new_basic_block();
self.current_loop_blocks
.as_mut()
- .ok_or(MirLowerError::ImplementationError("Current loop access out of loop"))?
+ .ok_or(MirLowerError::ImplementationError(
+ "Current loop access out of loop".to_string(),
+ ))?
.end = Some(s);
s
}
@@ -1327,36 +1497,28 @@ impl MirLowerCtx<'_> {
is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db)
}
- /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in
- /// the appropriated places.
- fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) {
- // Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break
- // and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in
- // the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
- // allow this:
- //
- // ```
- // let x;
- // loop {
- // let y = 2;
- // x = &y;
- // if some_condition {
- // break; // we need to add a StorageDead(y) above this to kill the x borrow
- // }
- // }
- // use(x)
- // ```
- // But I think this approach work for mutability analysis, as user can't write code which mutates a binding
- // after StorageDead, except loops, which are handled by this hack.
+ /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and
+ /// `Drop` in the appropriated places.
+ fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> {
let span = self.body.bindings[b]
.definitions
.first()
.copied()
.map(MirSpan::PatId)
.unwrap_or(MirSpan::Unknown);
- let l = self.result.binding_locals[b];
- self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
+ let l = self.binding_local(b)?;
+ self.push_storage_live_for_local(l, current, span)
+ }
+
+ fn push_storage_live_for_local(
+ &mut self,
+ l: LocalId,
+ current: BasicBlockId,
+ span: MirSpan,
+ ) -> Result<()> {
+ self.drop_scopes.last_mut().unwrap().locals.push(l);
self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
+ Ok(())
}
fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
@@ -1366,81 +1528,204 @@ impl MirLowerCtx<'_> {
fn lower_block_to_place(
&mut self,
- label: Option<LabelId>,
- statements: &[hir_def::expr::Statement],
+ statements: &[hir_def::hir::Statement],
mut current: BasicBlockId,
tail: Option<ExprId>,
place: Place,
+ span: MirSpan,
) -> Result<Option<Idx<BasicBlock>>> {
- if label.is_some() {
- not_supported!("block with label");
- }
+ let scope = self.push_drop_scope();
for statement in statements.iter() {
match statement {
- hir_def::expr::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
+ hir_def::hir::Statement::Let { pat, initializer, else_branch, type_ref: _ } => {
if let Some(expr_id) = initializer {
let else_block;
let Some((init_place, c)) =
self.lower_expr_as_place(current, *expr_id, true)?
else {
+ scope.pop_assume_dropped(self);
return Ok(None);
};
current = c;
- (current, else_block) = self.pattern_match(
- current,
- None,
- init_place,
- self.expr_ty_after_adjustments(*expr_id),
- *pat,
- BindingAnnotation::Unannotated,
- )?;
+ (current, else_block) =
+ self.pattern_match(current, None, init_place, *pat)?;
match (else_block, else_branch) {
(None, _) => (),
(Some(else_block), None) => {
- self.set_terminator(else_block, Terminator::Unreachable);
+ self.set_terminator(else_block, TerminatorKind::Unreachable, span);
}
(Some(else_block), Some(else_branch)) => {
if let Some((_, b)) =
self.lower_expr_as_place(else_block, *else_branch, true)?
{
- self.set_terminator(b, Terminator::Unreachable);
+ self.set_terminator(b, TerminatorKind::Unreachable, span);
}
}
}
} else {
+ let mut err = None;
self.body.walk_bindings_in_pat(*pat, |b| {
- self.push_storage_live(b, current);
+ if let Err(e) = self.push_storage_live(b, current) {
+ err = Some(e);
+ }
});
+ if let Some(e) = err {
+ return Err(e);
+ }
}
}
- hir_def::expr::Statement::Expr { expr, has_semi: _ } => {
+ hir_def::hir::Statement::Expr { expr, has_semi: _ } => {
+ let scope2 = self.push_drop_scope();
let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else {
+ scope2.pop_assume_dropped(self);
+ scope.pop_assume_dropped(self);
return Ok(None);
};
- current = c;
+ current = scope2.pop_and_drop(self, c);
}
}
}
- match tail {
- Some(tail) => self.lower_expr_to_place(tail, place, current),
- None => Ok(Some(current)),
+ if let Some(tail) = tail {
+ let Some(c) = self.lower_expr_to_place(tail, place, current)? else {
+ scope.pop_assume_dropped(self);
+ return Ok(None);
+ };
+ current = c;
}
+ current = scope.pop_and_drop(self, current);
+ Ok(Some(current))
}
-}
-fn pattern_matching_dereference(
- cond_ty: &mut Ty,
- binding_mode: &mut BindingAnnotation,
- cond_place: &mut Place,
-) {
- while let Some((ty, _, mu)) = cond_ty.as_reference() {
- if mu == Mutability::Mut && *binding_mode != BindingAnnotation::Ref {
- *binding_mode = BindingAnnotation::RefMut;
- } else {
- *binding_mode = BindingAnnotation::Ref;
+ fn lower_params_and_bindings(
+ &mut self,
+ params: impl Iterator<Item = (PatId, Ty)> + Clone,
+ pick_binding: impl Fn(BindingId) -> bool,
+ ) -> Result<BasicBlockId> {
+ let base_param_count = self.result.param_locals.len();
+ self.result.param_locals.extend(params.clone().map(|(x, ty)| {
+ let local_id = self.result.locals.alloc(Local { ty });
+ self.drop_scopes.last_mut().unwrap().locals.push(local_id);
+ if let Pat::Bind { id, subpat: None } = self.body[x] {
+ if matches!(
+ self.body.bindings[id].mode,
+ BindingAnnotation::Unannotated | BindingAnnotation::Mutable
+ ) {
+ self.result.binding_locals.insert(id, local_id);
+ }
+ }
+ local_id
+ }));
+ // and then rest of bindings
+ for (id, _) in self.body.bindings.iter() {
+ if !pick_binding(id) {
+ continue;
+ }
+ if !self.result.binding_locals.contains_idx(id) {
+ self.result
+ .binding_locals
+ .insert(id, self.result.locals.alloc(Local { ty: self.infer[id].clone() }));
+ }
+ }
+ let mut current = self.result.start_block;
+ for ((param, _), local) in
+ params.zip(self.result.param_locals.clone().into_iter().skip(base_param_count))
+ {
+ if let Pat::Bind { id, .. } = self.body[param] {
+ if local == self.binding_local(id)? {
+ continue;
+ }
+ }
+ let r = self.pattern_match(current, None, local.into(), param)?;
+ if let Some(b) = r.1 {
+ self.set_terminator(b, TerminatorKind::Unreachable, param.into());
+ }
+ current = r.0;
+ }
+ Ok(current)
+ }
+
+ fn binding_local(&self, b: BindingId) -> Result<LocalId> {
+ match self.result.binding_locals.get(b) {
+ Some(x) => Ok(*x),
+ None => {
+ // FIXME: It should never happens, but currently it will happen in `const_dependent_on_local` test, which
+ // is a hir lowering problem IMO.
+ // never!("Using unaccessable local for binding is always a bug");
+ Err(MirLowerError::UnaccessableLocal)
+ }
+ }
+ }
+
+ fn const_eval_discriminant(&self, variant: EnumVariantId) -> Result<i128> {
+ let r = self.db.const_eval_discriminant(variant);
+ match r {
+ Ok(r) => Ok(r),
+ Err(e) => {
+ let data = self.db.enum_data(variant.parent);
+ let name = format!(
+ "{}::{}",
+ data.name.display(self.db.upcast()),
+ data.variants[variant.local_id].name.display(self.db.upcast())
+ );
+ Err(MirLowerError::ConstEvalError(name, Box::new(e)))
+ }
+ }
+ }
+
+ fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId {
+ for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() {
+ self.emit_drop_and_storage_dead_for_scope(scope, &mut current);
+ }
+ current
+ }
+
+ fn push_drop_scope(&mut self) -> DropScopeToken {
+ self.drop_scopes.push(DropScope::default());
+ DropScopeToken
+ }
+
+ /// Don't call directly
+ fn pop_drop_scope_assume_dropped_internal(&mut self) {
+ self.drop_scopes.pop();
+ }
+
+ /// Don't call directly
+ fn pop_drop_scope_internal(&mut self, mut current: BasicBlockId) -> BasicBlockId {
+ let scope = self.drop_scopes.pop().unwrap();
+ self.emit_drop_and_storage_dead_for_scope(&scope, &mut current);
+ current
+ }
+
+ fn pop_drop_scope_assert_finished(
+ &mut self,
+ mut current: BasicBlockId,
+ ) -> Result<BasicBlockId> {
+ current = self.pop_drop_scope_internal(current);
+ if !self.drop_scopes.is_empty() {
+ implementation_error!("Mismatched count between drop scope push and pops");
+ }
+ Ok(current)
+ }
+
+ fn emit_drop_and_storage_dead_for_scope(
+ &mut self,
+ scope: &DropScope,
+ current: &mut Idx<BasicBlock>,
+ ) {
+ for &l in scope.locals.iter().rev() {
+ if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) {
+ let prev = std::mem::replace(current, self.new_basic_block());
+ self.set_terminator(
+ prev,
+ TerminatorKind::Drop { place: l.into(), target: *current, unwind: None },
+ MirSpan::Unknown,
+ );
+ }
+ self.push_statement(
+ *current,
+ StatementKind::StorageDead(l).with_span(MirSpan::Unknown),
+ );
}
- *cond_ty = ty.clone();
- cond_place.projection.push(ProjectionElem::Deref);
}
}
@@ -1452,6 +1737,26 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
(_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat,
(_, _) => CastKind::IntToInt,
},
+ (TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress,
+ (TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress,
+ (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => {
+ CastKind::Pointer(if a == b {
+ PointerCast::MutToConstPointer
+ } else if matches!(a.kind(Interner), TyKind::Slice(_) | TyKind::Str)
+ && matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Str)
+ {
+ // slice to slice cast is no-op (metadata is not touched), so we use this
+ PointerCast::MutToConstPointer
+ } else if matches!(b.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) {
+ PointerCast::Unsize
+ } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) {
+ PointerCast::ArrayToPointer
+ } else {
+ // cast between two sized pointer, like *const i32 to *const i8. There is no specific variant
+ // for it in `PointerCast` so we use `MutToConstPointer`
+ PointerCast::MutToConstPointer
+ })
+ }
// Enum to int casts
(TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
CastKind::IntToInt
@@ -1460,20 +1765,123 @@ fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
})
}
+pub fn mir_body_for_closure_query(
+ db: &dyn HirDatabase,
+ closure: ClosureId,
+) -> Result<Arc<MirBody>> {
+ let (owner, expr) = db.lookup_intern_closure(closure.into());
+ let body = db.body(owner);
+ let infer = db.infer(owner);
+ let Expr::Closure { args, body: root, .. } = &body[expr] else {
+ implementation_error!("closure expression is not closure");
+ };
+ let TyKind::Closure(_, substs) = &infer[expr].kind(Interner) else {
+ implementation_error!("closure expression is not closure");
+ };
+ let (captures, kind) = infer.closure_info(&closure);
+ let mut ctx = MirLowerCtx::new(db, owner, &body, &infer);
+ // 0 is return local
+ ctx.result.locals.alloc(Local { ty: infer[*root].clone() });
+ let closure_local = ctx.result.locals.alloc(Local {
+ ty: match kind {
+ FnTrait::FnOnce => infer[expr].clone(),
+ FnTrait::FnMut => TyKind::Ref(Mutability::Mut, static_lifetime(), infer[expr].clone())
+ .intern(Interner),
+ FnTrait::Fn => TyKind::Ref(Mutability::Not, static_lifetime(), infer[expr].clone())
+ .intern(Interner),
+ },
+ });
+ ctx.result.param_locals.push(closure_local);
+ let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else {
+ implementation_error!("closure has not callable sig");
+ };
+ let current = ctx.lower_params_and_bindings(
+ args.iter().zip(sig.params().iter()).map(|(x, y)| (*x, y.clone())),
+ |_| true,
+ )?;
+ if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
+ let current = ctx.pop_drop_scope_assert_finished(current)?;
+ ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
+ }
+ let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
+ for (i, capture) in captures.iter().enumerate() {
+ let local = ctx.binding_local(capture.place.local)?;
+ upvar_map.entry(local).or_default().push((capture, i));
+ }
+ let mut err = None;
+ let closure_local = ctx.result.locals.iter().nth(1).unwrap().0;
+ let closure_projection = match kind {
+ FnTrait::FnOnce => vec![],
+ FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref],
+ };
+ ctx.result.walk_places(|p| {
+ if let Some(x) = upvar_map.get(&p.local) {
+ let r = x.iter().find(|x| {
+ if p.projection.len() < x.0.place.projections.len() {
+ return false;
+ }
+ for (x, y) in p.projection.iter().zip(x.0.place.projections.iter()) {
+ match (x, y) {
+ (ProjectionElem::Deref, ProjectionElem::Deref) => (),
+ (ProjectionElem::Field(x), ProjectionElem::Field(y)) if x == y => (),
+ (
+ ProjectionElem::TupleOrClosureField(x),
+ ProjectionElem::TupleOrClosureField(y),
+ ) if x == y => (),
+ _ => return false,
+ }
+ }
+ true
+ });
+ match r {
+ Some(x) => {
+ p.local = closure_local;
+ let mut next_projs = closure_projection.clone();
+ next_projs.push(PlaceElem::TupleOrClosureField(x.1));
+ let prev_projs = mem::take(&mut p.projection);
+ if x.0.kind != CaptureKind::ByValue {
+ next_projs.push(ProjectionElem::Deref);
+ }
+ next_projs.extend(prev_projs.iter().cloned().skip(x.0.place.projections.len()));
+ p.projection = next_projs.into();
+ }
+ None => err = Some(p.clone()),
+ }
+ }
+ });
+ ctx.result.binding_locals = ctx
+ .result
+ .binding_locals
+ .into_iter()
+ .filter(|it| ctx.body.binding_owners.get(&it.0).copied() == Some(expr))
+ .collect();
+ if let Some(err) = err {
+ return Err(MirLowerError::UnresolvedUpvar(err));
+ }
+ ctx.result.shrink_to_fit();
+ Ok(Arc::new(ctx.result))
+}
+
pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<MirBody>> {
let _p = profile::span("mir_body_query").detail(|| match def {
- DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(),
- DefWithBodyId::StaticId(it) => db.static_data(it).name.clone().to_string(),
- DefWithBodyId::ConstId(it) => {
- db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
- }
+ DefWithBodyId::FunctionId(it) => db.function_data(it).name.display(db.upcast()).to_string(),
+ DefWithBodyId::StaticId(it) => db.static_data(it).name.display(db.upcast()).to_string(),
+ DefWithBodyId::ConstId(it) => db
+ .const_data(it)
+ .name
+ .clone()
+ .unwrap_or_else(Name::missing)
+ .display(db.upcast())
+ .to_string(),
DefWithBodyId::VariantId(it) => {
- db.enum_data(it.parent).variants[it.local_id].name.to_string()
+ db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast()).to_string()
}
+ DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"),
});
let body = db.body(def);
let infer = db.infer(def);
- let result = lower_to_mir(db, def, &body, &infer, body.body_expr)?;
+ let mut result = lower_to_mir(db, def, &body, &infer, body.body_expr)?;
+ result.shrink_to_fit();
Ok(Arc::new(result))
}
@@ -1497,85 +1905,40 @@ pub fn lower_to_mir(
if let Some((_, x)) = infer.type_mismatches().next() {
return Err(MirLowerError::TypeMismatch(x.clone()));
}
- let mut basic_blocks = Arena::new();
- let start_block =
- basic_blocks.alloc(BasicBlock { statements: vec![], terminator: None, is_cleanup: false });
- let mut locals = Arena::new();
+ let mut ctx = MirLowerCtx::new(db, owner, body, infer);
// 0 is return local
- locals.alloc(Local { ty: infer[root_expr].clone() });
- let mut binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
- // 1 to param_len is for params
- let param_locals: Vec<LocalId> = if let DefWithBodyId::FunctionId(fid) = owner {
- let substs = TyBuilder::placeholder_subst(db, fid);
- let callable_sig = db.callable_item_signature(fid.into()).substitute(Interner, &substs);
- body.params
- .iter()
- .zip(callable_sig.params().iter())
- .map(|(&x, ty)| {
- let local_id = locals.alloc(Local { ty: ty.clone() });
- if let Pat::Bind { id, subpat: None } = body[x] {
- if matches!(
- body.bindings[id].mode,
- BindingAnnotation::Unannotated | BindingAnnotation::Mutable
- ) {
- binding_locals.insert(id, local_id);
- }
- }
- local_id
- })
- .collect()
- } else {
- if !body.params.is_empty() {
- return Err(MirLowerError::TypeError("Unexpected parameter for non function body"));
- }
- vec![]
- };
- // and then rest of bindings
- for (id, _) in body.bindings.iter() {
- if !binding_locals.contains_idx(id) {
- binding_locals.insert(id, locals.alloc(Local { ty: infer[id].clone() }));
+ ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) });
+ let binding_picker = |b: BindingId| {
+ let owner = ctx.body.binding_owners.get(&b).copied();
+ if root_expr == body.body_expr {
+ owner.is_none()
+ } else {
+ owner == Some(root_expr)
}
- }
- let mir = MirBody {
- basic_blocks,
- locals,
- start_block,
- binding_locals,
- param_locals,
- owner,
- arg_count: body.params.len(),
- };
- let mut ctx = MirLowerCtx {
- result: mir,
- db,
- infer,
- body,
- owner,
- current_loop_blocks: None,
- discr_temp: None,
};
- let mut current = start_block;
- for (&param, local) in body.params.iter().zip(ctx.result.param_locals.clone().into_iter()) {
- if let Pat::Bind { id, .. } = body[param] {
- if local == ctx.result.binding_locals[id] {
- continue;
+ // 1 to param_len is for params
+ // FIXME: replace with let chain once it becomes stable
+ let current = 'b: {
+ if body.body_expr == root_expr {
+ // otherwise it's an inline const, and has no parameter
+ if let DefWithBodyId::FunctionId(fid) = owner {
+ let substs = TyBuilder::placeholder_subst(db, fid);
+ let callable_sig =
+ db.callable_item_signature(fid.into()).substitute(Interner, &substs);
+ break 'b ctx.lower_params_and_bindings(
+ body.params
+ .iter()
+ .zip(callable_sig.params().iter())
+ .map(|(x, y)| (*x, y.clone())),
+ binding_picker,
+ )?;
}
}
- let r = ctx.pattern_match(
- current,
- None,
- local.into(),
- ctx.result.locals[local].ty.clone(),
- param,
- BindingAnnotation::Unannotated,
- )?;
- if let Some(b) = r.1 {
- ctx.set_terminator(b, Terminator::Unreachable);
- }
- current = r.0;
- }
- if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
- ctx.result.basic_blocks[b].terminator = Some(Terminator::Return);
+ ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
+ };
+ if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
+ let current = ctx.pop_drop_scope_assert_finished(current)?;
+ ctx.set_terminator(current, TerminatorKind::Return, root_expr.into());
}
Ok(ctx.result)
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
index fe8147dcd..d2c8d9a08 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
@@ -1,6 +1,7 @@
//! MIR lowering for places
use super::*;
+use hir_def::{lang_item::lang_attr, FunctionId};
use hir_expand::name;
macro_rules! not_supported {
@@ -15,8 +16,8 @@ impl MirLowerCtx<'_> {
expr_id: ExprId,
prev_block: BasicBlockId,
) -> Result<Option<(Place, BasicBlockId)>> {
- let ty = self.expr_ty(expr_id);
- let place = self.temp(ty)?;
+ let ty = self.expr_ty_without_adjust(expr_id);
+ let place = self.temp(ty, prev_block, expr_id.into())?;
let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else {
return Ok(None);
};
@@ -29,9 +30,11 @@ impl MirLowerCtx<'_> {
prev_block: BasicBlockId,
adjustments: &[Adjustment],
) -> Result<Option<(Place, BasicBlockId)>> {
- let ty =
- adjustments.last().map(|x| x.target.clone()).unwrap_or_else(|| self.expr_ty(expr_id));
- let place = self.temp(ty)?;
+ let ty = adjustments
+ .last()
+ .map(|x| x.target.clone())
+ .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id));
+ let place = self.temp(ty, prev_block, expr_id.into())?;
let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else {
return Ok(None);
};
@@ -62,7 +65,7 @@ impl MirLowerCtx<'_> {
)? else {
return Ok(None);
};
- x.0.projection.push(ProjectionElem::Deref);
+ x.0 = x.0.project(ProjectionElem::Deref);
Ok(Some(x))
}
Adjust::Deref(Some(od)) => {
@@ -79,7 +82,7 @@ impl MirLowerCtx<'_> {
r,
rest.last()
.map(|x| x.target.clone())
- .unwrap_or_else(|| self.expr_ty(expr_id)),
+ .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)),
last.target.clone(),
expr_id.into(),
match od.0 {
@@ -125,35 +128,74 @@ impl MirLowerCtx<'_> {
match &self.body.exprs[expr_id] {
Expr::Path(p) => {
let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id);
- let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p.mod_path()) else {
- return Err(MirLowerError::unresolved_path(self.db, p));
- };
- let pr = match pr {
- ResolveValueResult::ValueNs(v) => v,
- ResolveValueResult::Partial(..) => return try_rvalue(self),
+ let Some(pr) = resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p) else {
+ return try_rvalue(self);
};
match pr {
ValueNs::LocalBinding(pat_id) => {
- Ok(Some((self.result.binding_locals[pat_id].into(), current)))
+ Ok(Some((self.binding_local(pat_id)?.into(), current)))
+ }
+ ValueNs::StaticId(s) => {
+ let ty = self.expr_ty_without_adjust(expr_id);
+ let ref_ty =
+ TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner);
+ let temp: Place = self.temp(ref_ty, current, expr_id.into())?.into();
+ self.push_assignment(
+ current,
+ temp.clone(),
+ Operand::Static(s).into(),
+ expr_id.into(),
+ );
+ Ok(Some((temp.project(ProjectionElem::Deref), current)))
}
_ => try_rvalue(self),
}
}
Expr::UnaryOp { expr, op } => match op {
- hir_def::expr::UnaryOp::Deref => {
- if !matches!(
- self.expr_ty(*expr).kind(Interner),
- TyKind::Ref(..) | TyKind::Raw(..)
- ) {
- let Some(_) = self.lower_expr_as_place(current, *expr, true)? else {
+ hir_def::hir::UnaryOp::Deref => {
+ let is_builtin = match self.expr_ty_without_adjust(*expr).kind(Interner) {
+ TyKind::Ref(..) | TyKind::Raw(..) => true,
+ TyKind::Adt(id, _) => {
+ if let Some(lang_item) = lang_attr(self.db.upcast(), id.0) {
+ lang_item == LangItem::OwnedBox
+ } else {
+ false
+ }
+ }
+ _ => false,
+ };
+ if !is_builtin {
+ let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None);
};
- not_supported!("explicit overloaded deref");
+ return self.lower_overloaded_deref(
+ current,
+ p,
+ self.expr_ty_after_adjustments(*expr),
+ self.expr_ty_without_adjust(expr_id),
+ expr_id.into(),
+ 'b: {
+ if let Some((f, _)) = self.infer.method_resolution(expr_id) {
+ if let Some(deref_trait) =
+ self.resolve_lang_item(LangItem::DerefMut)?.as_trait()
+ {
+ if let Some(deref_fn) = self
+ .db
+ .trait_data(deref_trait)
+ .method_by_name(&name![deref_mut])
+ {
+ break 'b deref_fn == f;
+ }
+ }
+ }
+ false
+ },
+ );
}
let Some((mut r, current)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None);
};
- r.projection.push(ProjectionElem::Deref);
+ r = r.project(ProjectionElem::Deref);
Ok(Some((r, current)))
}
_ => try_rvalue(self),
@@ -169,25 +211,84 @@ impl MirLowerCtx<'_> {
let base_ty = self.expr_ty_after_adjustments(*base);
let index_ty = self.expr_ty_after_adjustments(*index);
if index_ty != TyBuilder::usize()
- || !matches!(base_ty.kind(Interner), TyKind::Array(..) | TyKind::Slice(..))
+ || !matches!(
+ base_ty.strip_reference().kind(Interner),
+ TyKind::Array(..) | TyKind::Slice(..)
+ )
{
- not_supported!("overloaded index");
+ let Some(index_fn) = self.infer.method_resolution(expr_id) else {
+ return Err(MirLowerError::UnresolvedMethod("[overloaded index]".to_string()));
+ };
+ let Some((base_place, current)) = self.lower_expr_as_place(current, *base, true)? else {
+ return Ok(None);
+ };
+ let Some((index_operand, current)) = self.lower_expr_to_some_operand(*index, current)? else {
+ return Ok(None);
+ };
+ return self.lower_overloaded_index(
+ current,
+ base_place,
+ base_ty,
+ self.expr_ty_without_adjust(expr_id),
+ index_operand,
+ expr_id.into(),
+ index_fn,
+ );
}
+ let adjusts = self
+ .infer
+ .expr_adjustments
+ .get(base)
+ .and_then(|x| x.split_last())
+ .map(|x| x.1)
+ .unwrap_or(&[]);
let Some((mut p_base, current)) =
- self.lower_expr_as_place(current, *base, true)? else {
+ self.lower_expr_as_place_with_adjust(current, *base, true, adjusts)?
+ else {
return Ok(None);
};
- let l_index = self.temp(self.expr_ty_after_adjustments(*index))?;
+ let l_index =
+ self.temp(self.expr_ty_after_adjustments(*index), current, expr_id.into())?;
let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else {
return Ok(None);
};
- p_base.projection.push(ProjectionElem::Index(l_index));
+ p_base = p_base.project(ProjectionElem::Index(l_index));
Ok(Some((p_base, current)))
}
_ => try_rvalue(self),
}
}
+ fn lower_overloaded_index(
+ &mut self,
+ current: BasicBlockId,
+ place: Place,
+ base_ty: Ty,
+ result_ty: Ty,
+ index_operand: Operand,
+ span: MirSpan,
+ index_fn: (FunctionId, Substitution),
+ ) -> Result<Option<(Place, BasicBlockId)>> {
+ let mutability = match base_ty.as_reference() {
+ Some((_, _, mutability)) => mutability,
+ None => Mutability::Not,
+ };
+ let result_ref = TyKind::Ref(mutability, static_lifetime(), result_ty).intern(Interner);
+ let mut result: Place = self.temp(result_ref, current, span)?.into();
+ let index_fn_op = Operand::const_zst(
+ TyKind::FnDef(
+ self.db.intern_callable_def(CallableDefId::FunctionId(index_fn.0)).into(),
+ index_fn.1,
+ )
+ .intern(Interner),
+ );
+ let Some(current) = self.lower_call(index_fn_op, Box::new([Operand::Copy(place), index_operand]), result.clone(), current, false, span)? else {
+ return Ok(None);
+ };
+ result = result.project(ProjectionElem::Deref);
+ Ok(Some((result, current)))
+ }
+
fn lower_overloaded_deref(
&mut self,
current: BasicBlockId,
@@ -209,7 +310,7 @@ impl MirLowerCtx<'_> {
};
let ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), source_ty.clone()).intern(Interner);
let target_ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), target_ty).intern(Interner);
- let ref_place: Place = self.temp(ty_ref)?.into();
+ let ref_place: Place = self.temp(ty_ref, current, span)?.into();
self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span);
let deref_trait = self
.resolve_lang_item(trait_lang_item)?
@@ -227,11 +328,11 @@ impl MirLowerCtx<'_> {
)
.intern(Interner),
);
- let mut result: Place = self.temp(target_ty_ref)?.into();
- let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false)? else {
+ let mut result: Place = self.temp(target_ty_ref, current, span)?.into();
+ let Some(current) = self.lower_call(deref_fn_op, Box::new([Operand::Copy(ref_place)]), result.clone(), current, false, span)? else {
return Ok(None);
};
- result.projection.push(ProjectionElem::Deref);
+ result = result.project(ProjectionElem::Deref);
Ok(Some((result, current)))
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
new file mode 100644
index 000000000..ff43c64a9
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -0,0 +1,617 @@
+//! MIR lowering for patterns
+
+use hir_def::{hir::LiteralOrConst, resolver::HasResolver, AssocItemId};
+
+use crate::BindingMode;
+
+use super::*;
+
+macro_rules! not_supported {
+ ($x: expr) => {
+ return Err(MirLowerError::NotSupported(format!($x)))
+ };
+}
+
+pub(super) enum AdtPatternShape<'a> {
+ Tuple { args: &'a [PatId], ellipsis: Option<usize> },
+ Record { args: &'a [RecordFieldPat] },
+ Unit,
+}
+
+/// We need to do pattern matching in two phases: One to check if the pattern matches, and one to fill the bindings
+/// of patterns. This is necessary to prevent double moves and similar problems. For example:
+/// ```ignore
+/// struct X;
+/// match (X, 3) {
+/// (b, 2) | (b, 3) => {},
+/// _ => {}
+/// }
+/// ```
+/// If we do everything in one pass, we will move `X` to the first `b`, then we see that the second field of tuple
+/// doesn't match and we should move the `X` to the second `b` (which here is the same thing, but doesn't need to be) and
+/// it might even doesn't match the second pattern and we may want to not move `X` at all.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum MatchingMode {
+ /// Check that if this pattern matches
+ Check,
+ /// Assume that this pattern matches, fill bindings
+ Bind,
+}
+
+impl MirLowerCtx<'_> {
+ /// It gets a `current` unterminated block, appends some statements and possibly a terminator to it to check if
+ /// the pattern matches and write bindings, and returns two unterminated blocks, one for the matched path (which
+ /// can be the `current` block) and one for the mismatched path. If the input pattern is irrefutable, the
+ /// mismatched path block is `None`.
+ ///
+ /// By default, it will create a new block for mismatched path. If you already have one, you can provide it with
+ /// `current_else` argument to save an unnecessary jump. If `current_else` isn't `None`, the result mismatched path
+ /// wouldn't be `None` as well. Note that this function will add jumps to the beginning of the `current_else` block,
+ /// so it should be an empty block.
+ pub(super) fn pattern_match(
+ &mut self,
+ current: BasicBlockId,
+ current_else: Option<BasicBlockId>,
+ cond_place: Place,
+ pattern: PatId,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ let (current, current_else) = self.pattern_match_inner(
+ current,
+ current_else,
+ cond_place.clone(),
+ pattern,
+ MatchingMode::Check,
+ )?;
+ let (current, current_else) = self.pattern_match_inner(
+ current,
+ current_else,
+ cond_place,
+ pattern,
+ MatchingMode::Bind,
+ )?;
+ Ok((current, current_else))
+ }
+
+ fn pattern_match_inner(
+ &mut self,
+ mut current: BasicBlockId,
+ mut current_else: Option<BasicBlockId>,
+ mut cond_place: Place,
+ pattern: PatId,
+ mode: MatchingMode,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default();
+ cond_place.projection = cond_place
+ .projection
+ .iter()
+ .cloned()
+ .chain((0..cnt).map(|_| ProjectionElem::Deref))
+ .collect::<Vec<_>>()
+ .into();
+ Ok(match &self.body.pats[pattern] {
+ Pat::Missing => return Err(MirLowerError::IncompletePattern),
+ Pat::Wild => (current, current_else),
+ Pat::Tuple { args, ellipsis } => {
+ let subst = match self.infer[pattern].kind(Interner) {
+ TyKind::Tuple(_, s) => s,
+ _ => {
+ return Err(MirLowerError::TypeError(
+ "non tuple type matched with tuple pattern",
+ ))
+ }
+ };
+ self.pattern_match_tuple_like(
+ current,
+ current_else,
+ args,
+ *ellipsis,
+ (0..subst.len(Interner)).map(|i| PlaceElem::TupleOrClosureField(i)),
+ &(&mut cond_place),
+ mode,
+ )?
+ }
+ Pat::Or(pats) => {
+ let then_target = self.new_basic_block();
+ let mut finished = false;
+ for pat in &**pats {
+ let (mut next, next_else) = self.pattern_match_inner(
+ current,
+ None,
+ (&mut cond_place).clone(),
+ *pat,
+ MatchingMode::Check,
+ )?;
+ if mode == MatchingMode::Bind {
+ (next, _) = self.pattern_match_inner(
+ next,
+ None,
+ (&mut cond_place).clone(),
+ *pat,
+ MatchingMode::Bind,
+ )?;
+ }
+ self.set_goto(next, then_target, pattern.into());
+ match next_else {
+ Some(t) => {
+ current = t;
+ }
+ None => {
+ finished = true;
+ break;
+ }
+ }
+ }
+ if !finished {
+ if mode == MatchingMode::Bind {
+ self.set_terminator(current, TerminatorKind::Unreachable, pattern.into());
+ } else {
+ let ce = *current_else.get_or_insert_with(|| self.new_basic_block());
+ self.set_goto(current, ce, pattern.into());
+ }
+ }
+ (then_target, current_else)
+ }
+ Pat::Record { args, .. } => {
+ let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
+ not_supported!("unresolved variant for record");
+ };
+ self.pattern_matching_variant(
+ cond_place,
+ variant,
+ current,
+ pattern.into(),
+ current_else,
+ AdtPatternShape::Record { args: &*args },
+ mode,
+ )?
+ }
+ Pat::Range { start, end } => {
+ let mut add_check = |l: &LiteralOrConst, binop| -> Result<()> {
+ let lv =
+ self.lower_literal_or_const_to_operand(self.infer[pattern].clone(), l)?;
+ let else_target = *current_else.get_or_insert_with(|| self.new_basic_block());
+ let next = self.new_basic_block();
+ let discr: Place =
+ self.temp(TyBuilder::bool(), current, pattern.into())?.into();
+ self.push_assignment(
+ current,
+ discr.clone(),
+ Rvalue::CheckedBinaryOp(
+ binop,
+ lv,
+ Operand::Copy((&mut cond_place).clone()),
+ ),
+ pattern.into(),
+ );
+ let discr = Operand::Copy(discr);
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr,
+ targets: SwitchTargets::static_if(1, next, else_target),
+ },
+ pattern.into(),
+ );
+ current = next;
+ Ok(())
+ };
+ if mode == MatchingMode::Check {
+ if let Some(start) = start {
+ add_check(start, BinOp::Le)?;
+ }
+ if let Some(end) = end {
+ add_check(end, BinOp::Ge)?;
+ }
+ }
+ (current, current_else)
+ }
+ Pat::Slice { prefix, slice, suffix } => {
+ if mode == MatchingMode::Check {
+ // emit runtime length check for slice
+ if let TyKind::Slice(_) = self.infer[pattern].kind(Interner) {
+ let pattern_len = prefix.len() + suffix.len();
+ let place_len: Place =
+ self.temp(TyBuilder::usize(), current, pattern.into())?.into();
+ self.push_assignment(
+ current,
+ place_len.clone(),
+ Rvalue::Len((&mut cond_place).clone()),
+ pattern.into(),
+ );
+ let else_target =
+ *current_else.get_or_insert_with(|| self.new_basic_block());
+ let next = self.new_basic_block();
+ if slice.is_none() {
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr: Operand::Copy(place_len),
+ targets: SwitchTargets::static_if(
+ pattern_len as u128,
+ next,
+ else_target,
+ ),
+ },
+ pattern.into(),
+ );
+ } else {
+ let c = Operand::from_concrete_const(
+ pattern_len.to_le_bytes().to_vec(),
+ MemoryMap::default(),
+ TyBuilder::usize(),
+ );
+ let discr: Place =
+ self.temp(TyBuilder::bool(), current, pattern.into())?.into();
+ self.push_assignment(
+ current,
+ discr.clone(),
+ Rvalue::CheckedBinaryOp(BinOp::Le, c, Operand::Copy(place_len)),
+ pattern.into(),
+ );
+ let discr = Operand::Copy(discr);
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr,
+ targets: SwitchTargets::static_if(1, next, else_target),
+ },
+ pattern.into(),
+ );
+ }
+ current = next;
+ }
+ }
+ for (i, &pat) in prefix.iter().enumerate() {
+ let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex {
+ offset: i as u64,
+ from_end: false,
+ });
+ (current, current_else) =
+ self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
+ }
+ if let Some(slice) = slice {
+ if mode == MatchingMode::Bind {
+ if let Pat::Bind { id, subpat: _ } = self.body[*slice] {
+ let next_place = (&mut cond_place).project(ProjectionElem::Subslice {
+ from: prefix.len() as u64,
+ to: suffix.len() as u64,
+ });
+ (current, current_else) = self.pattern_match_binding(
+ id,
+ next_place,
+ (*slice).into(),
+ current,
+ current_else,
+ )?;
+ }
+ }
+ }
+ for (i, &pat) in suffix.iter().enumerate() {
+ let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex {
+ offset: i as u64,
+ from_end: true,
+ });
+ (current, current_else) =
+ self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
+ }
+ (current, current_else)
+ }
+ Pat::Path(p) => match self.infer.variant_resolution_for_pat(pattern) {
+ Some(variant) => self.pattern_matching_variant(
+ cond_place,
+ variant,
+ current,
+ pattern.into(),
+ current_else,
+ AdtPatternShape::Unit,
+ mode,
+ )?,
+ None => {
+ let unresolved_name = || MirLowerError::unresolved_path(self.db, p);
+ let resolver = self.owner.resolver(self.db.upcast());
+ let pr = resolver
+ .resolve_path_in_value_ns(self.db.upcast(), p)
+ .ok_or_else(unresolved_name)?;
+ let (c, subst) = 'b: {
+ if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern) {
+ if let AssocItemId::ConstId(c) = x.0 {
+ break 'b (c, x.1);
+ }
+ }
+ if let ResolveValueResult::ValueNs(v) = pr {
+ if let ValueNs::ConstId(c) = v {
+ break 'b (c, Substitution::empty(Interner));
+ }
+ }
+ not_supported!("path in pattern position that is not const or variant")
+ };
+ let tmp: Place =
+ self.temp(self.infer[pattern].clone(), current, pattern.into())?.into();
+ let span = pattern.into();
+ self.lower_const(
+ c.into(),
+ current,
+ tmp.clone(),
+ subst,
+ span,
+ self.infer[pattern].clone(),
+ )?;
+ let tmp2: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into();
+ self.push_assignment(
+ current,
+ tmp2.clone(),
+ Rvalue::CheckedBinaryOp(
+ BinOp::Eq,
+ Operand::Copy(tmp),
+ Operand::Copy(cond_place),
+ ),
+ span,
+ );
+ let next = self.new_basic_block();
+ let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr: Operand::Copy(tmp2),
+ targets: SwitchTargets::static_if(1, next, else_target),
+ },
+ span,
+ );
+ (next, Some(else_target))
+ }
+ },
+ Pat::Lit(l) => match &self.body.exprs[*l] {
+ Expr::Literal(l) => {
+ let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?;
+ if mode == MatchingMode::Check {
+ self.pattern_match_const(current_else, current, c, cond_place, pattern)?
+ } else {
+ (current, current_else)
+ }
+ }
+ _ => not_supported!("expression path literal"),
+ },
+ Pat::Bind { id, subpat } => {
+ if let Some(subpat) = subpat {
+ (current, current_else) = self.pattern_match_inner(
+ current,
+ current_else,
+ (&mut cond_place).clone(),
+ *subpat,
+ mode,
+ )?
+ }
+ if mode == MatchingMode::Bind {
+ self.pattern_match_binding(
+ *id,
+ cond_place,
+ pattern.into(),
+ current,
+ current_else,
+ )?
+ } else {
+ (current, current_else)
+ }
+ }
+ Pat::TupleStruct { path: _, args, ellipsis } => {
+ let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else {
+ not_supported!("unresolved variant");
+ };
+ self.pattern_matching_variant(
+ cond_place,
+ variant,
+ current,
+ pattern.into(),
+ current_else,
+ AdtPatternShape::Tuple { args, ellipsis: *ellipsis },
+ mode,
+ )?
+ }
+ Pat::Ref { pat, mutability: _ } => self.pattern_match_inner(
+ current,
+ current_else,
+ cond_place.project(ProjectionElem::Deref),
+ *pat,
+ mode,
+ )?,
+ Pat::Box { .. } => not_supported!("box pattern"),
+ Pat::ConstBlock(_) => not_supported!("const block pattern"),
+ })
+ }
+
+ fn pattern_match_binding(
+ &mut self,
+ id: BindingId,
+ cond_place: Place,
+ span: MirSpan,
+ current: BasicBlockId,
+ current_else: Option<BasicBlockId>,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ let target_place = self.binding_local(id)?;
+ let mode = self.infer.binding_modes[id];
+ self.push_storage_live(id, current)?;
+ self.push_assignment(
+ current,
+ target_place.into(),
+ match mode {
+ BindingMode::Move => Operand::Copy(cond_place).into(),
+ BindingMode::Ref(Mutability::Not) => Rvalue::Ref(BorrowKind::Shared, cond_place),
+ BindingMode::Ref(Mutability::Mut) => {
+ Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, cond_place)
+ }
+ },
+ span,
+ );
+ Ok((current, current_else))
+ }
+
+ fn pattern_match_const(
+ &mut self,
+ current_else: Option<BasicBlockId>,
+ current: BasicBlockId,
+ c: Operand,
+ cond_place: Place,
+ pattern: Idx<Pat>,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ let then_target = self.new_basic_block();
+ let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
+ let discr: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into();
+ self.push_assignment(
+ current,
+ discr.clone(),
+ Rvalue::CheckedBinaryOp(BinOp::Eq, c, Operand::Copy(cond_place)),
+ pattern.into(),
+ );
+ let discr = Operand::Copy(discr);
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr,
+ targets: SwitchTargets::static_if(1, then_target, else_target),
+ },
+ pattern.into(),
+ );
+ Ok((then_target, Some(else_target)))
+ }
+
+ fn pattern_matching_variant(
+ &mut self,
+ cond_place: Place,
+ variant: VariantId,
+ mut current: BasicBlockId,
+ span: MirSpan,
+ mut current_else: Option<BasicBlockId>,
+ shape: AdtPatternShape<'_>,
+ mode: MatchingMode,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ Ok(match variant {
+ VariantId::EnumVariantId(v) => {
+ if mode == MatchingMode::Check {
+ let e = self.const_eval_discriminant(v)? as u128;
+ let tmp = self.discr_temp_place(current);
+ self.push_assignment(
+ current,
+ tmp.clone(),
+ Rvalue::Discriminant(cond_place.clone()),
+ span,
+ );
+ let next = self.new_basic_block();
+ let else_target = current_else.get_or_insert_with(|| self.new_basic_block());
+ self.set_terminator(
+ current,
+ TerminatorKind::SwitchInt {
+ discr: Operand::Copy(tmp),
+ targets: SwitchTargets::static_if(e, next, *else_target),
+ },
+ span,
+ );
+ current = next;
+ }
+ let enum_data = self.db.enum_data(v.parent);
+ self.pattern_matching_variant_fields(
+ shape,
+ &enum_data.variants[v.local_id].variant_data,
+ variant,
+ current,
+ current_else,
+ &cond_place,
+ mode,
+ )?
+ }
+ VariantId::StructId(s) => {
+ let struct_data = self.db.struct_data(s);
+ self.pattern_matching_variant_fields(
+ shape,
+ &struct_data.variant_data,
+ variant,
+ current,
+ current_else,
+ &cond_place,
+ mode,
+ )?
+ }
+ VariantId::UnionId(_) => {
+ return Err(MirLowerError::TypeError("pattern matching on union"))
+ }
+ })
+ }
+
+ fn pattern_matching_variant_fields(
+ &mut self,
+ shape: AdtPatternShape<'_>,
+ variant_data: &VariantData,
+ v: VariantId,
+ current: BasicBlockId,
+ current_else: Option<BasicBlockId>,
+ cond_place: &Place,
+ mode: MatchingMode,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ Ok(match shape {
+ AdtPatternShape::Record { args } => {
+ let it = args
+ .iter()
+ .map(|x| {
+ let field_id =
+ variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?;
+ Ok((
+ PlaceElem::Field(FieldId { parent: v.into(), local_id: field_id }),
+ x.pat,
+ ))
+ })
+ .collect::<Result<Vec<_>>>()?;
+ self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)?
+ }
+ AdtPatternShape::Tuple { args, ellipsis } => {
+ let fields = variant_data
+ .fields()
+ .iter()
+ .map(|(x, _)| PlaceElem::Field(FieldId { parent: v.into(), local_id: x }));
+ self.pattern_match_tuple_like(
+ current,
+ current_else,
+ args,
+ ellipsis,
+ fields,
+ cond_place,
+ mode,
+ )?
+ }
+ AdtPatternShape::Unit => (current, current_else),
+ })
+ }
+
+ fn pattern_match_adt(
+ &mut self,
+ mut current: BasicBlockId,
+ mut current_else: Option<BasicBlockId>,
+ args: impl Iterator<Item = (PlaceElem, PatId)>,
+ cond_place: &Place,
+ mode: MatchingMode,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ for (proj, arg) in args {
+ let cond_place = cond_place.project(proj);
+ (current, current_else) =
+ self.pattern_match_inner(current, current_else, cond_place, arg, mode)?;
+ }
+ Ok((current, current_else))
+ }
+
+ fn pattern_match_tuple_like(
+ &mut self,
+ current: BasicBlockId,
+ current_else: Option<BasicBlockId>,
+ args: &[PatId],
+ ellipsis: Option<usize>,
+ fields: impl DoubleEndedIterator<Item = PlaceElem> + Clone,
+ cond_place: &Place,
+ mode: MatchingMode,
+ ) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
+ let (al, ar) = args.split_at(ellipsis.unwrap_or(args.len()));
+ let it = al
+ .iter()
+ .zip(fields.clone())
+ .chain(ar.iter().rev().zip(fields.rev()))
+ .map(|(x, y)| (y, *x));
+ self.pattern_match_adt(current, current_else, it, cond_place, mode)
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
new file mode 100644
index 000000000..ce3f7a8e5
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
@@ -0,0 +1,351 @@
+//! Monomorphization of mir, which is used in mir interpreter and const eval.
+//!
+//! The job of monomorphization is:
+//! * Monomorphization. That is, replacing `Option<T>` with `Option<i32>` where `T:=i32` substitution
+//! is provided
+//! * Normalizing types, for example replacing RPIT of other functions called in this body.
+//!
+//! So the monomorphization should be called even if the substitution is empty.
+
+use std::mem;
+
+use chalk_ir::{
+ fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
+ ConstData, DebruijnIndex,
+};
+use hir_def::{DefWithBodyId, GeneralConstId};
+use triomphe::Arc;
+
+use crate::{
+ consteval::unknown_const,
+ db::HirDatabase,
+ from_placeholder_idx,
+ infer::normalize,
+ method_resolution::lookup_impl_const,
+ utils::{generics, Generics},
+ ClosureId, Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind,
+};
+
+use super::{MirBody, MirLowerError, Operand, Rvalue, StatementKind, TerminatorKind};
+
+macro_rules! not_supported {
+ ($x: expr) => {
+ return Err(MirLowerError::NotSupported(format!($x)))
+ };
+}
+
+struct Filler<'a> {
+ db: &'a dyn HirDatabase,
+ trait_env: Arc<TraitEnvironment>,
+ subst: &'a Substitution,
+ generics: Option<Generics>,
+ owner: DefWithBodyId,
+}
+impl FallibleTypeFolder<Interner> for Filler<'_> {
+ type Error = MirLowerError;
+
+ fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
+ self
+ }
+
+ fn interner(&self) -> Interner {
+ Interner
+ }
+
+ fn try_fold_ty(
+ &mut self,
+ ty: Ty,
+ outer_binder: DebruijnIndex,
+ ) -> std::result::Result<Ty, Self::Error> {
+ match ty.kind(Interner) {
+ TyKind::AssociatedType(id, subst) => {
+ // I don't know exactly if and why this is needed, but it looks like `normalize_ty` likes
+ // this kind of associated types.
+ Ok(TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
+ associated_ty_id: *id,
+ substitution: subst.clone().try_fold_with(self, outer_binder)?,
+ }))
+ .intern(Interner))
+ }
+ TyKind::OpaqueType(id, subst) => {
+ let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into());
+ let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?;
+ match impl_trait_id {
+ crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
+ let infer = self.db.infer(func.into());
+ let filler = &mut Filler {
+ db: self.db,
+ owner: self.owner,
+ trait_env: self.trait_env.clone(),
+ subst: &subst,
+ generics: Some(generics(self.db.upcast(), func.into())),
+ };
+ filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder)
+ }
+ crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
+ not_supported!("async block impl trait");
+ }
+ }
+ }
+ _ => ty.try_super_fold_with(self.as_dyn(), outer_binder),
+ }
+ }
+
+ fn try_fold_free_placeholder_const(
+ &mut self,
+ _ty: chalk_ir::Ty<Interner>,
+ idx: chalk_ir::PlaceholderIndex,
+ _outer_binder: DebruijnIndex,
+ ) -> std::result::Result<chalk_ir::Const<Interner>, Self::Error> {
+ let x = from_placeholder_idx(self.db, idx);
+ let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else {
+ not_supported!("missing idx in generics");
+ };
+ Ok(self
+ .subst
+ .as_slice(Interner)
+ .get(idx)
+ .and_then(|x| x.constant(Interner))
+ .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))?
+ .clone())
+ }
+
+ fn try_fold_free_placeholder_ty(
+ &mut self,
+ idx: chalk_ir::PlaceholderIndex,
+ _outer_binder: DebruijnIndex,
+ ) -> std::result::Result<Ty, Self::Error> {
+ let x = from_placeholder_idx(self.db, idx);
+ let Some(idx) = self.generics.as_ref().and_then(|g| g.param_idx(x)) else {
+ not_supported!("missing idx in generics");
+ };
+ Ok(self
+ .subst
+ .as_slice(Interner)
+ .get(idx)
+ .and_then(|x| x.ty(Interner))
+ .ok_or_else(|| MirLowerError::GenericArgNotProvided(x, self.subst.clone()))?
+ .clone())
+ }
+
+ fn try_fold_const(
+ &mut self,
+ constant: chalk_ir::Const<Interner>,
+ outer_binder: DebruijnIndex,
+ ) -> Result<chalk_ir::Const<Interner>, Self::Error> {
+ let next_ty = normalize(
+ self.db,
+ self.trait_env.clone(),
+ constant.data(Interner).ty.clone().try_fold_with(self, outer_binder)?,
+ );
+ ConstData { ty: next_ty, value: constant.data(Interner).value.clone() }
+ .intern(Interner)
+ .try_super_fold_with(self, outer_binder)
+ }
+}
+
+impl Filler<'_> {
+ fn fill_ty(&mut self, ty: &mut Ty) -> Result<(), MirLowerError> {
+ let tmp = mem::replace(ty, TyKind::Error.intern(Interner));
+ *ty = normalize(
+ self.db,
+ self.trait_env.clone(),
+ tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?,
+ );
+ Ok(())
+ }
+
+ fn fill_const(&mut self, c: &mut Const) -> Result<(), MirLowerError> {
+ let tmp = mem::replace(c, unknown_const(c.data(Interner).ty.clone()));
+ *c = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?;
+ Ok(())
+ }
+
+ fn fill_subst(&mut self, ty: &mut Substitution) -> Result<(), MirLowerError> {
+ let tmp = mem::replace(ty, Substitution::empty(Interner));
+ *ty = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?;
+ Ok(())
+ }
+
+ fn fill_operand(&mut self, op: &mut Operand) -> Result<(), MirLowerError> {
+ match op {
+ Operand::Constant(c) => {
+ match &c.data(Interner).value {
+ chalk_ir::ConstValue::BoundVar(b) => {
+ let resolved = self
+ .subst
+ .as_slice(Interner)
+ .get(b.index)
+ .ok_or_else(|| {
+ MirLowerError::GenericArgNotProvided(
+ self.generics
+ .as_ref()
+ .and_then(|x| x.iter().nth(b.index))
+ .unwrap()
+ .0,
+ self.subst.clone(),
+ )
+ })?
+ .assert_const_ref(Interner);
+ *c = resolved.clone();
+ }
+ chalk_ir::ConstValue::InferenceVar(_)
+ | chalk_ir::ConstValue::Placeholder(_) => {}
+ chalk_ir::ConstValue::Concrete(cc) => match &cc.interned {
+ crate::ConstScalar::UnevaluatedConst(const_id, subst) => {
+ let mut const_id = *const_id;
+ let mut subst = subst.clone();
+ self.fill_subst(&mut subst)?;
+ if let GeneralConstId::ConstId(c) = const_id {
+ let (c, s) = lookup_impl_const(
+ self.db,
+ self.db.trait_environment_for_body(self.owner),
+ c,
+ subst,
+ );
+ const_id = GeneralConstId::ConstId(c);
+ subst = s;
+ }
+ let result =
+ self.db.const_eval(const_id.into(), subst).map_err(|e| {
+ let name = const_id.name(self.db.upcast());
+ MirLowerError::ConstEvalError(name, Box::new(e))
+ })?;
+ *c = result;
+ }
+ crate::ConstScalar::Bytes(_, _) | crate::ConstScalar::Unknown => (),
+ },
+ }
+ self.fill_const(c)?;
+ }
+ Operand::Copy(_) | Operand::Move(_) | Operand::Static(_) => (),
+ }
+ Ok(())
+ }
+
+ fn fill_body(&mut self, body: &mut MirBody) -> Result<(), MirLowerError> {
+ for (_, l) in body.locals.iter_mut() {
+ self.fill_ty(&mut l.ty)?;
+ }
+ for (_, bb) in body.basic_blocks.iter_mut() {
+ for statement in &mut bb.statements {
+ match &mut statement.kind {
+ StatementKind::Assign(_, r) => match r {
+ Rvalue::Aggregate(ak, ops) => {
+ for op in &mut **ops {
+ self.fill_operand(op)?;
+ }
+ match ak {
+ super::AggregateKind::Array(ty)
+ | super::AggregateKind::Tuple(ty)
+ | super::AggregateKind::Closure(ty) => self.fill_ty(ty)?,
+ super::AggregateKind::Adt(_, subst) => self.fill_subst(subst)?,
+ super::AggregateKind::Union(_, _) => (),
+ }
+ }
+ Rvalue::ShallowInitBox(_, ty) | Rvalue::ShallowInitBoxWithAlloc(ty) => {
+ self.fill_ty(ty)?;
+ }
+ Rvalue::Use(op) => {
+ self.fill_operand(op)?;
+ }
+ Rvalue::Repeat(op, len) => {
+ self.fill_operand(op)?;
+ self.fill_const(len)?;
+ }
+ Rvalue::Ref(_, _)
+ | Rvalue::Len(_)
+ | Rvalue::Cast(_, _, _)
+ | Rvalue::CheckedBinaryOp(_, _, _)
+ | Rvalue::UnaryOp(_, _)
+ | Rvalue::Discriminant(_)
+ | Rvalue::CopyForDeref(_) => (),
+ },
+ StatementKind::Deinit(_)
+ | StatementKind::StorageLive(_)
+ | StatementKind::StorageDead(_)
+ | StatementKind::Nop => (),
+ }
+ }
+ if let Some(terminator) = &mut bb.terminator {
+ match &mut terminator.kind {
+ TerminatorKind::Call { func, args, .. } => {
+ self.fill_operand(func)?;
+ for op in &mut **args {
+ self.fill_operand(op)?;
+ }
+ }
+ TerminatorKind::SwitchInt { discr, .. } => {
+ self.fill_operand(discr)?;
+ }
+ TerminatorKind::Goto { .. }
+ | TerminatorKind::Resume
+ | TerminatorKind::Abort
+ | TerminatorKind::Return
+ | TerminatorKind::Unreachable
+ | TerminatorKind::Drop { .. }
+ | TerminatorKind::DropAndReplace { .. }
+ | TerminatorKind::Assert { .. }
+ | TerminatorKind::Yield { .. }
+ | TerminatorKind::GeneratorDrop
+ | TerminatorKind::FalseEdge { .. }
+ | TerminatorKind::FalseUnwind { .. } => (),
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+pub fn monomorphized_mir_body_query(
+ db: &dyn HirDatabase,
+ owner: DefWithBodyId,
+ subst: Substitution,
+ trait_env: Arc<crate::TraitEnvironment>,
+) -> Result<Arc<MirBody>, MirLowerError> {
+ let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def));
+ let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
+ let body = db.mir_body(owner)?;
+ let mut body = (*body).clone();
+ filler.fill_body(&mut body)?;
+ Ok(Arc::new(body))
+}
+
+pub fn monomorphized_mir_body_recover(
+ _: &dyn HirDatabase,
+ _: &[String],
+ _: &DefWithBodyId,
+ _: &Substitution,
+ _: &Arc<crate::TraitEnvironment>,
+) -> Result<Arc<MirBody>, MirLowerError> {
+ return Err(MirLowerError::Loop);
+}
+
+pub fn monomorphized_mir_body_for_closure_query(
+ db: &dyn HirDatabase,
+ closure: ClosureId,
+ subst: Substitution,
+ trait_env: Arc<crate::TraitEnvironment>,
+) -> Result<Arc<MirBody>, MirLowerError> {
+ let (owner, _) = db.lookup_intern_closure(closure.into());
+ let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def));
+ let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
+ let body = db.mir_body_for_closure(closure)?;
+ let mut body = (*body).clone();
+ filler.fill_body(&mut body)?;
+ Ok(Arc::new(body))
+}
+
+// FIXME: remove this function. Monomorphization is a time consuming job and should always be a query.
+pub fn monomorphize_mir_body_bad(
+ db: &dyn HirDatabase,
+ mut body: MirBody,
+ subst: Substitution,
+ trait_env: Arc<crate::TraitEnvironment>,
+) -> Result<MirBody, MirLowerError> {
+ let owner = body.owner;
+ let generics = owner.as_generic_def_id().map(|g_def| generics(db.upcast(), g_def));
+ let filler = &mut Filler { db, subst: &subst, trait_env, generics, owner };
+ filler.fill_body(&mut body)?;
+ Ok(body)
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
index ffc08b7e3..ac23e77bd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
@@ -1,28 +1,83 @@
//! A pretty-printer for MIR.
-use std::fmt::{Display, Write};
+use std::{
+ fmt::{Debug, Display, Write},
+ mem,
+};
-use hir_def::{body::Body, expr::BindingId};
+use hir_def::{body::Body, hir::BindingId};
use hir_expand::name::Name;
use la_arena::ArenaMap;
use crate::{
db::HirDatabase,
- display::HirDisplay,
- mir::{PlaceElem, ProjectionElem, StatementKind, Terminator},
+ display::{ClosureStyle, HirDisplay},
+ mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind},
+ ClosureId,
};
use super::{
AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, Operand, Place, Rvalue, UnOp,
};
+macro_rules! w {
+ ($dst:expr, $($arg:tt)*) => {
+ { let _ = write!($dst, $($arg)*); }
+ };
+}
+
+macro_rules! wln {
+ ($dst:expr) => {
+ { let _ = writeln!($dst); }
+ };
+ ($dst:expr, $($arg:tt)*) => {
+ { let _ = writeln!($dst, $($arg)*); }
+ };
+}
+
impl MirBody {
pub fn pretty_print(&self, db: &dyn HirDatabase) -> String {
let hir_body = db.body(self.owner);
let mut ctx = MirPrettyCtx::new(self, &hir_body, db);
- ctx.for_body();
+ ctx.for_body(|this| match ctx.body.owner {
+ hir_def::DefWithBodyId::FunctionId(id) => {
+ let data = db.function_data(id);
+ w!(this, "fn {}() ", data.name.display(db.upcast()));
+ }
+ hir_def::DefWithBodyId::StaticId(id) => {
+ let data = db.static_data(id);
+ w!(this, "static {}: _ = ", data.name.display(db.upcast()));
+ }
+ hir_def::DefWithBodyId::ConstId(id) => {
+ let data = db.const_data(id);
+ w!(
+ this,
+ "const {}: _ = ",
+ data.name.as_ref().unwrap_or(&Name::missing()).display(db.upcast())
+ );
+ }
+ hir_def::DefWithBodyId::VariantId(id) => {
+ let data = db.enum_data(id.parent);
+ w!(this, "enum {} = ", data.name.display(db.upcast()));
+ }
+ hir_def::DefWithBodyId::InTypeConstId(id) => {
+ w!(this, "in type const {id:?} = ");
+ }
+ });
ctx.result
}
+
+ // String with lines is rendered poorly in `dbg` macros, which I use very much, so this
+ // function exists to solve that.
+ pub fn dbg(&self, db: &dyn HirDatabase) -> impl Debug {
+ struct StringDbg(String);
+ impl Debug for StringDbg {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(&self.0)
+ }
+ }
+ StringDbg(self.pretty_print(db))
+ }
}
struct MirPrettyCtx<'a> {
@@ -30,25 +85,10 @@ struct MirPrettyCtx<'a> {
hir_body: &'a Body,
db: &'a dyn HirDatabase,
result: String,
- ident: String,
+ indent: String,
local_to_binding: ArenaMap<LocalId, BindingId>,
}
-macro_rules! w {
- ($dst:expr, $($arg:tt)*) => {
- { let _ = write!($dst, $($arg)*); }
- };
-}
-
-macro_rules! wln {
- ($dst:expr) => {
- { let _ = writeln!($dst); }
- };
- ($dst:expr, $($arg:tt)*) => {
- { let _ = writeln!($dst, $($arg)*); }
- };
-}
-
impl Write for MirPrettyCtx<'_> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
let mut it = s.split('\n'); // note: `.lines()` is wrong here
@@ -66,31 +106,62 @@ enum LocalName {
Binding(Name, LocalId),
}
-impl Display for LocalName {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+impl HirDisplay for LocalName {
+ fn hir_fmt(
+ &self,
+ f: &mut crate::display::HirFormatter<'_>,
+ ) -> Result<(), crate::display::HirDisplayError> {
match self {
LocalName::Unknown(l) => write!(f, "_{}", u32::from(l.into_raw())),
- LocalName::Binding(n, l) => write!(f, "{n}_{}", u32::from(l.into_raw())),
+ LocalName::Binding(n, l) => {
+ write!(f, "{}_{}", n.display(f.db.upcast()), u32::from(l.into_raw()))
+ }
}
}
}
impl<'a> MirPrettyCtx<'a> {
- fn for_body(&mut self) {
+ fn for_body(&mut self, name: impl FnOnce(&mut MirPrettyCtx<'_>)) {
+ name(self);
self.with_block(|this| {
this.locals();
wln!(this);
this.blocks();
});
+ for &closure in &self.body.closures {
+ self.for_closure(closure);
+ }
+ }
+
+ fn for_closure(&mut self, closure: ClosureId) {
+ let body = match self.db.mir_body_for_closure(closure) {
+ Ok(x) => x,
+ Err(e) => {
+ wln!(self, "// error in {closure:?}: {e:?}");
+ return;
+ }
+ };
+ let result = mem::take(&mut self.result);
+ let indent = mem::take(&mut self.indent);
+ let mut ctx = MirPrettyCtx {
+ body: &body,
+ local_to_binding: body.binding_locals.iter().map(|(x, y)| (*y, x)).collect(),
+ result,
+ indent,
+ ..*self
+ };
+ ctx.for_body(|this| wln!(this, "// Closure: {:?}", closure));
+ self.result = ctx.result;
+ self.indent = ctx.indent;
}
fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) {
- self.ident += " ";
+ self.indent += " ";
wln!(self, "{{");
f(self);
for _ in 0..4 {
self.result.pop();
- self.ident.pop();
+ self.indent.pop();
}
wln!(self, "}}");
}
@@ -101,7 +172,7 @@ impl<'a> MirPrettyCtx<'a> {
body,
db,
result: String::new(),
- ident: String::new(),
+ indent: String::new(),
local_to_binding,
hir_body,
}
@@ -109,7 +180,7 @@ impl<'a> MirPrettyCtx<'a> {
fn write_line(&mut self) {
self.result.push('\n');
- self.result += &self.ident;
+ self.result += &self.indent;
}
fn write(&mut self, line: &str) {
@@ -118,7 +189,12 @@ impl<'a> MirPrettyCtx<'a> {
fn locals(&mut self) {
for (id, local) in self.body.locals.iter() {
- wln!(self, "let {}: {};", self.local_name(id), local.ty.display(self.db));
+ wln!(
+ self,
+ "let {}: {};",
+ self.local_name(id).display(self.db),
+ self.hir_display(&local.ty)
+ );
}
}
@@ -147,10 +223,10 @@ impl<'a> MirPrettyCtx<'a> {
wln!(this, ";");
}
StatementKind::StorageDead(p) => {
- wln!(this, "StorageDead({})", this.local_name(*p));
+ wln!(this, "StorageDead({})", this.local_name(*p).display(self.db));
}
StatementKind::StorageLive(p) => {
- wln!(this, "StorageLive({})", this.local_name(*p));
+ wln!(this, "StorageLive({})", this.local_name(*p).display(self.db));
}
StatementKind::Deinit(p) => {
w!(this, "Deinit(");
@@ -161,11 +237,11 @@ impl<'a> MirPrettyCtx<'a> {
}
}
match &block.terminator {
- Some(terminator) => match terminator {
- Terminator::Goto { target } => {
+ Some(terminator) => match &terminator.kind {
+ TerminatorKind::Goto { target } => {
wln!(this, "goto 'bb{};", u32::from(target.into_raw()))
}
- Terminator::SwitchInt { discr, targets } => {
+ TerminatorKind::SwitchInt { discr, targets } => {
w!(this, "switch ");
this.operand(discr);
w!(this, " ");
@@ -176,7 +252,7 @@ impl<'a> MirPrettyCtx<'a> {
wln!(this, "_ => {},", this.basic_block_id(targets.otherwise()));
});
}
- Terminator::Call { func, args, destination, target, .. } => {
+ TerminatorKind::Call { func, args, destination, target, .. } => {
w!(this, "Call ");
this.with_block(|this| {
w!(this, "func: ");
@@ -208,7 +284,7 @@ impl<'a> MirPrettyCtx<'a> {
fn f(this: &mut MirPrettyCtx<'_>, local: LocalId, projections: &[PlaceElem]) {
let Some((last, head)) = projections.split_last() else {
// no projection
- w!(this, "{}", this.local_name(local));
+ w!(this, "{}", this.local_name(local).display(this.db));
return;
};
match last {
@@ -226,21 +302,26 @@ impl<'a> MirPrettyCtx<'a> {
f(this, local, head);
let variant_name =
&this.db.enum_data(e.parent).variants[e.local_id].name;
- w!(this, " as {}).{}", variant_name, name);
+ w!(
+ this,
+ " as {}).{}",
+ variant_name.display(this.db.upcast()),
+ name.display(this.db.upcast())
+ );
}
hir_def::VariantId::StructId(_) | hir_def::VariantId::UnionId(_) => {
f(this, local, head);
- w!(this, ".{name}");
+ w!(this, ".{}", name.display(this.db.upcast()));
}
}
}
- ProjectionElem::TupleField(x) => {
+ ProjectionElem::TupleOrClosureField(x) => {
f(this, local, head);
w!(this, ".{}", x);
}
ProjectionElem::Index(l) => {
f(this, local, head);
- w!(this, "[{}]", this.local_name(*l));
+ w!(this, "[{}]", this.local_name(*l).display(this.db));
}
x => {
f(this, local, head);
@@ -258,7 +339,8 @@ impl<'a> MirPrettyCtx<'a> {
// equally. Feel free to change it.
self.place(p);
}
- Operand::Constant(c) => w!(self, "Const({})", c.display(self.db)),
+ Operand::Constant(c) => w!(self, "Const({})", self.hir_display(c)),
+ Operand::Static(s) => w!(self, "Static({:?})", s),
}
}
@@ -284,11 +366,21 @@ impl<'a> MirPrettyCtx<'a> {
self.operand_list(x);
w!(self, "]");
}
+ Rvalue::Repeat(op, len) => {
+ w!(self, "[");
+ self.operand(op);
+ w!(self, "; {}]", len.display(self.db));
+ }
Rvalue::Aggregate(AggregateKind::Adt(_, _), x) => {
w!(self, "Adt(");
self.operand_list(x);
w!(self, ")");
}
+ Rvalue::Aggregate(AggregateKind::Closure(_), x) => {
+ w!(self, "Closure(");
+ self.operand_list(x);
+ w!(self, ")");
+ }
Rvalue::Aggregate(AggregateKind::Union(_, _), x) => {
w!(self, "Union(");
self.operand_list(x);
@@ -300,9 +392,9 @@ impl<'a> MirPrettyCtx<'a> {
w!(self, ")");
}
Rvalue::Cast(ck, op, ty) => {
- w!(self, "Discriminant({ck:?}");
+ w!(self, "Cast({ck:?}, ");
self.operand(op);
- w!(self, "{})", ty.display(self.db));
+ w!(self, ", {})", self.hir_display(ty));
}
Rvalue::CheckedBinaryOp(b, o1, o2) => {
self.operand(o1);
@@ -322,6 +414,7 @@ impl<'a> MirPrettyCtx<'a> {
self.place(p);
w!(self, ")");
}
+ Rvalue::ShallowInitBoxWithAlloc(_) => w!(self, "ShallowInitBoxWithAlloc"),
Rvalue::ShallowInitBox(op, _) => {
w!(self, "ShallowInitBox(");
self.operand(op);
@@ -345,4 +438,8 @@ impl<'a> MirPrettyCtx<'a> {
}
}
}
+
+ fn hir_display<T: HirDisplay>(&self, ty: &'a T) -> impl Display + 'a {
+ ty.display(self.db).with_closure_style(ClosureStyle::ClosureWithSubst)
+ }
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
index 8c48331b9..7d19e0a19 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
@@ -1,18 +1,18 @@
//! Database used for testing `hir`.
-use std::{
- fmt, panic,
- sync::{Arc, Mutex},
-};
+use std::{fmt, panic, sync::Mutex};
use base_db::{
- salsa, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
+ salsa::{self, Durability},
+ AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
};
use hir_def::{db::DefDatabase, ModuleId};
use hir_expand::db::ExpandDatabase;
-use stdx::hash::{NoHashHashMap, NoHashHashSet};
+use nohash_hasher::IntMap;
+use rustc_hash::FxHashSet;
use syntax::TextRange;
use test_utils::extract_annotations;
+use triomphe::Arc;
#[salsa::database(
base_db::SourceDatabaseExtStorage,
@@ -30,7 +30,7 @@ pub(crate) struct TestDB {
impl Default for TestDB {
fn default() -> Self {
let mut this = Self { storage: Default::default(), events: Default::default() };
- this.set_enable_proc_attr_macros(true);
+ this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
this
}
}
@@ -74,13 +74,13 @@ impl salsa::ParallelDatabase for TestDB {
impl panic::RefUnwindSafe for TestDB {}
impl FileLoader for TestDB {
- fn file_text(&self, file_id: FileId) -> Arc<String> {
+ fn file_text(&self, file_id: FileId) -> Arc<str> {
FileLoaderDelegate(self).file_text(file_id)
}
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
FileLoaderDelegate(self).relevant_crates(file_id)
}
}
@@ -102,7 +102,7 @@ impl TestDB {
self.module_for_file_opt(file_id).unwrap()
}
- pub(crate) fn extract_annotations(&self) -> NoHashHashMap<FileId, Vec<(TextRange, String)>> {
+ pub(crate) fn extract_annotations(&self) -> IntMap<FileId, Vec<(TextRange, String)>> {
let mut files = Vec::new();
let crate_graph = self.crate_graph();
for krate in crate_graph.iter() {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index 83d31f002..857141280 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -10,14 +10,14 @@ mod display_source_code;
mod incremental;
mod diagnostics;
-use std::{collections::HashMap, env, sync::Arc};
+use std::{collections::HashMap, env};
use base_db::{fixture::WithFixture, FileRange, SourceDatabaseExt};
use expect_test::Expect;
use hir_def::{
body::{Body, BodySourceMap, SyntheticSyntax},
db::{DefDatabase, InternDatabase},
- expr::{ExprId, PatId},
+ hir::{ExprId, Pat, PatId},
item_scope::ItemScope,
nameres::DefMap,
src::HasSource,
@@ -32,6 +32,7 @@ use syntax::{
};
use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry};
use tracing_tree::HierarchicalLayer;
+use triomphe::Arc;
use crate::{
db::HirDatabase,
@@ -145,13 +146,17 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let loc = db.lookup_intern_enum(it.parent);
loc.source(&db).value.syntax().text_range().start()
}
+ DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(),
});
let mut unexpected_type_mismatches = String::new();
for def in defs {
- let (_body, body_source_map) = db.body_with_source_map(def);
+ let (body, body_source_map) = db.body_with_source_map(def);
let inference_result = db.infer(def);
- for (pat, ty) in inference_result.type_of_pat.iter() {
+ for (pat, mut ty) in inference_result.type_of_pat.iter() {
+ if let Pat::Bind { id, .. } = body.pats[pat] {
+ ty = &inference_result.type_of_binding[id];
+ }
let node = match pat_node(&body_source_map, pat, &db) {
Some(value) => value,
None => continue,
@@ -159,7 +164,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let range = node.as_ref().original_file_range(&db);
if let Some(expected) = types.remove(&range) {
let actual = if display_source {
- ty.display_source_code(&db, def.module(&db)).unwrap()
+ ty.display_source_code(&db, def.module(&db), true).unwrap()
} else {
ty.display_test(&db).to_string()
};
@@ -175,7 +180,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let range = node.as_ref().original_file_range(&db);
if let Some(expected) = types.remove(&range) {
let actual = if display_source {
- ty.display_source_code(&db, def.module(&db)).unwrap()
+ ty.display_source_code(&db, def.module(&db), true).unwrap()
} else {
ty.display_test(&db).to_string()
};
@@ -198,8 +203,8 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
for (expr_or_pat, mismatch) in inference_result.type_mismatches() {
let Some(node) = (match expr_or_pat {
- hir_def::expr::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
- hir_def::expr::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
+ hir_def::hir::ExprOrPatId::ExprId(expr) => expr_node(&body_source_map, expr, &db),
+ hir_def::hir::ExprOrPatId::PatId(pat) => pat_node(&body_source_map, pat, &db),
}) else { continue; };
let range = node.as_ref().original_file_range(&db);
let actual = format!(
@@ -246,7 +251,7 @@ fn expr_node(
) -> Option<InFile<SyntaxNode>> {
Some(match body_source_map.expr_syntax(expr) {
Ok(sp) => {
- let root = db.parse_or_expand(sp.file_id).unwrap();
+ let root = db.parse_or_expand(sp.file_id);
sp.map(|ptr| ptr.to_node(&root).syntax().clone())
}
Err(SyntheticSyntax) => return None,
@@ -260,7 +265,7 @@ fn pat_node(
) -> Option<InFile<SyntaxNode>> {
Some(match body_source_map.pat_syntax(pat) {
Ok(sp) => {
- let root = db.parse_or_expand(sp.file_id).unwrap();
+ let root = db.parse_or_expand(sp.file_id);
sp.map(|ptr| {
ptr.either(
|it| it.to_node(&root).syntax().clone(),
@@ -283,14 +288,18 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let mut buf = String::new();
let mut infer_def = |inference_result: Arc<InferenceResult>,
+ body: Arc<Body>,
body_source_map: Arc<BodySourceMap>| {
let mut types: Vec<(InFile<SyntaxNode>, &Ty)> = Vec::new();
let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new();
- for (pat, ty) in inference_result.type_of_pat.iter() {
+ for (pat, mut ty) in inference_result.type_of_pat.iter() {
+ if let Pat::Bind { id, .. } = body.pats[pat] {
+ ty = &inference_result.type_of_binding[id];
+ }
let syntax_ptr = match body_source_map.pat_syntax(pat) {
Ok(sp) => {
- let root = db.parse_or_expand(sp.file_id).unwrap();
+ let root = db.parse_or_expand(sp.file_id);
sp.map(|ptr| {
ptr.either(
|it| it.to_node(&root).syntax().clone(),
@@ -309,7 +318,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
for (expr, ty) in inference_result.type_of_expr.iter() {
let node = match body_source_map.expr_syntax(expr) {
Ok(sp) => {
- let root = db.parse_or_expand(sp.file_id).unwrap();
+ let root = db.parse_or_expand(sp.file_id);
sp.map(|ptr| ptr.to_node(&root).syntax().clone())
}
Err(SyntheticSyntax) => continue,
@@ -383,11 +392,12 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let loc = db.lookup_intern_enum(it.parent);
loc.source(&db).value.syntax().text_range().start()
}
+ DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(),
});
for def in defs {
- let (_body, source_map) = db.body_with_source_map(def);
+ let (body, source_map) = db.body_with_source_map(def);
let infer = db.infer(def);
- infer_def(infer, source_map);
+ infer_def(infer, body, source_map);
}
buf.truncate(buf.trim_end().len());
@@ -572,10 +582,9 @@ fn salsa_bug() {
let x = 1;
x.push(1);
}
- "
- .to_string();
+ ";
- db.set_file_text(pos.file_id, Arc::new(new_text));
+ db.set_file_text(pos.file_id, Arc::from(new_text));
let module = db.module_for_file(pos.file_id);
let crate_def_map = module.def_map(&db);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
index b524922b6..16e5ef85d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
@@ -258,7 +258,6 @@ fn test() {
#[test]
fn coerce_autoderef_block() {
- // FIXME: We should know mutability in overloaded deref
check_no_mismatches(
r#"
//- minicore: deref
@@ -268,7 +267,7 @@ fn takes_ref_str(x: &str) {}
fn returns_string() -> String { loop {} }
fn test() {
takes_ref_str(&{ returns_string() });
- // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(None))), Borrow(Ref(Not))
+ // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
}
"#,
);
@@ -397,9 +396,39 @@ fn test() {
}
#[test]
+fn coerce_fn_item_to_fn_ptr_in_array() {
+ check_no_mismatches(
+ r"
+fn foo(x: u32) -> isize { 1 }
+fn bar(x: u32) -> isize { 1 }
+fn test() {
+ let f = [foo, bar];
+ // ^^^ adjustments: Pointer(ReifyFnPointer)
+}",
+ );
+}
+
+#[test]
fn coerce_fn_items_in_match_arms() {
cov_mark::check!(coerce_fn_reification);
+ check_no_mismatches(
+ r"
+fn foo1(x: u32) -> isize { 1 }
+fn foo2(x: u32) -> isize { 2 }
+fn foo3(x: u32) -> isize { 3 }
+fn test() {
+ let x = match 1 {
+ 1 => foo1,
+ // ^^^^ adjustments: Pointer(ReifyFnPointer)
+ 2 => foo2,
+ // ^^^^ adjustments: Pointer(ReifyFnPointer)
+ _ => foo3,
+ // ^^^^ adjustments: Pointer(ReifyFnPointer)
+ };
+ x;
+}",
+ );
check_types(
r"
fn foo1(x: u32) -> isize { 1 }
@@ -507,7 +536,6 @@ fn test() {
#[test]
fn coerce_unsize_generic() {
- // FIXME: fix the type mismatches here
check(
r#"
//- minicore: coerce_unsized
@@ -516,9 +544,9 @@ struct Bar<T>(Foo<T>);
fn test() {
let _: &Foo<[usize]> = &Foo { t: [1, 2, 3] };
- //^^^^^^^^^ expected [usize], got [usize; 3]
+ //^^^^^^^^^^^^^^^^^^^^^ expected &Foo<[usize]>, got &Foo<[i32; 3]>
let _: &Bar<[usize]> = &Bar(Foo { t: [1, 2, 3] });
- //^^^^^^^^^ expected [usize], got [usize; 3]
+ //^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &Bar<[usize]>, got &Bar<[i32; 3]>
}
"#,
);
@@ -547,7 +575,7 @@ fn two_closures_lub() {
fn foo(c: i32) {
let add = |a: i32, b: i32| a + b;
let sub = |a, b| a - b;
- //^^^^^^^^^^^^ |i32, i32| -> i32
+ //^^^^^^^^^^^^ impl Fn(i32, i32) -> i32
if c > 42 { add } else { sub };
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fn(i32, i32) -> i32
}
@@ -842,3 +870,74 @@ fn test() {
}",
);
}
+
+#[test]
+fn adjust_index() {
+ check_no_mismatches(
+ r"
+//- minicore: index, slice, coerce_unsized
+fn test() {
+ let x = [1, 2, 3];
+ x[2] = 6;
+ // ^ adjustments: Borrow(Ref(Mut))
+}
+ ",
+ );
+ check_no_mismatches(
+ r"
+//- minicore: index
+struct Struct;
+impl core::ops::Index<usize> for Struct {
+ type Output = ();
+
+ fn index(&self, index: usize) -> &Self::Output { &() }
+}
+struct StructMut;
+
+impl core::ops::Index<usize> for StructMut {
+ type Output = ();
+
+ fn index(&self, index: usize) -> &Self::Output { &() }
+}
+impl core::ops::IndexMut for StructMut {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut () }
+}
+fn test() {
+ Struct[0];
+ // ^^^^^^ adjustments: Borrow(Ref(Not))
+ StructMut[0];
+ // ^^^^^^^^^ adjustments: Borrow(Ref(Not))
+ &mut StructMut[0];
+ // ^^^^^^^^^ adjustments: Borrow(Ref(Mut))
+}",
+ );
+}
+
+#[test]
+fn regression_14443_dyn_coercion_block_impls() {
+ check_no_mismatches(
+ r#"
+//- minicore: coerce_unsized
+trait T {}
+
+fn dyn_t(d: &dyn T) {}
+
+fn main() {
+ struct A;
+ impl T for A {}
+
+ let a = A;
+
+ let b = {
+ struct B;
+ impl T for B {}
+
+ B
+ };
+
+ dyn_t(&a);
+ dyn_t(&b);
+}
+"#,
+ )
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
index 073d6d9be..bb15ca8c4 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
@@ -1,6 +1,5 @@
-use std::sync::Arc;
-
use base_db::{fixture::WithFixture, SourceDatabaseExt};
+use triomphe::Arc;
use crate::{db::HirDatabase, test_db::TestDB};
@@ -33,10 +32,9 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
+
1
}
- "
- .to_string();
+ ";
- db.set_file_text(pos.file_id, Arc::new(new_text));
+ db.set_file_text(pos.file_id, Arc::from(new_text));
{
let events = db.log_executed(|| {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
index 8b75ec842..111ac0b61 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs
@@ -140,6 +140,7 @@ fn infer_path_qualified_macros_expanded() {
fn expr_macro_def_expanded_in_various_places() {
check_infer(
r#"
+ //- minicore: iterator
macro spam() {
1isize
}
@@ -195,10 +196,19 @@ fn expr_macro_def_expanded_in_various_places() {
!0..6 '1isize': isize
39..442 '{ ...!(); }': ()
73..94 'spam!(...am!())': {unknown}
+ 100..119 'for _ ...!() {}': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter
+ 100..119 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+ 100..119 'for _ ...!() {}': !
+ 100..119 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+ 100..119 'for _ ...!() {}': &mut IntoIterator::IntoIter<isize>
+ 100..119 'for _ ...!() {}': fn next<IntoIterator::IntoIter<isize>>(&mut IntoIterator::IntoIter<isize>) -> Option<<IntoIterator::IntoIter<isize> as Iterator>::Item>
+ 100..119 'for _ ...!() {}': Option<Iterator::Item<IntoIterator::IntoIter<isize>>>
100..119 'for _ ...!() {}': ()
- 104..105 '_': {unknown}
+ 100..119 'for _ ...!() {}': ()
+ 100..119 'for _ ...!() {}': ()
+ 104..105 '_': Iterator::Item<IntoIterator::IntoIter<isize>>
117..119 '{}': ()
- 124..134 '|| spam!()': || -> isize
+ 124..134 '|| spam!()': impl Fn() -> isize
140..156 'while ...!() {}': ()
154..156 '{}': ()
161..174 'break spam!()': !
@@ -221,6 +231,7 @@ fn expr_macro_def_expanded_in_various_places() {
fn expr_macro_rules_expanded_in_various_places() {
check_infer(
r#"
+ //- minicore: iterator
macro_rules! spam {
() => (1isize);
}
@@ -276,10 +287,19 @@ fn expr_macro_rules_expanded_in_various_places() {
!0..6 '1isize': isize
53..456 '{ ...!(); }': ()
87..108 'spam!(...am!())': {unknown}
+ 114..133 'for _ ...!() {}': fn into_iter<isize>(isize) -> <isize as IntoIterator>::IntoIter
+ 114..133 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+ 114..133 'for _ ...!() {}': !
+ 114..133 'for _ ...!() {}': IntoIterator::IntoIter<isize>
+ 114..133 'for _ ...!() {}': &mut IntoIterator::IntoIter<isize>
+ 114..133 'for _ ...!() {}': fn next<IntoIterator::IntoIter<isize>>(&mut IntoIterator::IntoIter<isize>) -> Option<<IntoIterator::IntoIter<isize> as Iterator>::Item>
+ 114..133 'for _ ...!() {}': Option<Iterator::Item<IntoIterator::IntoIter<isize>>>
+ 114..133 'for _ ...!() {}': ()
114..133 'for _ ...!() {}': ()
- 118..119 '_': {unknown}
+ 114..133 'for _ ...!() {}': ()
+ 118..119 '_': Iterator::Item<IntoIterator::IntoIter<isize>>
131..133 '{}': ()
- 138..148 '|| spam!()': || -> isize
+ 138..148 '|| spam!()': impl Fn() -> isize
154..170 'while ...!() {}': ()
168..170 '{}': ()
175..188 'break spam!()': !
@@ -661,8 +681,9 @@ fn infer_builtin_macros_line() {
"#,
expect![[r#"
!0..1 '0': i32
+ !0..6 '0asu32': u32
63..87 '{ ...!(); }': ()
- 73..74 'x': i32
+ 73..74 'x': u32
"#]],
);
}
@@ -699,8 +720,9 @@ fn infer_builtin_macros_column() {
"#,
expect![[r#"
!0..1 '0': i32
+ !0..6 '0asu32': u32
65..91 '{ ...!(); }': ()
- 75..76 'x': i32
+ 75..76 'x': u32
"#]],
);
}
@@ -945,7 +967,7 @@ fn infer_builtin_macros_concat_with_lazy() {
#[test]
fn infer_builtin_macros_env() {
- check_infer(
+ check_types(
r#"
//- /main.rs env:foo=bar
#[rustc_builtin_macro]
@@ -953,13 +975,26 @@ fn infer_builtin_macros_env() {
fn main() {
let x = env!("foo");
+ //^ &str
+ }
+ "#,
+ );
+}
+
+#[test]
+fn infer_builtin_macros_option_env() {
+ check_types(
+ r#"
+ //- minicore: option
+ //- /main.rs env:foo=bar
+ #[rustc_builtin_macro]
+ macro_rules! option_env {() => {}}
+
+ fn main() {
+ let x = option_env!("foo");
+ //^ Option<&str>
}
"#,
- expect![[r#"
- !0..22 '"__RA_...TED__"': &str
- 62..90 '{ ...o"); }': ()
- 72..73 'x': &str
- "#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
index 378d47833..1e57a4ae2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
@@ -389,6 +389,24 @@ mod bar_test {
}
#[test]
+fn infer_trait_method_multiple_mutable_reference() {
+ check_types(
+ r#"
+trait Trait {
+ fn method(&mut self) -> i32 { 5 }
+}
+struct S;
+impl Trait for &mut &mut S {}
+fn test() {
+ let s = &mut &mut &mut S;
+ s.method();
+ //^^^^^^^^^^ i32
+}
+ "#,
+ );
+}
+
+#[test]
fn infer_trait_method_generic_1() {
// the trait implementation is intentionally incomplete -- it shouldn't matter
check_types(
@@ -1255,7 +1273,6 @@ fn foo<T: Trait>(a: &T) {
#[test]
fn autoderef_visibility_field() {
- // FIXME: We should know mutability in overloaded deref
check(
r#"
//- minicore: deref
@@ -1277,7 +1294,7 @@ mod a {
mod b {
fn foo() {
let x = super::a::Bar::new().0;
- // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(None)))
+ // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not))))
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
}
}
@@ -1723,7 +1740,7 @@ fn test() {
Foo.foo();
//^^^ adjustments: Borrow(Ref(Not))
(&Foo).foo();
- // ^^^^ adjustments: ,
+ // ^^^^ adjustments: Deref(None), Borrow(Ref(Not))
}
"#,
);
@@ -1922,3 +1939,54 @@ fn foo() {
"#,
);
}
+
+#[test]
+fn box_deref_is_builtin() {
+ check(
+ r#"
+//- minicore: deref
+use core::ops::Deref;
+
+#[lang = "owned_box"]
+struct Box<T>(*mut T);
+
+impl<T> Box<T> {
+ fn new(t: T) -> Self {
+ loop {}
+ }
+}
+
+impl<T> Deref for Box<T> {
+ type Target = T;
+ fn deref(&self) -> &Self::Target;
+}
+
+struct Foo;
+impl Foo {
+ fn foo(&self) {}
+}
+fn test() {
+ Box::new(Foo).foo();
+ //^^^^^^^^^^^^^ adjustments: Deref(None), Borrow(Ref(Not))
+}
+"#,
+ );
+}
+
+#[test]
+fn manually_drop_deref_is_not_builtin() {
+ check(
+ r#"
+//- minicore: manually_drop, deref
+struct Foo;
+impl Foo {
+ fn foo(&self) {}
+}
+use core::mem::ManuallyDrop;
+fn test() {
+ ManuallyDrop::new(Foo).foo();
+ //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
+}
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
index fbdc8209f..59046c043 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
@@ -327,6 +327,7 @@ fn diverging_expression_2() {
fn diverging_expression_3_break() {
check_infer_with_mismatches(
r"
+ //- minicore: iterator
//- /main.rs
fn test1() {
// should give type mismatch
@@ -360,6 +361,15 @@ fn diverging_expression_3_break() {
97..343 '{ ...; }; }': ()
140..141 'x': u32
149..175 '{ for ...; }; }': u32
+ 151..172 'for a ...eak; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+ 151..172 'for a ...eak; }': {unknown}
+ 151..172 'for a ...eak; }': !
+ 151..172 'for a ...eak; }': {unknown}
+ 151..172 'for a ...eak; }': &mut {unknown}
+ 151..172 'for a ...eak; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 151..172 'for a ...eak; }': Option<{unknown}>
+ 151..172 'for a ...eak; }': ()
+ 151..172 'for a ...eak; }': ()
151..172 'for a ...eak; }': ()
155..156 'a': {unknown}
160..161 'b': {unknown}
@@ -367,12 +377,30 @@ fn diverging_expression_3_break() {
164..169 'break': !
226..227 'x': u32
235..253 '{ for ... {}; }': u32
+ 237..250 'for a in b {}': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+ 237..250 'for a in b {}': {unknown}
+ 237..250 'for a in b {}': !
+ 237..250 'for a in b {}': {unknown}
+ 237..250 'for a in b {}': &mut {unknown}
+ 237..250 'for a in b {}': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 237..250 'for a in b {}': Option<{unknown}>
+ 237..250 'for a in b {}': ()
+ 237..250 'for a in b {}': ()
237..250 'for a in b {}': ()
241..242 'a': {unknown}
246..247 'b': {unknown}
248..250 '{}': ()
304..305 'x': u32
313..340 '{ for ...; }; }': u32
+ 315..337 'for a ...urn; }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+ 315..337 'for a ...urn; }': {unknown}
+ 315..337 'for a ...urn; }': !
+ 315..337 'for a ...urn; }': {unknown}
+ 315..337 'for a ...urn; }': &mut {unknown}
+ 315..337 'for a ...urn; }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 315..337 'for a ...urn; }': Option<{unknown}>
+ 315..337 'for a ...urn; }': ()
+ 315..337 'for a ...urn; }': ()
315..337 'for a ...urn; }': ()
319..320 'a': {unknown}
324..325 'b': {unknown}
@@ -483,3 +511,22 @@ fn example() -> bool {
"#,
);
}
+
+#[test]
+fn reservation_impl_should_be_ignored() {
+ // See rust-lang/rust#64631.
+ check_types(
+ r#"
+//- minicore: from
+struct S;
+#[rustc_reservation_impl]
+impl<T> From<!> for T {}
+fn foo<T, U: From<T>>(_: U) -> T { loop {} }
+
+fn test() {
+ let s = foo(S);
+ //^ S
+}
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
index 74bcab6ca..0f5a3e175 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
@@ -1,11 +1,12 @@
use expect_test::expect;
-use super::{check, check_infer, check_infer_with_mismatches, check_types};
+use super::{check, check_infer, check_infer_with_mismatches, check_no_mismatches, check_types};
#[test]
fn infer_pattern() {
check_infer(
r#"
+ //- minicore: iterator
fn test(x: &i32) {
let y = x;
let &z = x;
@@ -46,6 +47,15 @@ fn infer_pattern() {
82..94 '(1, "hello")': (i32, &str)
83..84 '1': i32
86..93 '"hello"': &str
+ 101..151 'for (e... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+ 101..151 'for (e... }': {unknown}
+ 101..151 'for (e... }': !
+ 101..151 'for (e... }': {unknown}
+ 101..151 'for (e... }': &mut {unknown}
+ 101..151 'for (e... }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 101..151 'for (e... }': Option<({unknown}, {unknown})>
+ 101..151 'for (e... }': ()
+ 101..151 'for (e... }': ()
101..151 'for (e... }': ()
105..111 '(e, f)': ({unknown}, {unknown})
106..107 'e': {unknown}
@@ -70,8 +80,8 @@ fn infer_pattern() {
228..233 '&true': &bool
229..233 'true': bool
234..236 '{}': ()
- 246..252 'lambda': |u64, u64, i32| -> i32
- 255..287 '|a: u6...b; c }': |u64, u64, i32| -> i32
+ 246..252 'lambda': impl Fn(u64, u64, i32) -> i32
+ 255..287 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32
256..257 'a': u64
264..265 'b': u64
267..268 'c': i32
@@ -241,6 +251,21 @@ fn infer_pattern_match_ergonomics_ref() {
}
#[test]
+fn ref_pat_with_inference_variable() {
+ check_no_mismatches(
+ r#"
+enum E { A }
+fn test() {
+ let f = |e| match e {
+ &E::A => {}
+ };
+ f(&E::A);
+}
+"#,
+ );
+}
+
+#[test]
fn infer_pattern_match_slice() {
check_infer(
r#"
@@ -476,7 +501,7 @@ fn infer_adt_pattern() {
183..184 'x': usize
190..191 'x': usize
201..205 'E::B': E
- 209..212 'foo': {unknown}
+ 209..212 'foo': bool
216..217 '1': usize
227..231 'E::B': E
235..237 '10': usize
@@ -677,25 +702,25 @@ fn test() {
51..58 'loop {}': !
56..58 '{}': ()
72..171 '{ ... x); }': ()
- 78..81 'foo': fn foo<&(i32, &str), i32, |&(i32, &str)| -> i32>(&(i32, &str), |&(i32, &str)| -> i32) -> i32
+ 78..81 'foo': fn foo<&(i32, &str), i32, impl Fn(&(i32, &str)) -> i32>(&(i32, &str), impl Fn(&(i32, &str)) -> i32) -> i32
78..105 'foo(&(...y)| x)': i32
82..91 '&(1, "a")': &(i32, &str)
83..91 '(1, "a")': (i32, &str)
84..85 '1': i32
87..90 '"a"': &str
- 93..104 '|&(x, y)| x': |&(i32, &str)| -> i32
+ 93..104 '|&(x, y)| x': impl Fn(&(i32, &str)) -> i32
94..101 '&(x, y)': &(i32, &str)
95..101 '(x, y)': (i32, &str)
96..97 'x': i32
99..100 'y': &str
103..104 'x': i32
- 142..145 'foo': fn foo<&(i32, &str), &i32, |&(i32, &str)| -> &i32>(&(i32, &str), |&(i32, &str)| -> &i32) -> &i32
+ 142..145 'foo': fn foo<&(i32, &str), &i32, impl Fn(&(i32, &str)) -> &i32>(&(i32, &str), impl Fn(&(i32, &str)) -> &i32) -> &i32
142..168 'foo(&(...y)| x)': &i32
146..155 '&(1, "a")': &(i32, &str)
147..155 '(1, "a")': (i32, &str)
148..149 '1': i32
151..154 '"a"': &str
- 157..167 '|(x, y)| x': |&(i32, &str)| -> &i32
+ 157..167 '|(x, y)| x': impl Fn(&(i32, &str)) -> &i32
158..164 '(x, y)': (i32, &str)
159..160 'x': &i32
162..163 'y': &&str
@@ -1084,7 +1109,7 @@ fn var_args() {
#[lang = "va_list"]
pub struct VaListImpl<'f>;
fn my_fn(foo: ...) {}
- //^^^ VaListImpl
+ //^^^ VaListImpl<'_>
"#,
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index 689f0da44..047900a32 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -246,6 +246,7 @@ fn infer_std_crash_5() {
// taken from rustc
check_infer(
r#"
+ //- minicore: iterator
fn extra_compiler_flags() {
for content in doesnt_matter {
let name = if doesnt_matter {
@@ -264,13 +265,22 @@ fn infer_std_crash_5() {
"#,
expect![[r#"
26..322 '{ ... } }': ()
+ 32..320 'for co... }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
+ 32..320 'for co... }': {unknown}
+ 32..320 'for co... }': !
+ 32..320 'for co... }': {unknown}
+ 32..320 'for co... }': &mut {unknown}
+ 32..320 'for co... }': fn next<{unknown}>(&mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
+ 32..320 'for co... }': Option<{unknown}>
+ 32..320 'for co... }': ()
+ 32..320 'for co... }': ()
32..320 'for co... }': ()
36..43 'content': {unknown}
47..60 'doesnt_matter': {unknown}
61..320 '{ ... }': ()
75..79 'name': &{unknown}
82..166 'if doe... }': &{unknown}
- 85..98 'doesnt_matter': {unknown}
+ 85..98 'doesnt_matter': bool
99..128 '{ ... }': &{unknown}
113..118 'first': &{unknown}
134..166 '{ ... }': &{unknown}
@@ -279,7 +289,7 @@ fn infer_std_crash_5() {
181..188 'content': &{unknown}
191..313 'if ICE... }': &{unknown}
194..231 'ICE_RE..._VALUE': {unknown}
- 194..247 'ICE_RE...&name)': {unknown}
+ 194..247 'ICE_RE...&name)': bool
241..246 '&name': &&{unknown}
242..246 'name': &{unknown}
248..276 '{ ... }': &{unknown}
@@ -805,19 +815,19 @@ fn issue_4966() {
225..229 'iter': T
244..246 '{}': Vec<A>
258..402 '{ ...r(); }': ()
- 268..273 'inner': Map<|&f64| -> f64>
- 276..300 'Map { ... 0.0 }': Map<|&f64| -> f64>
- 285..298 '|_: &f64| 0.0': |&f64| -> f64
+ 268..273 'inner': Map<impl Fn(&f64) -> f64>
+ 276..300 'Map { ... 0.0 }': Map<impl Fn(&f64) -> f64>
+ 285..298 '|_: &f64| 0.0': impl Fn(&f64) -> f64
286..287 '_': &f64
295..298 '0.0': f64
- 311..317 'repeat': Repeat<Map<|&f64| -> f64>>
- 320..345 'Repeat...nner }': Repeat<Map<|&f64| -> f64>>
- 338..343 'inner': Map<|&f64| -> f64>
- 356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
- 362..371 '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>>>>
- 362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
- 372..378 'repeat': Repeat<Map<|&f64| -> f64>>
- 386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<|&f64| -> f64>>>>
+ 311..317 'repeat': Repeat<Map<impl Fn(&f64) -> f64>>
+ 320..345 'Repeat...nner }': Repeat<Map<impl Fn(&f64) -> f64>>
+ 338..343 'inner': Map<impl Fn(&f64) -> f64>
+ 356..359 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
+ 362..371 'from_iter': fn from_iter<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>, Repeat<Map<impl Fn(&f64) -> f64>>>(Repeat<Map<impl Fn(&f64) -> f64>>) -> Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
+ 362..379 'from_i...epeat)': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
+ 372..378 'repeat': Repeat<Map<impl Fn(&f64) -> f64>>
+ 386..389 'vec': Vec<IntoIterator::Item<Repeat<Map<impl Fn(&f64) -> f64>>>>
386..399 'vec.foo_bar()': {unknown}
"#]],
);
@@ -852,7 +862,7 @@ fn main() {
123..126 'S()': S<i32>
132..133 's': S<i32>
132..144 's.g(|_x| {})': ()
- 136..143 '|_x| {}': |&i32| -> ()
+ 136..143 '|_x| {}': impl Fn(&i32)
137..139 '_x': &i32
141..143 '{}': ()
150..151 's': S<i32>
@@ -886,13 +896,13 @@ fn flush(&self) {
"#,
expect![[r#"
123..127 'self': &Mutex<T>
- 150..152 '{}': MutexGuard<T>
+ 150..152 '{}': MutexGuard<'_, T>
234..238 'self': &{unknown}
240..290 '{ ...()); }': ()
250..251 'w': &Mutex<BufWriter>
276..287 '*(w.lock())': BufWriter
278..279 'w': &Mutex<BufWriter>
- 278..286 'w.lock()': MutexGuard<BufWriter>
+ 278..286 'w.lock()': MutexGuard<'_, BufWriter>
"#]],
);
}
@@ -1060,7 +1070,7 @@ fn infix_parse<T, S>(_state: S, _level_code: &Fn(S)) -> T {
loop {}
}
-fn parse_arule() {
+fn parse_a_rule() {
infix_parse((), &(|_recurse| ()))
}
"#,
@@ -1068,6 +1078,23 @@ fn parse_arule() {
}
#[test]
+fn nested_closure() {
+ check_types(
+ r#"
+//- minicore: fn, option
+
+fn map<T, U>(o: Option<T>, f: impl FnOnce(T) -> U) -> Option<U> { loop {} }
+
+fn test() {
+ let o = Some(Some(2));
+ map(o, |s| map(s, |x| x));
+ // ^ i32
+}
+ "#,
+ );
+}
+
+#[test]
fn call_expected_type_closure() {
check_types(
r#"
@@ -1198,6 +1225,7 @@ fn mamba(a: U32!(), p: u32) -> u32 {
fn for_loop_block_expr_iterable() {
check_infer(
r#"
+//- minicore: iterator
fn test() {
for _ in { let x = 0; } {
let y = 0;
@@ -1206,8 +1234,17 @@ fn test() {
"#,
expect![[r#"
10..68 '{ ... } }': ()
+ 16..66 'for _ ... }': fn into_iter<()>(()) -> <() as IntoIterator>::IntoIter
+ 16..66 'for _ ... }': IntoIterator::IntoIter<()>
+ 16..66 'for _ ... }': !
+ 16..66 'for _ ... }': IntoIterator::IntoIter<()>
+ 16..66 'for _ ... }': &mut IntoIterator::IntoIter<()>
+ 16..66 'for _ ... }': fn next<IntoIterator::IntoIter<()>>(&mut IntoIterator::IntoIter<()>) -> Option<<IntoIterator::IntoIter<()> as Iterator>::Item>
+ 16..66 'for _ ... }': Option<Iterator::Item<IntoIterator::IntoIter<()>>>
+ 16..66 'for _ ... }': ()
16..66 'for _ ... }': ()
- 20..21 '_': {unknown}
+ 16..66 'for _ ... }': ()
+ 20..21 '_': Iterator::Item<IntoIterator::IntoIter<()>>
25..39 '{ let x = 0; }': ()
31..32 'x': i32
35..36 '0': i32
@@ -1458,13 +1495,12 @@ fn regression_11688_3() {
struct Ar<T, const N: u8>(T);
fn f<const LEN: usize, T, const BASE: u8>(
num_zeros: usize,
- ) -> dyn Iterator<Item = [Ar<T, BASE>; LEN]> {
+ ) -> &dyn Iterator<Item = [Ar<T, BASE>; LEN]> {
loop {}
}
fn dynamic_programming() {
- for board in f::<9, u8, 7>(1) {
- //^^^^^ [Ar<u8, 7>; 9]
- }
+ let board = f::<9, u8, 7>(1).next();
+ //^^^^^ Option<[Ar<u8, 7>; 9]>
}
"#,
);
@@ -1758,6 +1794,21 @@ const C: usize = 2 + 2;
}
#[test]
+fn regression_14456() {
+ check_types(
+ r#"
+//- minicore: future
+async fn x() {}
+fn f() {
+ let fut = x();
+ let t = [0u8; { let a = 2 + 2; a }];
+ //^ [u8; 4]
+}
+"#,
+ );
+}
+
+#[test]
fn regression_14164() {
check_types(
r#"
@@ -1788,3 +1839,142 @@ where
"#,
);
}
+
+#[test]
+fn match_ergonomics_with_binding_modes_interaction() {
+ check_types(
+ r"
+enum E { A }
+fn foo() {
+ match &E::A {
+ b @ (x @ E::A | x) => {
+ b;
+ //^ &E
+ x;
+ //^ &E
+ }
+ }
+}",
+ );
+}
+
+#[test]
+fn regression_14844() {
+ check_no_mismatches(
+ r#"
+pub type Ty = Unknown;
+
+pub struct Inner<T>();
+
+pub struct Outer {
+ pub inner: Inner<Ty>,
+}
+
+fn main() {
+ _ = Outer {
+ inner: Inner::<i32>(),
+ };
+}
+ "#,
+ );
+ check_no_mismatches(
+ r#"
+pub const ONE: usize = 1;
+
+pub struct Inner<const P: usize>();
+
+pub struct Outer {
+ pub inner: Inner<ONE>,
+}
+
+fn main() {
+ _ = Outer {
+ inner: Inner::<1>(),
+ };
+}
+ "#,
+ );
+ check_no_mismatches(
+ r#"
+pub const ONE: usize = unknown();
+
+pub struct Inner<const P: usize>();
+
+pub struct Outer {
+ pub inner: Inner<ONE>,
+}
+
+fn main() {
+ _ = Outer {
+ inner: Inner::<1>(),
+ };
+}
+ "#,
+ );
+ check_no_mismatches(
+ r#"
+pub const N: usize = 2 + 2;
+
+fn f(t: [u8; N]) {}
+
+fn main() {
+ let a = [1, 2, 3, 4];
+ f(a);
+ let b = [1; 4];
+ let c: [u8; N] = b;
+ let d = [1; N];
+ let e: [u8; N] = d;
+ let f = [1; N];
+ let g = match f {
+ [a, b, c, d] => a + b + c + d,
+ };
+}
+ "#,
+ );
+}
+
+#[test]
+fn regression_14844_2() {
+ check_no_mismatches(
+ r#"
+//- minicore: fn
+pub const ONE: usize = 1;
+
+pub type MyInner = Inner<ONE>;
+
+pub struct Inner<const P: usize>();
+
+impl Inner<1> {
+ fn map<F>(&self, func: F) -> bool
+ where
+ F: Fn(&MyInner) -> bool,
+ {
+ func(self)
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn dont_crash_on_slice_unsizing() {
+ check_no_mismatches(
+ r#"
+//- minicore: slice, unsize, coerce_unsized
+trait Tr {
+ fn f(self);
+}
+
+impl Tr for [i32] {
+ fn f(self) {
+ let t;
+ x(t);
+ }
+}
+
+fn x(a: [i32; 4]) {
+ let b = a.f();
+}
+ "#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 13cc3fea5..a0ff62843 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -854,9 +854,9 @@ fn test2(a1: *const A, a2: *mut A) {
237..239 'a2': *mut A
249..272 '{ ...2.b; }': ()
255..257 'a1': *const A
- 255..259 'a1.b': B
+ 255..259 'a1.b': {unknown}
265..267 'a2': *mut A
- 265..269 'a2.b': B
+ 265..269 'a2.b': {unknown}
"#]],
);
}
@@ -1812,6 +1812,52 @@ fn main() {
//^ [(); 7]
}"#,
);
+ check_types(
+ r#"
+trait Foo {
+ fn x(self);
+}
+
+impl Foo for u8 {
+ fn x(self) {
+ let t = [0; 4 + 2];
+ //^ [i32; 6]
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn const_eval_in_function_signature() {
+ check_types(
+ r#"
+const fn foo() -> usize {
+ 5
+}
+
+fn f() -> [u8; foo()] {
+ loop {}
+}
+
+fn main() {
+ let t = f();
+ //^ [u8; 5]
+}"#,
+ );
+ check_types(
+ r#"
+//- minicore: default, builtin_impls
+fn f() -> [u8; Default::default()] {
+ loop {}
+}
+
+fn main() {
+ let t = f();
+ //^ [u8; 0]
+}
+ "#,
+ );
}
#[test]
@@ -1906,8 +1952,8 @@ fn closure_return() {
"#,
expect![[r#"
16..58 '{ ...; }; }': u32
- 26..27 'x': || -> usize
- 30..55 '|| -> ...n 1; }': || -> usize
+ 26..27 'x': impl Fn() -> usize
+ 30..55 '|| -> ...n 1; }': impl Fn() -> usize
42..55 '{ return 1; }': usize
44..52 'return 1': !
51..52 '1': usize
@@ -1925,8 +1971,8 @@ fn closure_return_unit() {
"#,
expect![[r#"
16..47 '{ ...; }; }': u32
- 26..27 'x': || -> ()
- 30..44 '|| { return; }': || -> ()
+ 26..27 'x': impl Fn()
+ 30..44 '|| { return; }': impl Fn()
33..44 '{ return; }': ()
35..41 'return': !
"#]],
@@ -1943,8 +1989,8 @@ fn closure_return_inferred() {
"#,
expect![[r#"
16..46 '{ ..." }; }': u32
- 26..27 'x': || -> &str
- 30..43 '|| { "test" }': || -> &str
+ 26..27 'x': impl Fn() -> &str
+ 30..43 '|| { "test" }': impl Fn() -> &str
33..43 '{ "test" }': &str
35..41 '"test"': &str
"#]],
@@ -2034,6 +2080,56 @@ fn test() {
}
#[test]
+fn tuple_pattern_nested_match_ergonomics() {
+ check_no_mismatches(
+ r#"
+fn f(x: (&i32, &i32)) -> i32 {
+ match x {
+ (3, 4) => 5,
+ _ => 12,
+ }
+}
+ "#,
+ );
+ check_types(
+ r#"
+fn f(x: (&&&&i32, &&&i32)) {
+ let f = match x {
+ t @ (3, 4) => t,
+ _ => loop {},
+ };
+ f;
+ //^ (&&&&i32, &&&i32)
+}
+ "#,
+ );
+ check_types(
+ r#"
+fn f() {
+ let x = &&&(&&&2, &&&&&3);
+ let (y, z) = x;
+ //^ &&&&i32
+ let t @ (y, z) = x;
+ t;
+ //^ &&&(&&&i32, &&&&&i32)
+}
+ "#,
+ );
+ check_types(
+ r#"
+fn f() {
+ let x = &&&(&&&2, &&&&&3);
+ let (y, z) = x;
+ //^ &&&&i32
+ let t @ (y, z) = x;
+ t;
+ //^ &&&(&&&i32, &&&&&i32)
+}
+ "#,
+ );
+}
+
+#[test]
fn fn_pointer_return() {
check_infer(
r#"
@@ -2050,7 +2146,7 @@ fn fn_pointer_return() {
47..120 '{ ...hod; }': ()
57..63 'vtable': Vtable
66..90 'Vtable...| {} }': Vtable
- 83..88 '|| {}': || -> ()
+ 83..88 '|| {}': impl Fn()
86..88 '{}': ()
100..101 'm': fn()
104..110 'vtable': Vtable
@@ -2087,6 +2183,7 @@ async fn main() {
136..138 '()': ()
150..151 'w': i32
154..166 'const { 92 }': i32
+ 154..166 'const { 92 }': i32
162..164 '92': i32
176..177 't': i32
180..190 ''a: { 92 }': i32
@@ -2094,6 +2191,24 @@ async fn main() {
"#]],
)
}
+
+#[test]
+fn async_fn_and_try_operator() {
+ check_no_mismatches(
+ r#"
+//- minicore: future, result, fn, try, from
+async fn foo() -> Result<(), ()> {
+ Ok(())
+}
+
+async fn bar() -> Result<(), ()> {
+ let x = foo().await?;
+ Ok(x)
+}
+ "#,
+ )
+}
+
#[test]
fn async_block_early_return() {
check_infer(
@@ -2124,9 +2239,9 @@ fn main() {
149..151 'Ok': Ok<(), ()>(()) -> Result<(), ()>
149..155 'Ok(())': Result<(), ()>
152..154 '()': ()
- 167..171 'test': fn test<(), (), || -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(|| -> impl Future<Output = Result<(), ()>>)
+ 167..171 'test': fn test<(), (), impl Fn() -> impl Future<Output = Result<(), ()>>, impl Future<Output = Result<(), ()>>>(impl Fn() -> impl Future<Output = Result<(), ()>>)
167..228 'test(|... })': ()
- 172..227 '|| asy... }': || -> impl Future<Output = Result<(), ()>>
+ 172..227 '|| asy... }': impl Fn() -> impl Future<Output = Result<(), ()>>
175..227 'async ... }': impl Future<Output = Result<(), ()>>
191..205 'return Err(())': !
198..201 'Err': Err<(), ()>(()) -> Result<(), ()>
@@ -2252,8 +2367,8 @@ fn infer_labelled_break_with_val() {
"#,
expect![[r#"
9..335 '{ ... }; }': ()
- 19..21 '_x': || -> bool
- 24..332 '|| 'ou... }': || -> bool
+ 19..21 '_x': impl Fn() -> bool
+ 24..332 '|| 'ou... }': impl Fn() -> bool
27..332 ''outer... }': bool
40..332 '{ ... }': ()
54..59 'inner': i8
@@ -2678,6 +2793,179 @@ impl B for Astruct {}
}
#[test]
+fn capture_kinds_simple() {
+ check_types(
+ r#"
+struct S;
+
+impl S {
+ fn read(&self) -> &S { self }
+ fn write(&mut self) -> &mut S { self }
+ fn consume(self) -> S { self }
+}
+
+fn f() {
+ let x = S;
+ let c1 = || x.read();
+ //^^ impl Fn() -> &S
+ let c2 = || x.write();
+ //^^ impl FnMut() -> &mut S
+ let c3 = || x.consume();
+ //^^ impl FnOnce() -> S
+ let c3 = || x.consume().consume().consume();
+ //^^ impl FnOnce() -> S
+ let c3 = || x.consume().write().read();
+ //^^ impl FnOnce() -> &S
+ let x = &mut x;
+ let c1 = || x.write();
+ //^^ impl FnMut() -> &mut S
+ let x = S;
+ let c1 = || { let ref t = x; t };
+ //^^ impl Fn() -> &S
+ let c2 = || { let ref mut t = x; t };
+ //^^ impl FnMut() -> &mut S
+ let c3 = || { let t = x; t };
+ //^^ impl FnOnce() -> S
+}
+ "#,
+ )
+}
+
+#[test]
+fn capture_kinds_closure() {
+ check_types(
+ r#"
+//- minicore: copy, fn
+fn f() {
+ let mut x = 2;
+ x = 5;
+ let mut c1 = || { x = 3; x };
+ //^^^^^^ impl FnMut() -> i32
+ let mut c2 = || { c1() };
+ //^^^^^^ impl FnMut() -> i32
+ let mut c1 = || { x };
+ //^^^^^^ impl Fn() -> i32
+ let mut c2 = || { c1() };
+ //^^^^^^ impl Fn() -> i32
+ struct X;
+ let x = X;
+ let mut c1 = || { x };
+ //^^^^^^ impl FnOnce() -> X
+ let mut c2 = || { c1() };
+ //^^^^^^ impl FnOnce() -> X
+}
+ "#,
+ );
+}
+
+#[test]
+fn capture_kinds_overloaded_deref() {
+ check_types(
+ r#"
+//- minicore: fn, deref_mut
+use core::ops::{Deref, DerefMut};
+
+struct Foo;
+impl Deref for Foo {
+ type Target = (i32, u8);
+ fn deref(&self) -> &(i32, u8) {
+ &(5, 2)
+ }
+}
+impl DerefMut for Foo {
+ fn deref_mut(&mut self) -> &mut (i32, u8) {
+ &mut (5, 2)
+ }
+}
+fn test() {
+ let mut x = Foo;
+ let c1 = || *x;
+ //^^ impl Fn() -> (i32, u8)
+ let c2 = || { *x = (2, 5); };
+ //^^ impl FnMut()
+ let c3 = || { x.1 };
+ //^^ impl Fn() -> u8
+ let c4 = || { x.1 = 6; };
+ //^^ impl FnMut()
+}
+ "#,
+ );
+}
+
+#[test]
+fn capture_kinds_with_copy_types() {
+ check_types(
+ r#"
+//- minicore: copy, clone, derive
+#[derive(Clone, Copy)]
+struct Copy;
+struct NotCopy;
+#[derive(Clone, Copy)]
+struct Generic<T>(T);
+
+trait Tr {
+ type Assoc;
+}
+
+impl Tr for Copy {
+ type Assoc = NotCopy;
+}
+
+#[derive(Clone, Copy)]
+struct AssocGeneric<T: Tr>(T::Assoc);
+
+fn f() {
+ let a = Copy;
+ let b = NotCopy;
+ let c = Generic(Copy);
+ let d = Generic(NotCopy);
+ let e: AssocGeneric<Copy> = AssocGeneric(NotCopy);
+ let c1 = || a;
+ //^^ impl Fn() -> Copy
+ let c2 = || b;
+ //^^ impl FnOnce() -> NotCopy
+ let c3 = || c;
+ //^^ impl Fn() -> Generic<Copy>
+ let c3 = || d;
+ //^^ impl FnOnce() -> Generic<NotCopy>
+ let c3 = || e;
+ //^^ impl FnOnce() -> AssocGeneric<Copy>
+}
+ "#,
+ )
+}
+
+#[test]
+fn derive_macro_should_work_for_associated_type() {
+ check_types(
+ r#"
+//- minicore: copy, clone, derive
+#[derive(Clone)]
+struct X;
+#[derive(Clone)]
+struct Y;
+
+trait Tr {
+ type Assoc;
+}
+
+impl Tr for X {
+ type Assoc = Y;
+}
+
+#[derive(Clone)]
+struct AssocGeneric<T: Tr>(T::Assoc);
+
+fn f() {
+ let e: AssocGeneric<X> = AssocGeneric(Y);
+ let e_clone = e.clone();
+ //^^^^^^^ AssocGeneric<X>
+}
+ "#,
+ )
+}
+
+#[test]
fn cfgd_out_assoc_items() {
check_types(
r#"
@@ -2697,6 +2985,21 @@ fn f() {
}
#[test]
+fn infer_ref_to_raw_cast() {
+ check_types(
+ r#"
+struct S;
+
+fn f() {
+ let s = &mut S;
+ let s = s as *mut _;
+ //^ *mut S
+}
+ "#,
+ );
+}
+
+#[test]
fn infer_missing_type() {
check_types(
r#"
@@ -3194,6 +3497,22 @@ fn func() {
);
}
+#[test]
+fn pointee_trait() {
+ check_types(
+ r#"
+//- minicore: pointee
+use core::ptr::Pointee;
+fn func() {
+ let x: <u8 as Pointee>::Metadata;
+ //^ ()
+ let x: <[u8] as Pointee>::Metadata;
+ //^ usize
+}
+ "#,
+ );
+}
+
// FIXME
#[test]
fn castable_to() {
@@ -3258,35 +3577,60 @@ fn f<T>(t: Ark<T>) {
);
}
-// FIXME
#[test]
-fn castable_to2() {
- check_infer(
+fn const_dependent_on_local() {
+ check_types(
r#"
-fn func() {
- let x = &0u32 as *const _;
+fn main() {
+ let s = 5;
+ let t = [2; s];
+ //^ [i32; _]
}
"#,
- expect![[r#"
- 10..44 '{ ...t _; }': ()
- 20..21 'x': *const {unknown}
- 24..29 '&0u32': &u32
- 24..41 '&0u32 ...onst _': *const {unknown}
- 25..29 '0u32': u32
- "#]],
);
}
#[test]
fn issue_14275() {
- // FIXME: evaluate const generic
check_types(
r#"
struct Foo<const T: bool>;
fn main() {
const B: bool = false;
let foo = Foo::<B>;
- //^^^ Foo<_>
+ //^^^ Foo<false>
+}
+"#,
+ );
+ check_types(
+ r#"
+struct Foo<const T: bool>;
+impl Foo<true> {
+ fn foo(self) -> u8 { 2 }
+}
+impl Foo<false> {
+ fn foo(self) -> u16 { 5 }
+}
+fn main() {
+ const B: bool = false;
+ let foo: Foo<B> = Foo;
+ let x = foo.foo();
+ //^ u16
+}
+"#,
+ );
+}
+
+#[test]
+fn cstring_literals() {
+ check_types(
+ r#"
+#[lang = "CStr"]
+pub struct CStr;
+
+fn main() {
+ c"ello";
+ //^^^^^^^ &CStr
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index da76d7fd8..97ae732a9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -90,7 +90,7 @@ fn infer_async_closure() {
async fn test() {
let f = async move |x: i32| x + 42;
f;
-// ^ |i32| -> impl Future<Output = i32>
+// ^ impl Fn(i32) -> impl Future<Output = i32>
let a = f(4);
a;
// ^ impl Future<Output = i32>
@@ -99,7 +99,7 @@ async fn test() {
// ^ i32
let f = async move || 42;
f;
-// ^ || -> impl Future<Output = i32>
+// ^ impl Fn() -> impl Future<Output = i32>
let a = f();
a;
// ^ impl Future<Output = i32>
@@ -116,7 +116,7 @@ async fn test() {
};
let _: Option<u64> = c().await;
c;
-// ^ || -> impl Future<Output = Option<u64>>
+// ^ impl Fn() -> impl Future<Output = Option<u64>>
}
"#,
);
@@ -206,19 +206,27 @@ fn test() {
fn infer_try_trait() {
check_types(
r#"
-//- minicore: try, result
+//- minicore: try, result, from
fn test() {
let r: Result<i32, u64> = Result::Ok(1);
let v = r?;
v;
} //^ i32
-
-impl<O, E> core::ops::Try for Result<O, E> {
- type Output = O;
- type Error = Result<core::convert::Infallible, E>;
+"#,
+ );
}
-impl<T, E, F: From<E>> core::ops::FromResidual<Result<core::convert::Infallible, E>> for Result<T, F> {}
+#[test]
+fn infer_try_block() {
+ // FIXME: We should test more cases, but it currently doesn't work, since
+ // our labeled block type inference is broken.
+ check_types(
+ r#"
+//- minicore: try, option
+fn test() {
+ let x: Option<_> = try { Some(2)?; };
+ //^ Option<()>
+}
"#,
);
}
@@ -542,7 +550,7 @@ fn test() -> u64 {
53..54 'a': S
57..58 'S': S(fn(u32) -> u64) -> S
57..74 'S(|i| ...s u64)': S
- 59..73 '|i| 2*i as u64': |u32| -> u64
+ 59..73 '|i| 2*i as u64': impl Fn(u32) -> u64
60..61 'i': u32
63..64 '2': u64
63..73 '2*i as u64': u64
@@ -1325,9 +1333,9 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) {
}
"#,
expect![[r#"
- 134..165 '{ ...(C)) }': (|&str, T| -> (), Bar<u8>)
- 140..163 '(|inpu...ar(C))': (|&str, T| -> (), Bar<u8>)
- 141..154 '|input, t| {}': |&str, T| -> ()
+ 134..165 '{ ...(C)) }': (impl Fn(&str, T), Bar<u8>)
+ 140..163 '(|inpu...ar(C))': (impl Fn(&str, T), Bar<u8>)
+ 141..154 '|input, t| {}': impl Fn(&str, T)
142..147 'input': &str
149..150 't': T
152..154 '{}': ()
@@ -1498,8 +1506,8 @@ fn main() {
71..105 '{ ...()); }': ()
77..78 'f': fn f(&dyn Fn(S))
77..102 'f(&|nu...foo())': ()
- 79..101 '&|numb....foo()': &|S| -> ()
- 80..101 '|numbe....foo()': |S| -> ()
+ 79..101 '&|numb....foo()': &impl Fn(S)
+ 80..101 '|numbe....foo()': impl Fn(S)
81..87 'number': S
89..95 'number': S
89..101 'number.foo()': ()
@@ -1904,13 +1912,13 @@ fn test() {
131..132 'f': F
151..153 '{}': Lazy<T, F>
251..497 '{ ...o(); }': ()
- 261..266 'lazy1': Lazy<Foo, || -> Foo>
- 283..292 'Lazy::new': fn new<Foo, || -> Foo>(|| -> Foo) -> Lazy<Foo, || -> Foo>
- 283..300 'Lazy::...| Foo)': Lazy<Foo, || -> Foo>
- 293..299 '|| Foo': || -> Foo
+ 261..266 'lazy1': Lazy<Foo, impl Fn() -> Foo>
+ 283..292 'Lazy::new': fn new<Foo, impl Fn() -> Foo>(impl Fn() -> Foo) -> Lazy<Foo, impl Fn() -> Foo>
+ 283..300 'Lazy::...| Foo)': Lazy<Foo, impl Fn() -> Foo>
+ 293..299 '|| Foo': impl Fn() -> Foo
296..299 'Foo': Foo
310..312 'r1': usize
- 315..320 'lazy1': Lazy<Foo, || -> Foo>
+ 315..320 'lazy1': Lazy<Foo, impl Fn() -> Foo>
315..326 'lazy1.foo()': usize
368..383 'make_foo_fn_ptr': fn() -> Foo
399..410 'make_foo_fn': fn make_foo_fn() -> Foo
@@ -1955,20 +1963,20 @@ fn test() {
163..167 '1u32': u32
174..175 'x': Option<u32>
174..190 'x.map(...v + 1)': Option<u32>
- 180..189 '|v| v + 1': |u32| -> u32
+ 180..189 '|v| v + 1': impl Fn(u32) -> u32
181..182 'v': u32
184..185 'v': u32
184..189 'v + 1': u32
188..189 '1': u32
196..197 'x': Option<u32>
196..212 'x.map(... 1u64)': Option<u64>
- 202..211 '|_v| 1u64': |u32| -> u64
+ 202..211 '|_v| 1u64': impl Fn(u32) -> u64
203..205 '_v': u32
207..211 '1u64': u64
222..223 'y': Option<i64>
239..240 'x': Option<u32>
239..252 'x.map(|_v| 1)': Option<i64>
- 245..251 '|_v| 1': |u32| -> i64
+ 245..251 '|_v| 1': impl Fn(u32) -> i64
246..248 '_v': u32
250..251 '1': i64
"#]],
@@ -1997,11 +2005,11 @@ fn test<F: FnOnce(u32) -> u64>(f: F) {
//^^^^ u64
let g = |v| v + 1;
//^^^^^ u64
- //^^^^^^^^^ |u64| -> u64
+ //^^^^^^^^^ impl Fn(u64) -> u64
g(1u64);
//^^^^^^^ u64
let h = |v| 1u128 + v;
- //^^^^^^^^^^^^^ |u128| -> u128
+ //^^^^^^^^^^^^^ impl Fn(u128) -> u128
}"#,
);
}
@@ -2054,17 +2062,17 @@ fn test() {
312..314 '{}': ()
330..489 '{ ... S); }': ()
340..342 'x1': u64
- 345..349 'foo1': fn foo1<S, u64, |S| -> u64>(S, |S| -> u64) -> u64
+ 345..349 'foo1': fn foo1<S, u64, impl Fn(S) -> u64>(S, impl Fn(S) -> u64) -> u64
345..368 'foo1(S...hod())': u64
350..351 'S': S
- 353..367 '|s| s.method()': |S| -> u64
+ 353..367 '|s| s.method()': impl Fn(S) -> u64
354..355 's': S
357..358 's': S
357..367 's.method()': u64
378..380 'x2': u64
- 383..387 'foo2': fn foo2<S, u64, |S| -> u64>(|S| -> u64, S) -> u64
+ 383..387 'foo2': fn foo2<S, u64, impl Fn(S) -> u64>(impl Fn(S) -> u64, S) -> u64
383..406 'foo2(|...(), S)': u64
- 388..402 '|s| s.method()': |S| -> u64
+ 388..402 '|s| s.method()': impl Fn(S) -> u64
389..390 's': S
392..393 's': S
392..402 's.method()': u64
@@ -2073,14 +2081,14 @@ fn test() {
421..422 'S': S
421..446 'S.foo1...hod())': u64
428..429 'S': S
- 431..445 '|s| s.method()': |S| -> u64
+ 431..445 '|s| s.method()': impl Fn(S) -> u64
432..433 's': S
435..436 's': S
435..445 's.method()': u64
456..458 'x4': u64
461..462 'S': S
461..486 'S.foo2...(), S)': u64
- 468..482 '|s| s.method()': |S| -> u64
+ 468..482 '|s| s.method()': impl Fn(S) -> u64
469..470 's': S
472..473 's': S
472..482 's.method()': u64
@@ -2554,9 +2562,9 @@ fn main() {
72..74 '_v': F
117..120 '{ }': ()
132..163 '{ ... }); }': ()
- 138..148 'f::<(), _>': fn f<(), |&()| -> ()>(|&()| -> ())
+ 138..148 'f::<(), _>': fn f<(), impl Fn(&())>(impl Fn(&()))
138..160 'f::<()... z; })': ()
- 149..159 '|z| { z; }': |&()| -> ()
+ 149..159 '|z| { z; }': impl Fn(&())
150..151 'z': &()
153..159 '{ z; }': ()
155..156 'z': &()
@@ -2713,9 +2721,9 @@ fn main() {
983..998 'Vec::<i32>::new': fn new<i32>() -> Vec<i32>
983..1000 'Vec::<...:new()': Vec<i32>
983..1012 'Vec::<...iter()': IntoIter<i32>
- 983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, |i32| -> Option<u32>>
+ 983..1075 'Vec::<...one })': FilterMap<IntoIter<i32>, impl Fn(i32) -> Option<u32>>
983..1101 'Vec::<... y; })': ()
- 1029..1074 '|x| if...None }': |i32| -> Option<u32>
+ 1029..1074 '|x| if...None }': impl Fn(i32) -> Option<u32>
1030..1031 'x': i32
1033..1074 'if x >...None }': Option<u32>
1036..1037 'x': i32
@@ -2728,7 +2736,7 @@ fn main() {
1049..1057 'x as u32': u32
1066..1074 '{ None }': Option<u32>
1068..1072 'None': Option<u32>
- 1090..1100 '|y| { y; }': |u32| -> ()
+ 1090..1100 '|y| { y; }': impl Fn(u32)
1091..1092 'y': u32
1094..1100 '{ y; }': ()
1096..1097 'y': u32
@@ -2971,13 +2979,13 @@ fn foo() {
52..126 '{ ...)(s) }': ()
62..63 's': Option<i32>
66..78 'Option::None': Option<i32>
- 88..89 'f': |Option<i32>| -> ()
- 92..111 '|x: Op...2>| {}': |Option<i32>| -> ()
+ 88..89 'f': impl Fn(Option<i32>)
+ 92..111 '|x: Op...2>| {}': impl Fn(Option<i32>)
93..94 'x': Option<i32>
109..111 '{}': ()
117..124 '(&f)(s)': ()
- 118..120 '&f': &|Option<i32>| -> ()
- 119..120 'f': |Option<i32>| -> ()
+ 118..120 '&f': &impl Fn(Option<i32>)
+ 119..120 'f': impl Fn(Option<i32>)
122..123 's': Option<i32>
"#]],
);
@@ -3043,7 +3051,7 @@ impl<T: ?Sized> core::ops::Deref for Box<T> {
type Target = T;
fn deref(&self) -> &T {
- &self.inner
+ unsafe { &*self.inner }
}
}
@@ -3054,23 +3062,25 @@ fn foo() {
}"#,
expect![[r#"
154..158 'self': &Box<T>
- 166..193 '{ ... }': &T
- 176..187 '&self.inner': &*mut T
- 177..181 'self': &Box<T>
- 177..187 'self.inner': *mut T
- 206..296 '{ ...&s); }': ()
- 216..217 's': Option<i32>
- 220..224 'None': Option<i32>
- 234..235 'f': Box<dyn FnOnce(&Option<i32>)>
- 269..282 'box (|ps| {})': Box<|&Option<i32>| -> ()>
- 274..281 '|ps| {}': |&Option<i32>| -> ()
- 275..277 'ps': &Option<i32>
- 279..281 '{}': ()
- 288..289 'f': Box<dyn FnOnce(&Option<i32>)>
- 288..293 'f(&s)': ()
- 290..292 '&s': &Option<i32>
- 291..292 's': Option<i32>
- 269..282: expected Box<dyn FnOnce(&Option<i32>)>, got Box<|&Option<i32>| -> ()>
+ 166..205 '{ ... }': &T
+ 176..199 'unsafe...nner }': &T
+ 185..197 '&*self.inner': &T
+ 186..197 '*self.inner': T
+ 187..191 'self': &Box<T>
+ 187..197 'self.inner': *mut T
+ 218..308 '{ ...&s); }': ()
+ 228..229 's': Option<i32>
+ 232..236 'None': Option<i32>
+ 246..247 'f': Box<dyn FnOnce(&Option<i32>)>
+ 281..294 'box (|ps| {})': Box<impl Fn(&Option<i32>)>
+ 286..293 '|ps| {}': impl Fn(&Option<i32>)
+ 287..289 'ps': &Option<i32>
+ 291..293 '{}': ()
+ 300..301 'f': Box<dyn FnOnce(&Option<i32>)>
+ 300..305 'f(&s)': ()
+ 302..304 '&s': &Option<i32>
+ 303..304 's': Option<i32>
+ 281..294: expected Box<dyn FnOnce(&Option<i32>)>, got Box<impl Fn(&Option<i32>)>
"#]],
);
}
@@ -3709,7 +3719,6 @@ async fn get_accounts() -> Result<u32, ()> {
#[test]
fn local_impl_1() {
- check!(block_local_impls);
check_types(
r#"
trait Trait<T> {
@@ -3731,7 +3740,6 @@ fn test() {
#[test]
fn local_impl_2() {
- check!(block_local_impls);
check_types(
r#"
struct S;
@@ -3753,7 +3761,6 @@ fn test() {
#[test]
fn local_impl_3() {
- check!(block_local_impls);
check_types(
r#"
trait Trait<T> {
@@ -3778,6 +3785,62 @@ fn test() {
}
#[test]
+fn foreign_trait_with_local_trait_impl() {
+ check!(block_local_impls);
+ check(
+ r#"
+mod module {
+ pub trait T {
+ const C: usize;
+ fn f(&self);
+ }
+}
+
+fn f() {
+ use module::T;
+ impl T for usize {
+ const C: usize = 0;
+ fn f(&self) {}
+ }
+ 0usize.f();
+ //^^^^^^^^^^ type: ()
+ usize::C;
+ //^^^^^^^^type: usize
+}
+"#,
+ );
+}
+
+#[test]
+fn regression_14443_trait_solve() {
+ check_no_mismatches(
+ r#"
+trait T {
+ fn f(&self) {}
+}
+
+
+fn main() {
+ struct A;
+ impl T for A {}
+
+ let a = A;
+
+ let b = {
+ struct B;
+ impl T for B {}
+
+ B
+ };
+
+ a.f();
+ b.f();
+}
+"#,
+ )
+}
+
+#[test]
fn associated_type_sized_bounds() {
check_infer(
r#"
@@ -4149,3 +4212,201 @@ fn test() {
"#,
);
}
+
+#[test]
+fn associated_type_in_struct_expr_path() {
+ // FIXME: All annotation should be resolvable.
+ // For lines marked as unstable, see rust-lang/rust#86935.
+ // FIXME: Remove the comments once stablized.
+ check_types(
+ r#"
+trait Trait {
+ type Assoc;
+ fn f();
+}
+
+struct S { x: u32 }
+
+impl Trait for () {
+ type Assoc = S;
+
+ fn f() {
+ let x = 42;
+ let a = Self::Assoc { x };
+ // ^ S
+ let a = <Self>::Assoc { x }; // unstable
+ // ^ {unknown}
+
+ // should be `Copy` but we don't track ownership anyway.
+ let value = S { x };
+ if let Self::Assoc { x } = value {}
+ // ^ u32
+ if let <Self>::Assoc { x } = value {} // unstable
+ // ^ {unknown}
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn associated_type_in_struct_expr_path_enum() {
+ // FIXME: All annotation should be resolvable.
+ // For lines marked as unstable, see rust-lang/rust#86935.
+ // FIXME: Remove the comments once stablized.
+ check_types(
+ r#"
+trait Trait {
+ type Assoc;
+ fn f();
+}
+
+enum E {
+ Unit,
+ Struct { x: u32 },
+}
+
+impl Trait for () {
+ type Assoc = E;
+
+ fn f() {
+ let a = Self::Assoc::Unit;
+ // ^ E
+ let a = <Self>::Assoc::Unit;
+ // ^ E
+ let a = <Self::Assoc>::Unit;
+ // ^ E
+ let a = <<Self>::Assoc>::Unit;
+ // ^ E
+
+ // should be `Copy` but we don't track ownership anyway.
+ let value = E::Unit;
+ if let Self::Assoc::Unit = value {}
+ // ^^^^^^^^^^^^^^^^^ E
+ if let <Self>::Assoc::Unit = value {}
+ // ^^^^^^^^^^^^^^^^^^^ E
+ if let <Self::Assoc>::Unit = value {}
+ // ^^^^^^^^^^^^^^^^^^^ E
+ if let <<Self>::Assoc>::Unit = value {}
+ // ^^^^^^^^^^^^^^^^^^^^^ E
+
+ let x = 42;
+ let a = Self::Assoc::Struct { x };
+ // ^ E
+ let a = <Self>::Assoc::Struct { x }; // unstable
+ // ^ {unknown}
+ let a = <Self::Assoc>::Struct { x }; // unstable
+ // ^ {unknown}
+ let a = <<Self>::Assoc>::Struct { x }; // unstable
+ // ^ {unknown}
+
+ // should be `Copy` but we don't track ownership anyway.
+ let value = E::Struct { x: 42 };
+ if let Self::Assoc::Struct { x } = value {}
+ // ^ u32
+ if let <Self>::Assoc::Struct { x } = value {} // unstable
+ // ^ {unknown}
+ if let <Self::Assoc>::Struct { x } = value {} // unstable
+ // ^ {unknown}
+ if let <<Self>::Assoc>::Struct { x } = value {} // unstable
+ // ^ {unknown}
+ }
+}
+ "#,
+ );
+}
+
+#[test]
+fn derive_macro_bounds() {
+ check_types(
+ r#"
+ //- minicore: clone, derive
+ #[derive(Clone)]
+ struct Copy;
+ struct NotCopy;
+ #[derive(Clone)]
+ struct Generic<T>(T);
+ trait Tr {
+ type Assoc;
+ }
+ impl Tr for Copy {
+ type Assoc = NotCopy;
+ }
+ #[derive(Clone)]
+ struct AssocGeneric<T: Tr>(T::Assoc);
+
+ // Currently rustc does not accept this.
+ // #[derive(Clone)]
+ // struct AssocGeneric2<T: Tr>(<T as Tr>::Assoc);
+
+ #[derive(Clone)]
+ struct AssocGeneric3<T: Tr>(Generic<T::Assoc>);
+
+ #[derive(Clone)]
+ struct Vec<T>();
+
+ #[derive(Clone)]
+ struct R1(Vec<R2>);
+ #[derive(Clone)]
+ struct R2(R1);
+
+ fn f() {
+ let x = (&Copy).clone();
+ //^ Copy
+ let x = (&NotCopy).clone();
+ //^ &NotCopy
+ let x = (&Generic(Copy)).clone();
+ //^ Generic<Copy>
+ let x = (&Generic(NotCopy)).clone();
+ //^ &Generic<NotCopy>
+ let x: &AssocGeneric<Copy> = &AssocGeneric(NotCopy);
+ let x = x.clone();
+ //^ &AssocGeneric<Copy>
+ // let x: &AssocGeneric2<Copy> = &AssocGeneric2(NotCopy);
+ // let x = x.clone();
+ let x: &AssocGeneric3<Copy> = &AssocGeneric3(Generic(NotCopy));
+ let x = x.clone();
+ //^ &AssocGeneric3<Copy>
+ let x = (&R1(Vec())).clone();
+ //^ R1
+ let x = (&R2(R1(Vec()))).clone();
+ //^ R2
+ }
+ "#,
+ );
+}
+
+#[test]
+fn trait_obligations_should_be_registered_during_path_inference() {
+ check_types(
+ r#"
+//- minicore: fn, from
+struct S<T>(T);
+fn map<T, U, F: FnOnce(T) -> S<U>>(_: T, _: F) -> U { loop {} }
+
+fn test(v: S<i32>) {
+ let res = map(v, Into::into);
+ //^^^ i32
+}
+"#,
+ );
+}
+
+#[test]
+fn fn_obligation_should_be_registered_during_path_inference() {
+ check_types(
+ r#"
+//- minicore: fn, from
+struct S<T>(T);
+impl<T> S<T> {
+ fn foo<U: Into<S<T>>>(_: U) -> Self { loop {} }
+}
+fn map<T, U, F: FnOnce(T) -> U>(_: T, _: F) -> U { loop {} }
+
+fn test(v: S<i32>) {
+ let res = map(v, S::foo);
+ //^^^ S<i32>
+}
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
index b7e6ee674..83814ed0e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs
@@ -24,7 +24,8 @@ impl DebugContext<'_> {
AdtId::UnionId(it) => self.0.union_data(it).name.clone(),
AdtId::EnumId(it) => self.0.enum_data(it).name.clone(),
};
- name.fmt(f)
+ name.display(self.0.upcast()).fmt(f)?;
+ Ok(())
}
pub(crate) fn debug_trait_id(
@@ -34,7 +35,8 @@ impl DebugContext<'_> {
) -> Result<(), fmt::Error> {
let trait_: hir_def::TraitId = from_chalk_trait_id(id);
let trait_data = self.0.trait_data(trait_);
- trait_data.name.fmt(f)
+ trait_data.name.display(self.0.upcast()).fmt(f)?;
+ Ok(())
}
pub(crate) fn debug_assoc_type_id(
@@ -49,7 +51,13 @@ impl DebugContext<'_> {
_ => panic!("associated type not in trait"),
};
let trait_data = self.0.trait_data(trait_);
- write!(fmt, "{}::{}", trait_data.name, type_alias_data.name)
+ write!(
+ fmt,
+ "{}::{}",
+ trait_data.name.display(self.0.upcast()),
+ type_alias_data.name.display(self.0.upcast())
+ )?;
+ Ok(())
}
pub(crate) fn debug_projection_ty(
@@ -67,7 +75,7 @@ impl DebugContext<'_> {
let trait_ref = projection_ty.trait_ref(self.0);
let trait_params = trait_ref.substitution.as_slice(Interner);
let self_ty = trait_ref.self_type_parameter(Interner);
- write!(fmt, "<{self_ty:?} as {trait_name}")?;
+ write!(fmt, "<{self_ty:?} as {}", trait_name.display(self.0.upcast()))?;
if trait_params.len() > 1 {
write!(
fmt,
@@ -75,7 +83,7 @@ impl DebugContext<'_> {
trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))),
)?;
}
- write!(fmt, ">::{}", type_alias_data.name)?;
+ write!(fmt, ">::{}", type_alias_data.name.display(self.0.upcast()))?;
let proj_params_count = projection_ty.substitution.len(Interner) - trait_params.len();
let proj_params = &projection_ty.substitution.as_slice(Interner)[..proj_params_count];
@@ -105,9 +113,9 @@ impl DebugContext<'_> {
}
};
match def {
- CallableDefId::FunctionId(_) => write!(fmt, "{{fn {name}}}"),
+ CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name.display(self.0.upcast())),
CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {
- write!(fmt, "{{ctor {name}}}")
+ write!(fmt, "{{ctor {}}}", name.display(self.0.upcast()))
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
index 3ab85c68f..f40b7db3a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
@@ -1,22 +1,24 @@
//! Trait solving using Chalk.
-use std::{env::var, sync::Arc};
+use std::env::var;
-use chalk_ir::GoalData;
+use chalk_ir::{fold::TypeFoldable, DebruijnIndex, GoalData};
use chalk_recursive::Cache;
-use chalk_solve::{logging_db::LoggingRustIrDatabase, Solver};
+use chalk_solve::{logging_db::LoggingRustIrDatabase, rust_ir, Solver};
use base_db::CrateId;
use hir_def::{
lang_item::{LangItem, LangItemTarget},
- TraitId,
+ BlockId, TraitId,
};
+use hir_expand::name::{name, Name};
use stdx::panic_context;
+use triomphe::Arc;
use crate::{
- db::HirDatabase, infer::unify::InferenceTable, AliasEq, AliasTy, Canonical, DomainGoal, Goal,
- Guidance, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, Solution, TraitRefExt, Ty,
- TyKind, WhereClause,
+ db::HirDatabase, infer::unify::InferenceTable, utils::UnevaluatedConstEvaluatorFolder, AliasEq,
+ AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, Interner, ProjectionTy,
+ ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, WhereClause,
};
/// This controls how much 'time' we give the Chalk solver before giving up.
@@ -26,6 +28,7 @@ const CHALK_SOLVER_FUEL: i32 = 1000;
pub(crate) struct ChalkContext<'a> {
pub(crate) db: &'a dyn HirDatabase,
pub(crate) krate: CrateId,
+ pub(crate) block: Option<BlockId>,
}
fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
@@ -43,6 +46,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TraitEnvironment {
pub krate: CrateId,
+ pub block: Option<BlockId>,
// FIXME make this a BTreeMap
pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>,
pub env: chalk_ir::Environment<Interner>,
@@ -52,6 +56,7 @@ impl TraitEnvironment {
pub fn empty(krate: CrateId) -> Self {
TraitEnvironment {
krate,
+ block: None,
traits_from_clauses: Vec::new(),
env: chalk_ir::Environment::new(Interner),
}
@@ -78,11 +83,12 @@ pub(crate) fn normalize_projection_query(
pub(crate) fn trait_solve_query(
db: &dyn HirDatabase,
krate: CrateId,
+ block: Option<BlockId>,
goal: Canonical<InEnvironment<Goal>>,
) -> Option<Solution> {
let _p = profile::span("trait_solve_query").detail(|| match &goal.value.goal.data(Interner) {
GoalData::DomainGoal(DomainGoal::Holds(WhereClause::Implemented(it))) => {
- db.trait_data(it.hir_trait_id()).name.to_string()
+ db.trait_data(it.hir_trait_id()).name.display(db.upcast()).to_string()
}
GoalData::DomainGoal(DomainGoal::Holds(WhereClause::AliasEq(_))) => "alias_eq".to_string(),
_ => "??".to_string(),
@@ -100,18 +106,25 @@ pub(crate) fn trait_solve_query(
}
}
+ // Chalk see `UnevaluatedConst` as a unique concrete value, but we see it as an alias for another const. So
+ // we should get rid of it when talking to chalk.
+ let goal = goal
+ .try_fold_with(&mut UnevaluatedConstEvaluatorFolder { db }, DebruijnIndex::INNERMOST)
+ .unwrap();
+
// We currently don't deal with universes (I think / hope they're not yet
// relevant for our use cases?)
let u_canonical = chalk_ir::UCanonical { canonical: goal, universes: 1 };
- solve(db, krate, &u_canonical)
+ solve(db, krate, block, &u_canonical)
}
fn solve(
db: &dyn HirDatabase,
krate: CrateId,
+ block: Option<BlockId>,
goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<Interner>>>,
) -> Option<chalk_solve::Solution<Interner>> {
- let context = ChalkContext { db, krate };
+ let context = ChalkContext { db, krate, block };
tracing::debug!("solve goal: {:?}", goal);
let mut solver = create_chalk_solver();
@@ -171,8 +184,10 @@ fn is_chalk_print() -> bool {
std::env::var("CHALK_PRINT").is_ok()
}
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum FnTrait {
+ // Warning: Order is important. If something implements `x` it should also implement
+ // `y` if `y <= x`.
FnOnce,
FnMut,
Fn,
@@ -187,7 +202,23 @@ impl FnTrait {
}
}
- pub fn get_id(&self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
+ pub const fn to_chalk_ir(self) -> rust_ir::ClosureKind {
+ match self {
+ FnTrait::FnOnce => rust_ir::ClosureKind::FnOnce,
+ FnTrait::FnMut => rust_ir::ClosureKind::FnMut,
+ FnTrait::Fn => rust_ir::ClosureKind::Fn,
+ }
+ }
+
+ pub fn method_name(self) -> Name {
+ match self {
+ FnTrait::FnOnce => name!(call_once),
+ FnTrait::FnMut => name!(call_mut),
+ FnTrait::Fn => name!(call),
+ }
+ }
+
+ pub fn get_id(self, db: &dyn HirDatabase, krate: CrateId) -> Option<TraitId> {
let target = db.lang_item(krate, self.lang_item())?;
match target {
LangItemTarget::Trait(t) => Some(t),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
index 34d957e26..363658063 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
@@ -1,10 +1,14 @@
//! Helper functions for working with def, which don't need to be a separate
//! query, but can't be computed directly from `*Data` (ie, which need a `db`).
-use std::iter;
+use std::{hash::Hash, iter};
use base_db::CrateId;
-use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex};
+use chalk_ir::{
+ cast::Cast,
+ fold::{FallibleTypeFolder, Shift},
+ BoundVar, DebruijnIndex,
+};
use either::Either;
use hir_def::{
db::DefDatabase,
@@ -15,16 +19,23 @@ use hir_def::{
lang_item::LangItem,
resolver::{HasResolver, TypeNs},
type_ref::{TraitBoundModifier, TypeRef},
- ConstParamId, FunctionId, GenericDefId, ItemContainerId, Lookup, TraitId, TypeAliasId,
- TypeOrConstParamId, TypeParamId,
+ ConstParamId, EnumId, EnumVariantId, FunctionId, GenericDefId, ItemContainerId,
+ LocalEnumVariantId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId,
+ TypeParamId,
};
use hir_expand::name::Name;
use intern::Interned;
use rustc_hash::FxHashSet;
use smallvec::{smallvec, SmallVec};
+use stdx::never;
use crate::{
- db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, WhereClause,
+ consteval::unknown_const,
+ db::HirDatabase,
+ layout::{Layout, TagEncoding},
+ mir::pad16,
+ ChalkTraitId, Const, ConstScalar, GenericArg, Interner, Substitution, TraitRef, TraitRefExt,
+ Ty, WhereClause,
};
pub(crate) fn fn_traits(
@@ -69,9 +80,7 @@ pub(super) fn all_super_trait_refs<T>(
cb: impl FnMut(TraitRef) -> Option<T>,
) -> Option<T> {
let seen = iter::once(trait_ref.trait_id).collect();
- let mut stack = Vec::new();
- stack.push(trait_ref);
- SuperTraits { db, seen, stack }.find_map(cb)
+ SuperTraits { db, seen, stack: vec![trait_ref] }.find_map(cb)
}
struct SuperTraits<'a> {
@@ -130,7 +139,7 @@ fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(Tra
WherePredicate::Lifetime { .. } => None,
})
.filter(|(_, bound_modifier)| matches!(bound_modifier, TraitBoundModifier::None))
- .filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path.mod_path()) {
+ .filter_map(|(path, _)| match resolver.resolve_path_in_type_ns_fully(db, path) {
Some(TypeNs::TraitId(t)) => Some(t),
_ => None,
})
@@ -176,6 +185,37 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
Generics { def, params: db.generic_params(def), parent_generics }
}
+/// It is a bit different from the rustc equivalent. Currently it stores:
+/// - 0: the function signature, encoded as a function pointer type
+/// - 1..n: generics of the parent
+///
+/// and it doesn't store the closure types and fields.
+///
+/// Codes should not assume this ordering, and should always use methods available
+/// on this struct for retriving, and `TyBuilder::substs_for_closure` for creating.
+pub(crate) struct ClosureSubst<'a>(pub(crate) &'a Substitution);
+
+impl<'a> ClosureSubst<'a> {
+ pub(crate) fn parent_subst(&self) -> &'a [GenericArg] {
+ match self.0.as_slice(Interner) {
+ [_, x @ ..] => x,
+ _ => {
+ never!("Closure missing parameter");
+ &[]
+ }
+ }
+ }
+
+ pub(crate) fn sig_ty(&self) -> &'a Ty {
+ match self.0.as_slice(Interner) {
+ [x, ..] => x.assert_ty_ref(Interner),
+ _ => {
+ unreachable!("Closure missing sig_ty parameter");
+ }
+ }
+ }
+}
+
#[derive(Debug)]
pub(crate) struct Generics {
def: GenericDefId,
@@ -354,3 +394,99 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
_ => false,
}
}
+
+pub(crate) struct UnevaluatedConstEvaluatorFolder<'a> {
+ pub(crate) db: &'a dyn HirDatabase,
+}
+
+impl FallibleTypeFolder<Interner> for UnevaluatedConstEvaluatorFolder<'_> {
+ type Error = ();
+
+ fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = ()> {
+ self
+ }
+
+ fn interner(&self) -> Interner {
+ Interner
+ }
+
+ fn try_fold_const(
+ &mut self,
+ constant: Const,
+ _outer_binder: DebruijnIndex,
+ ) -> Result<Const, Self::Error> {
+ if let chalk_ir::ConstValue::Concrete(c) = &constant.data(Interner).value {
+ if let ConstScalar::UnevaluatedConst(id, subst) = &c.interned {
+ if let Ok(eval) = self.db.const_eval(*id, subst.clone()) {
+ return Ok(eval);
+ } else {
+ return Ok(unknown_const(constant.data(Interner).ty.clone()));
+ }
+ }
+ }
+ Ok(constant)
+ }
+}
+
+pub(crate) fn detect_variant_from_bytes<'a>(
+ layout: &'a Layout,
+ db: &dyn HirDatabase,
+ krate: CrateId,
+ b: &[u8],
+ e: EnumId,
+) -> Option<(LocalEnumVariantId, &'a Layout)> {
+ let (var_id, var_layout) = match &layout.variants {
+ hir_def::layout::Variants::Single { index } => (index.0, &*layout),
+ hir_def::layout::Variants::Multiple { tag, tag_encoding, variants, .. } => {
+ let target_data_layout = db.target_data_layout(krate)?;
+ let size = tag.size(&*target_data_layout).bytes_usize();
+ let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
+ let tag = i128::from_le_bytes(pad16(&b[offset..offset + size], false));
+ match tag_encoding {
+ TagEncoding::Direct => {
+ let x = variants.iter_enumerated().find(|x| {
+ db.const_eval_discriminant(EnumVariantId { parent: e, local_id: x.0 .0 })
+ == Ok(tag)
+ })?;
+ (x.0 .0, x.1)
+ }
+ TagEncoding::Niche { untagged_variant, niche_start, .. } => {
+ let candidate_tag = tag.wrapping_sub(*niche_start as i128) as usize;
+ let variant = variants
+ .iter_enumerated()
+ .map(|(x, _)| x)
+ .filter(|x| x != untagged_variant)
+ .nth(candidate_tag)
+ .unwrap_or(*untagged_variant);
+ (variant.0, &variants[variant])
+ }
+ }
+ }
+ };
+ Some((var_id, var_layout))
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub(crate) struct InTypeConstIdMetadata(pub(crate) Ty);
+
+impl OpaqueInternableThing for InTypeConstIdMetadata {
+ fn dyn_hash(&self, mut state: &mut dyn std::hash::Hasher) {
+ self.hash(&mut state);
+ }
+
+ fn dyn_eq(&self, other: &dyn OpaqueInternableThing) -> bool {
+ other.as_any().downcast_ref::<Self>().map_or(false, |x| self == x)
+ }
+
+ fn dyn_clone(&self) -> Box<dyn OpaqueInternableThing> {
+ Box::new(self.clone())
+ }
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
+
+ fn box_any(&self) -> Box<dyn std::any::Any> {
+ Box::new(self.clone())
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml
index ef40a8902..a20aff93f 100644
--- a/src/tools/rust-analyzer/crates/hir/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml
@@ -17,6 +17,7 @@ either = "1.7.0"
arrayvec = "0.7.2"
itertools = "0.10.5"
smallvec.workspace = true
+triomphe.workspace = true
once_cell = "1.17.0"
# local deps
diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
index db0b84ef0..b81793729 100644
--- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
@@ -4,7 +4,6 @@ use hir_def::{
attr::{AttrsWithOwner, Documentation},
item_scope::ItemInNs,
path::ModPath,
- per_ns::PerNs,
resolver::HasResolver,
AttrDefId, GenericParamId, ModuleDefId,
};
@@ -41,7 +40,7 @@ macro_rules! impl_has_attrs {
impl HasAttrs for $def {
fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
let def = AttrDefId::$def_id(self.into());
- db.attrs(def)
+ db.attrs_with_owner(def)
}
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
let def = AttrDefId::$def_id(self.into());
@@ -121,6 +120,7 @@ impl HasAttrs for AssocItem {
}
}
+/// Resolves the item `link` points to in the scope of `def`.
fn resolve_doc_path(
db: &dyn HirDatabase,
def: AttrDefId,
@@ -155,14 +155,14 @@ fn resolve_doc_path(
.syntax_node()
.descendants()
.find_map(ast::Path::cast)?;
- if ast_path.to_string() != link {
+ if ast_path.syntax().text() != link {
return None;
}
ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())?
};
let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
- let resolved = if resolved == PerNs::none() {
+ let resolved = if resolved.is_none() {
resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)?
} else {
resolved
diff --git a/src/tools/rust-analyzer/crates/hir/src/db.rs b/src/tools/rust-analyzer/crates/hir/src/db.rs
index 0935b5ea5..e0cde689f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/db.rs
@@ -6,8 +6,8 @@
pub use hir_def::db::*;
pub use hir_expand::db::{
AstIdMapQuery, ExpandDatabase, ExpandDatabaseStorage, ExpandProcMacroQuery, HygieneFrameQuery,
- InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandErrorQuery,
- MacroExpandQuery, ParseMacroExpansionQuery,
+ InternMacroCallQuery, MacroArgTextQuery, MacroDefQuery, MacroExpandQuery,
+ ParseMacroExpansionErrorQuery, ParseMacroExpansionQuery,
};
pub use hir_ty::db::*;
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index 253d62daf..b64d81490 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -10,7 +10,7 @@ use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_def::path::ModPath;
use hir_expand::{name::Name, HirFileId, InFile};
-use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange};
+use syntax::{ast, AstPtr, SyntaxError, SyntaxNodePtr, TextRange};
use crate::{AssocItem, Field, Local, MacroKind, Type};
@@ -38,19 +38,25 @@ diagnostics![
IncorrectCase,
InvalidDeriveTarget,
IncoherentImpl,
+ MacroDefError,
MacroError,
+ MacroExpansionParseError,
MalformedDerive,
MismatchedArgCount,
MissingFields,
MissingMatchArms,
MissingUnsafe,
+ MovedOutOfRef,
NeedMut,
NoSuchField,
PrivateAssocItem,
PrivateField,
ReplaceFilterMapNextWithFindMap,
+ TypedHole,
TypeMismatch,
+ UndeclaredLabel,
UnimplementedBuiltinMacro,
+ UnreachableLabel,
UnresolvedExternCrate,
UnresolvedField,
UnresolvedImport,
@@ -62,6 +68,19 @@ diagnostics![
];
#[derive(Debug)]
+pub struct BreakOutsideOfLoop {
+ pub expr: InFile<AstPtr<ast::Expr>>,
+ pub is_break: bool,
+ pub bad_value_break: bool,
+}
+
+#[derive(Debug)]
+pub struct TypedHole {
+ pub expr: InFile<AstPtr<ast::Expr>>,
+ pub expected: Type,
+}
+
+#[derive(Debug)]
pub struct UnresolvedModule {
pub decl: InFile<AstPtr<ast::Module>>,
pub candidates: Box<[String]>,
@@ -84,6 +103,17 @@ pub struct UnresolvedMacroCall {
pub path: ModPath,
pub is_bang: bool,
}
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct UnreachableLabel {
+ pub node: InFile<AstPtr<ast::Lifetime>>,
+ pub name: Name,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct UndeclaredLabel {
+ pub node: InFile<AstPtr<ast::Lifetime>>,
+ pub name: Name,
+}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct InactiveCode {
@@ -111,6 +141,20 @@ pub struct MacroError {
pub message: String,
}
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct MacroExpansionParseError {
+ pub node: InFile<SyntaxNodePtr>,
+ pub precise_location: Option<TextRange>,
+ pub errors: Box<[SyntaxError]>,
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct MacroDefError {
+ pub node: InFile<AstPtr<ast::Macro>>,
+ pub message: String,
+ pub name: Option<TextRange>,
+}
+
#[derive(Debug)]
pub struct UnimplementedBuiltinMacro {
pub node: InFile<SyntaxNodePtr>,
@@ -167,13 +211,6 @@ pub struct PrivateField {
}
#[derive(Debug)]
-pub struct BreakOutsideOfLoop {
- pub expr: InFile<AstPtr<ast::Expr>>,
- pub is_break: bool,
- pub bad_value_break: bool,
-}
-
-#[derive(Debug)]
pub struct MissingUnsafe {
pub expr: InFile<AstPtr<ast::Expr>>,
}
@@ -223,3 +260,9 @@ pub struct NeedMut {
pub struct UnusedMut {
pub local: Local,
}
+
+#[derive(Debug)]
+pub struct MovedOutOfRef {
+ pub ty: Type,
+ pub span: InFile<SyntaxNodePtr>,
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs
index 5aae92efd..9a2090ab7 100644
--- a/src/tools/rust-analyzer/crates/hir/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/display.rs
@@ -1,6 +1,6 @@
//! HirDisplay implementations for various hir types.
use hir_def::{
- adt::VariantData,
+ data::adt::VariantData,
generics::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
},
@@ -8,6 +8,7 @@ use hir_def::{
type_ref::{TypeBound, TypeRef},
AdtId, GenericDefId,
};
+use hir_expand::name;
use hir_ty::{
display::{
write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
@@ -50,7 +51,7 @@ impl HirDisplay for Function {
// FIXME: String escape?
write!(f, "extern \"{}\" ", &**abi)?;
}
- write!(f, "fn {}", data.name)?;
+ write!(f, "fn {}", data.name.display(f.db.upcast()))?;
write_generic_params(GenericDefId::FunctionId(self.id), f)?;
@@ -62,7 +63,7 @@ impl HirDisplay for Function {
{
f.write_char('&')?;
if let Some(lifetime) = lifetime {
- write!(f, "{} ", lifetime.name)?;
+ write!(f, "{} ", lifetime.name.display(f.db.upcast()))?;
}
if let hir_def::type_ref::Mutability::Mut = mut_ {
f.write_str("mut ")?;
@@ -76,22 +77,22 @@ impl HirDisplay for Function {
};
let mut first = true;
- for (name, type_ref) in &data.params {
+ // FIXME: Use resolved `param.ty` once we no longer discard lifetimes
+ for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)) {
+ let local = param.as_local(db).map(|it| it.name(db));
if !first {
f.write_str(", ")?;
} else {
first = false;
- if data.has_self_param() {
+ if local == Some(name!(self)) {
write_self_param(type_ref, f)?;
continue;
}
}
- match name {
- Some(name) => write!(f, "{name}: ")?,
+ match local {
+ Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?,
None => f.write_str("_: ")?,
}
- // FIXME: Use resolved `param.ty` or raw `type_ref`?
- // The former will ignore lifetime arguments currently.
type_ref.hir_fmt(f)?;
}
@@ -150,7 +151,7 @@ impl HirDisplay for Struct {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
f.write_str("struct ")?;
- write!(f, "{}", self.name(f.db))?;
+ write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
let def_id = GenericDefId::AdtId(AdtId::StructId(self.id));
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
@@ -162,7 +163,7 @@ impl HirDisplay for Enum {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
f.write_str("enum ")?;
- write!(f, "{}", self.name(f.db))?;
+ write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id));
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
@@ -174,7 +175,7 @@ impl HirDisplay for Union {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
f.write_str("union ")?;
- write!(f, "{}", self.name(f.db))?;
+ write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id));
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
@@ -185,14 +186,14 @@ impl HirDisplay for Union {
impl HirDisplay for Field {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.parent.module(f.db).id, self.visibility(f.db), f)?;
- write!(f, "{}: ", self.name(f.db))?;
+ write!(f, "{}: ", self.name(f.db).display(f.db.upcast()))?;
self.ty(f.db).hir_fmt(f)
}
}
impl HirDisplay for Variant {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
- write!(f, "{}", self.name(f.db))?;
+ write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
let data = self.variant_data(f.db);
match &*data {
VariantData::Unit => {}
@@ -221,7 +222,7 @@ impl HirDisplay for Variant {
f.write_str(", ")?;
}
// Enum variant fields must be pub.
- write!(f, "{}: ", field.name)?;
+ write!(f, "{}: ", field.name.display(f.db.upcast()))?;
field.type_ref.hir_fmt(f)?;
}
f.write_str(" }")?;
@@ -258,7 +259,7 @@ impl HirDisplay for TypeOrConstParam {
impl HirDisplay for TypeParam {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
- write!(f, "{}", self.name(f.db))?;
+ write!(f, "{}", self.name(f.db).display(f.db.upcast()))?;
if f.omit_verbose_types() {
return Ok(());
}
@@ -285,13 +286,13 @@ impl HirDisplay for TypeParam {
impl HirDisplay for LifetimeParam {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
- write!(f, "{}", self.name(f.db))
+ write!(f, "{}", self.name(f.db).display(f.db.upcast()))
}
}
impl HirDisplay for ConstParam {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
- write!(f, "const {}: ", self.name(f.db))?;
+ write!(f, "const {}: ", self.name(f.db).display(f.db.upcast()))?;
self.ty(f.db).hir_fmt(f)
}
}
@@ -324,7 +325,7 @@ fn write_generic_params(
};
for (_, lifetime) in params.lifetimes.iter() {
delim(f)?;
- write!(f, "{}", lifetime.name)?;
+ write!(f, "{}", lifetime.name.display(f.db.upcast()))?;
}
for (_, ty) in params.type_or_consts.iter() {
if let Some(name) = &ty.name() {
@@ -334,7 +335,7 @@ fn write_generic_params(
continue;
}
delim(f)?;
- write!(f, "{name}")?;
+ write!(f, "{}", name.display(f.db.upcast()))?;
if let Some(default) = &ty.default {
f.write_str(" = ")?;
default.hir_fmt(f)?;
@@ -342,7 +343,7 @@ fn write_generic_params(
}
TypeOrConstParamData::ConstParamData(c) => {
delim(f)?;
- write!(f, "const {name}: ")?;
+ write!(f, "const {}: ", name.display(f.db.upcast()))?;
c.ty.hir_fmt(f)?;
}
}
@@ -379,7 +380,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
WherePredicateTypeTarget::TypeRef(ty) => ty.hir_fmt(f),
WherePredicateTypeTarget::TypeOrConstParam(id) => {
match &params.type_or_consts[*id].name() {
- Some(name) => write!(f, "{name}"),
+ Some(name) => write!(f, "{}", name.display(f.db.upcast())),
None => f.write_str("{unnamed}"),
}
}
@@ -411,10 +412,15 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
WherePredicate::Lifetime { target, bound } => {
if matches!(prev_pred, Some(WherePredicate::Lifetime { target: target_, .. }) if target_ == target)
{
- write!(f, " + {}", bound.name)?;
+ write!(f, " + {}", bound.name.display(f.db.upcast()))?;
} else {
new_predicate(f)?;
- write!(f, "{}: {}", target.name, bound.name)?;
+ write!(
+ f,
+ "{}: {}",
+ target.name.display(f.db.upcast()),
+ bound.name.display(f.db.upcast())
+ )?;
}
}
WherePredicate::ForLifetime { lifetimes, target, bound } => {
@@ -431,7 +437,7 @@ fn write_where_clause(def: GenericDefId, f: &mut HirFormatter<'_>) -> Result<(),
if idx != 0 {
f.write_str(", ")?;
}
- write!(f, "{lifetime}")?;
+ write!(f, "{}", lifetime.display(f.db.upcast()))?;
}
f.write_str("> ")?;
write_target(target, f)?;
@@ -461,7 +467,7 @@ impl HirDisplay for Const {
let data = db.const_data(self.id);
f.write_str("const ")?;
match &data.name {
- Some(name) => write!(f, "{name}: ")?,
+ Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?,
None => f.write_str("_: ")?,
}
data.type_ref.hir_fmt(f)?;
@@ -477,7 +483,7 @@ impl HirDisplay for Static {
if data.mutable {
f.write_str("mut ")?;
}
- write!(f, "{}: ", &data.name)?;
+ write!(f, "{}: ", data.name.display(f.db.upcast()))?;
data.type_ref.hir_fmt(f)?;
Ok(())
}
@@ -493,7 +499,7 @@ impl HirDisplay for Trait {
if data.is_auto {
f.write_str("auto ")?;
}
- write!(f, "trait {}", data.name)?;
+ write!(f, "trait {}", data.name.display(f.db.upcast()))?;
let def_id = GenericDefId::TraitId(self.id);
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
@@ -505,7 +511,7 @@ impl HirDisplay for TraitAlias {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
let data = f.db.trait_alias_data(self.id);
- write!(f, "trait {}", data.name)?;
+ write!(f, "trait {}", data.name.display(f.db.upcast()))?;
let def_id = GenericDefId::TraitAliasId(self.id);
write_generic_params(def_id, f)?;
f.write_str(" = ")?;
@@ -521,7 +527,7 @@ impl HirDisplay for TypeAlias {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
let data = f.db.type_alias_data(self.id);
- write!(f, "type {}", data.name)?;
+ write!(f, "type {}", data.name.display(f.db.upcast()))?;
let def_id = GenericDefId::TypeAliasId(self.id);
write_generic_params(def_id, f)?;
write_where_clause(def_id, f)?;
@@ -541,8 +547,8 @@ impl HirDisplay for Module {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
// FIXME: Module doesn't have visibility saved in data.
match self.name(f.db) {
- Some(name) => write!(f, "mod {name}"),
- None if self.is_crate_root(f.db) => match self.krate(f.db).display_name(f.db) {
+ Some(name) => write!(f, "mod {}", name.display(f.db.upcast())),
+ None if self.is_crate_root() => match self.krate(f.db).display_name(f.db) {
Some(name) => write!(f, "extern crate {name}"),
None => f.write_str("extern crate {unknown}"),
},
@@ -558,6 +564,6 @@ impl HirDisplay for Macro {
hir_def::MacroId::MacroRulesId(_) => f.write_str("macro_rules!"),
hir_def::MacroId::ProcMacroId(_) => f.write_str("proc_macro"),
}?;
- write!(f, " {}", self.name(f.db))
+ write!(f, " {}", self.name(f.db).display(f.db.upcast()))
}
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
index aaaa7abf3..de2390219 100644
--- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
@@ -4,7 +4,7 @@
//! are splitting the hir.
use hir_def::{
- expr::{BindingId, LabelId},
+ hir::{BindingId, LabelId},
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
ModuleDefId, VariantId,
};
@@ -40,6 +40,7 @@ from_id![
(hir_def::TraitAliasId, crate::TraitAlias),
(hir_def::StaticId, crate::Static),
(hir_def::ConstId, crate::Const),
+ (hir_def::InTypeConstId, crate::InTypeConst),
(hir_def::FunctionId, crate::Function),
(hir_def::ImplId, crate::Impl),
(hir_def::TypeOrConstParamId, crate::TypeOrConstParam),
@@ -144,6 +145,7 @@ impl From<DefWithBody> for DefWithBodyId {
DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id),
DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id),
DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()),
+ DefWithBody::InTypeConst(it) => DefWithBodyId::InTypeConstId(it.id),
}
}
}
@@ -155,6 +157,7 @@ impl From<DefWithBodyId> for DefWithBody {
DefWithBodyId::StaticId(it) => DefWithBody::Static(it.into()),
DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()),
DefWithBodyId::VariantId(it) => DefWithBody::Variant(it.into()),
+ DefWithBodyId::InTypeConstId(it) => DefWithBody::InTypeConst(it.into()),
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 35424feec..6df625380 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -6,7 +6,7 @@
//! applied. So, the relation between syntax and HIR is many-to-one.
//!
//! HIR is the public API of the all of the compiler logic above syntax trees.
-//! It is written in "OO" style. Each type is self contained (as in, it knows it's
+//! It is written in "OO" style. Each type is self contained (as in, it knows its
//! parents and full context). It should be "clean code".
//!
//! `hir_*` crates are the implementation of the compiler logic.
@@ -33,27 +33,29 @@ pub mod symbols;
mod display;
-use std::{iter, ops::ControlFlow, sync::Arc};
+use std::{iter, ops::ControlFlow};
use arrayvec::ArrayVec;
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
use either::Either;
use hir_def::{
- adt::VariantData,
body::{BodyDiagnostic, SyntheticSyntax},
- expr::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
+ data::adt::VariantData,
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
+ hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
item_tree::ItemTreeNode,
- lang_item::{LangItem, LangItemTarget},
- layout::{Layout, LayoutError, ReprOptions},
+ lang_item::LangItemTarget,
+ layout::{self, ReprOptions, TargetDataLayout},
+ macro_id_to_def_id,
nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin},
per_ns::PerNs,
resolver::{HasResolver, Resolver},
src::HasSource as _,
- AdtId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId,
- EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
- LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
- TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
+ AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId,
+ EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, InTypeConstId, ItemContainerId,
+ LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId,
+ StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId,
+ UnionId,
};
use hir_expand::{name::name, MacroCallKind};
use hir_ty::{
@@ -61,14 +63,15 @@ use hir_ty::{
consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
diagnostics::BodyValidationDiagnostic,
display::HexifiedConst,
- layout::layout_of_ty,
+ layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding},
method_resolution::{self, TyFingerprint},
mir::{self, interpret_mir},
primitive::UintTy,
traits::FnTrait,
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
- TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause,
+ TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId,
+ WhereClause,
};
use itertools::Itertools;
use nameres::diagnostics::DefDiagnosticKind;
@@ -79,6 +82,7 @@ use syntax::{
ast::{self, HasAttrs as _, HasDocComments, HasName},
AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, T,
};
+use triomphe::Arc;
use crate::db::{DefDatabase, HirDatabase};
@@ -86,11 +90,13 @@ pub use crate::{
attrs::{HasAttrs, Namespace},
diagnostics::{
AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl,
- IncorrectCase, InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount,
- MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem,
- PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro,
- UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall,
- UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut,
+ IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError,
+ MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe,
+ MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
+ ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
+ UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
+ UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
+ UnresolvedProcMacro, UnusedMut,
},
has_source::HasSource,
semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits},
@@ -108,20 +114,18 @@ pub use crate::{
pub use {
cfg::{CfgAtom, CfgExpr, CfgOptions},
hir_def::{
- adt::StructKind,
- attr::{Attrs, AttrsWithOwner, Documentation},
- builtin_attr::AttributeTemplate,
+ attr::{builtin::AttributeTemplate, Attrs, AttrsWithOwner, Documentation},
+ data::adt::StructKind,
find_path::PrefixKind,
import_map,
- nameres::ModuleSource,
+ lang_item::LangItem,
+ nameres::{DefMap, ModuleSource},
path::{ModPath, PathKind},
type_ref::{Mutability, TypeRef},
visibility::Visibility,
- // FIXME: This is here since it is input of a method in `HirWrite`
- // and things outside of hir need to implement that trait. We probably
- // should move whole `hir_ty::display` to this crate so we will become
- // able to use `ModuleDef` or `Definition` instead of `ModuleDefId`.
- ModuleDefId,
+ // FIXME: This is here since some queries take it as input that are used
+ // outside of hir.
+ {AdtId, ModuleDefId},
},
hir_expand::{
attrs::Attr,
@@ -129,7 +133,8 @@ pub use {
ExpandResult, HirFileId, InFile, MacroFile, Origin,
},
hir_ty::{
- display::{HirDisplay, HirDisplayError, HirWrite},
+ display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
+ layout::LayoutError,
mir::MirEvalError,
PointerCast, Safety,
},
@@ -198,7 +203,7 @@ impl Crate {
pub fn root_module(self, db: &dyn HirDatabase) -> Module {
let def_map = db.crate_def_map(self.id);
- Module { id: def_map.module_id(def_map.root()) }
+ Module { id: def_map.crate_root().into() }
}
pub fn modules(self, db: &dyn HirDatabase) -> Vec<Module> {
@@ -253,7 +258,8 @@ impl Crate {
}
pub fn potential_cfg(&self, db: &dyn HirDatabase) -> CfgOptions {
- db.crate_graph()[self.id].potential_cfg_options.clone()
+ let data = &db.crate_graph()[self.id];
+ data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone())
}
}
@@ -326,7 +332,7 @@ impl ModuleDef {
segments.extend(m.name(db))
}
segments.reverse();
- Some(segments.into_iter().join("::"))
+ Some(segments.iter().map(|it| it.display(db.upcast())).join("::"))
}
pub fn canonical_module_path(
@@ -470,12 +476,11 @@ impl Module {
/// in the module tree of any target in `Cargo.toml`.
pub fn crate_root(self, db: &dyn HirDatabase) -> Module {
let def_map = db.crate_def_map(self.id.krate());
- Module { id: def_map.module_id(def_map.root()) }
+ Module { id: def_map.crate_root().into() }
}
- pub fn is_crate_root(self, db: &dyn HirDatabase) -> bool {
- let def_map = db.crate_def_map(self.id.krate());
- def_map.root() == self.id.local_id
+ pub fn is_crate_root(self) -> bool {
+ DefMap::ROOT == self.id.local_id
}
/// Iterates over all child modules.
@@ -552,7 +557,11 @@ impl Module {
/// Fills `acc` with the module's diagnostics.
pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
let _p = profile::span("Module::diagnostics").detail(|| {
- format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string()))
+ format!(
+ "{:?}",
+ self.name(db)
+ .map_or("<unknown>".into(), |name| name.display(db.upcast()).to_string())
+ )
});
let def_map = self.id.def_map(db.upcast());
for diag in def_map.diagnostics() {
@@ -562,6 +571,7 @@ impl Module {
}
emit_def_diagnostic(db, acc, diag);
}
+
for decl in self.declarations(db) {
match decl {
ModuleDef::Module(m) => {
@@ -600,9 +610,11 @@ impl Module {
}
acc.extend(decl.diagnostics(db))
}
+ ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m),
_ => acc.extend(decl.diagnostics(db)),
}
}
+ self.legacy_macros(db).into_iter().for_each(|m| emit_macro_def_diagnostics(db, acc, m));
let inherent_impls = db.inherent_impls_in_crate(self.id.krate());
@@ -684,8 +696,31 @@ impl Module {
}
}
+fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, m: Macro) {
+ let id = macro_id_to_def_id(db.upcast(), m.id);
+ if let Err(e) = db.macro_def(id) {
+ let Some(ast) = id.ast_id().left() else {
+ never!("MacroDefError for proc-macro: {:?}", e);
+ return;
+ };
+ emit_def_diagnostic_(
+ db,
+ acc,
+ &DefDiagnosticKind::MacroDefError { ast, message: e.to_string() },
+ );
+ }
+}
+
fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: &DefDiagnostic) {
- match &diag.kind {
+ emit_def_diagnostic_(db, acc, &diag.kind)
+}
+
+fn emit_def_diagnostic_(
+ db: &dyn HirDatabase,
+ acc: &mut Vec<AnyDiagnostic>,
+ diag: &DefDiagnosticKind,
+) {
+ match diag {
DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => {
let decl = declaration.to_node(db.upcast());
acc.push(
@@ -725,7 +760,6 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
.into(),
);
}
-
DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => {
let (node, precise_location, macro_name, kind) = precise_macro_call_location(ast, db);
acc.push(
@@ -733,7 +767,6 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
.into(),
);
}
-
DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
acc.push(
@@ -746,12 +779,16 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
.into(),
);
}
-
DefDiagnosticKind::MacroError { ast, message } => {
let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
acc.push(MacroError { node, precise_location, message: message.clone() }.into());
}
-
+ DefDiagnosticKind::MacroExpansionParseError { ast, errors } => {
+ let (node, precise_location, _, _) = precise_macro_call_location(ast, db);
+ acc.push(
+ MacroExpansionParseError { node, precise_location, errors: errors.clone() }.into(),
+ );
+ }
DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => {
let node = ast.to_node(db.upcast());
// Must have a name, otherwise we wouldn't emit it.
@@ -793,6 +830,17 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag:
None => stdx::never!("derive diagnostic on item without derive attribute"),
}
}
+ DefDiagnosticKind::MacroDefError { ast, message } => {
+ let node = ast.to_node(db.upcast());
+ acc.push(
+ MacroDefError {
+ node: InFile::new(ast.file_id, AstPtr::new(&node)),
+ name: node.name().map(|it| it.syntax().text_range()),
+ message: message.clone(),
+ }
+ .into(),
+ );
+ }
}
}
@@ -800,7 +848,7 @@ fn precise_macro_call_location(
ast: &MacroCallKind,
db: &dyn HirDatabase,
) -> (InFile<SyntaxNodePtr>, Option<TextRange>, Option<String>, MacroKind) {
- // FIXME: maaybe we actually want slightly different ranges for the different macro diagnostics
+ // FIXME: maybe we actually want slightly different ranges for the different macro diagnostics
// - e.g. the full attribute for macro errors, but only the name for name resolution
match ast {
MacroCallKind::FnLike { ast_id, .. } => {
@@ -915,7 +963,8 @@ impl Field {
}
pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
- layout_of_ty(db, &self.ty(db).ty, self.parent.module(db).krate().into())
+ db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into())
+ .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).into()).unwrap()))
}
pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef {
@@ -959,6 +1008,10 @@ impl Struct {
Type::from_def(db, self.id)
}
+ pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
+ Type::from_value_def(db, self.id)
+ }
+
pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> {
db.struct_data(self.id).repr
}
@@ -996,6 +1049,10 @@ impl Union {
Type::from_def(db, self.id)
}
+ pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
+ Type::from_value_def(db, self.id)
+ }
+
pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
db.union_data(self.id)
.variant_data
@@ -1034,6 +1091,10 @@ impl Enum {
db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect()
}
+ pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> {
+ db.enum_data(self.id).repr
+ }
+
pub fn ty(self, db: &dyn HirDatabase) -> Type {
Type::from_def(db, self.id)
}
@@ -1043,7 +1104,7 @@ impl Enum {
Type::new_for_crate(
self.id.lookup(db.upcast()).container.krate(),
TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() {
- hir_def::layout::IntegerType::Pointer(sign) => match sign {
+ layout::IntegerType::Pointer(sign) => match sign {
true => hir_def::builtin_type::BuiltinType::Int(
hir_def::builtin_type::BuiltinInt::Isize,
),
@@ -1051,29 +1112,34 @@ impl Enum {
hir_def::builtin_type::BuiltinUint::Usize,
),
},
- hir_def::layout::IntegerType::Fixed(i, sign) => match sign {
+ layout::IntegerType::Fixed(i, sign) => match sign {
true => hir_def::builtin_type::BuiltinType::Int(match i {
- hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8,
- hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16,
- hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32,
- hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64,
- hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128,
+ layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8,
+ layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16,
+ layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32,
+ layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64,
+ layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128,
}),
false => hir_def::builtin_type::BuiltinType::Uint(match i {
- hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8,
- hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16,
- hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32,
- hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64,
- hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128,
+ layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8,
+ layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16,
+ layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32,
+ layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64,
+ layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128,
}),
},
}),
)
}
+ /// Returns true if at least one variant of this enum is a non-unit variant.
pub fn is_data_carrying(self, db: &dyn HirDatabase) -> bool {
self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit))
}
+
+ pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+ Adt::from(self).layout(db)
+ }
}
impl HasVisibility for Enum {
@@ -1103,6 +1169,10 @@ impl Variant {
self.parent
}
+ pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type {
+ Type::from_value_def(db, EnumVariantId { parent: self.parent.id, local_id: self.id })
+ }
+
pub fn name(self, db: &dyn HirDatabase) -> Name {
db.enum_data(self.parent.id).variants[self.id].name.clone()
}
@@ -1130,6 +1200,18 @@ impl Variant {
pub fn eval(self, db: &dyn HirDatabase) -> Result<i128, ConstEvalError> {
db.const_eval_discriminant(self.into())
}
+
+ pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+ let parent_enum = self.parent_enum(db);
+ let parent_layout = parent_enum.layout(db)?;
+ Ok(match &parent_layout.0.variants {
+ layout::Variants::Multiple { variants, .. } => Layout(
+ Arc::new(variants[RustcEnumVariantIdx(self.id)].clone()),
+ db.target_data_layout(parent_enum.krate(db).into()).unwrap(),
+ ),
+ _ => parent_layout,
+ })
+ }
}
/// Variants inherit visibility from the parent enum.
@@ -1161,7 +1243,9 @@ impl Adt {
if db.generic_params(self.into()).iter().count() != 0 {
return Err(LayoutError::HasPlaceholder);
}
- db.layout_of_adt(self.into(), Substitution::empty(Interner))
+ let krate = self.krate(db).id;
+ db.layout_of_adt(self.into(), Substitution::empty(Interner), krate)
+ .map(|layout| Layout(layout, db.target_data_layout(krate).unwrap()))
}
/// Turns this ADT into a type. Any type parameters of the ADT will be
@@ -1292,8 +1376,9 @@ pub enum DefWithBody {
Static(Static),
Const(Const),
Variant(Variant),
+ InTypeConst(InTypeConst),
}
-impl_from!(Function, Const, Static, Variant for DefWithBody);
+impl_from!(Function, Const, Static, Variant, InTypeConst for DefWithBody);
impl DefWithBody {
pub fn module(self, db: &dyn HirDatabase) -> Module {
@@ -1302,6 +1387,7 @@ impl DefWithBody {
DefWithBody::Function(f) => f.module(db),
DefWithBody::Static(s) => s.module(db),
DefWithBody::Variant(v) => v.module(db),
+ DefWithBody::InTypeConst(c) => c.module(db),
}
}
@@ -1311,6 +1397,7 @@ impl DefWithBody {
DefWithBody::Static(s) => Some(s.name(db)),
DefWithBody::Const(c) => c.name(db),
DefWithBody::Variant(v) => Some(v.name(db)),
+ DefWithBody::InTypeConst(_) => None,
}
}
@@ -1321,6 +1408,11 @@ impl DefWithBody {
DefWithBody::Static(it) => it.ty(db),
DefWithBody::Const(it) => it.ty(db),
DefWithBody::Variant(it) => it.parent.variant_body_ty(db),
+ DefWithBody::InTypeConst(it) => Type::new_with_resolver_inner(
+ db,
+ &DefWithBodyId::from(it.id).resolver(db.upcast()),
+ TyKind::Error.intern(Interner),
+ ),
}
}
@@ -1330,6 +1422,7 @@ impl DefWithBody {
DefWithBody::Static(it) => it.id.into(),
DefWithBody::Const(it) => it.id.into(),
DefWithBody::Variant(it) => it.into(),
+ DefWithBody::InTypeConst(it) => it.id.into(),
}
}
@@ -1392,6 +1485,12 @@ impl DefWithBody {
}
.into(),
),
+ BodyDiagnostic::UnreachableLabel { node, name } => {
+ acc.push(UnreachableLabel { node: node.clone(), name: name.clone() }.into())
+ }
+ BodyDiagnostic::UndeclaredLabel { node, name } => {
+ acc.push(UndeclaredLabel { node: node.clone(), name: name.clone() }.into())
+ }
}
}
@@ -1404,14 +1503,6 @@ impl DefWithBody {
let field = source_map.field_syntax(expr);
acc.push(NoSuchField { field }.into())
}
- &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
- expr,
- is_break,
- bad_value_break,
- } => {
- let expr = expr_syntax(expr);
- acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
- }
&hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
acc.push(
MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found }
@@ -1483,14 +1574,29 @@ impl DefWithBody {
.into(),
)
}
+ &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop {
+ expr,
+ is_break,
+ bad_value_break,
+ } => {
+ let expr = expr_syntax(expr);
+ acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into())
+ }
+ hir_ty::InferenceDiagnostic::TypedHole { expr, expected } => {
+ let expr = expr_syntax(*expr);
+ acc.push(
+ TypedHole {
+ expr,
+ expected: Type::new(db, DefWithBodyId::from(self), expected.clone()),
+ }
+ .into(),
+ )
+ }
}
}
for (pat_or_expr, mismatch) in infer.type_mismatches() {
let expr_or_pat = match pat_or_expr {
ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left),
- // FIXME: Re-enable these once we have less false positives
- ExprOrPatId::PatId(_pat) => continue,
- #[allow(unreachable_patterns)]
ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right),
};
let expr_or_pat = match expr_or_pat {
@@ -1515,7 +1621,7 @@ impl DefWithBody {
match source_map.expr_syntax(expr) {
Ok(expr) => acc.push(MissingUnsafe { expr }.into()),
Err(SyntheticSyntax) => {
- // FIXME: Here and eslwhere in this file, the `expr` was
+ // FIXME: Here and elsewhere in this file, the `expr` was
// desugared, report or assert that this doesn't happen.
}
}
@@ -1523,35 +1629,71 @@ impl DefWithBody {
let hir_body = db.body(self.into());
- if let Ok(borrowck_result) = db.borrowck(self.into()) {
- let mir_body = &borrowck_result.mir_body;
- let mol = &borrowck_result.mutability_of_locals;
- for (binding_id, _) in hir_body.bindings.iter() {
- let need_mut = &mol[mir_body.binding_locals[binding_id]];
- let local = Local { parent: self.into(), binding_id };
- match (need_mut, local.is_mut(db)) {
- (mir::MutabilityReason::Mut { .. }, true)
- | (mir::MutabilityReason::Not, false) => (),
- (mir::MutabilityReason::Mut { spans }, false) => {
- for span in spans {
- let span: InFile<SyntaxNodePtr> = match span {
- mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
- Ok(s) => s.map(|x| x.into()),
- Err(_) => continue,
- },
- mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
- Ok(s) => s.map(|x| match x {
- Either::Left(e) => e.into(),
- Either::Right(e) => e.into(),
- }),
- Err(_) => continue,
- },
- mir::MirSpan::Unknown => continue,
- };
- acc.push(NeedMut { local, span }.into());
+ if let Ok(borrowck_results) = db.borrowck(self.into()) {
+ for borrowck_result in borrowck_results.iter() {
+ let mir_body = &borrowck_result.mir_body;
+ for moof in &borrowck_result.moved_out_of_ref {
+ let span: InFile<SyntaxNodePtr> = match moof.span {
+ mir::MirSpan::ExprId(e) => match source_map.expr_syntax(e) {
+ Ok(s) => s.map(|x| x.into()),
+ Err(_) => continue,
+ },
+ mir::MirSpan::PatId(p) => match source_map.pat_syntax(p) {
+ Ok(s) => s.map(|x| match x {
+ Either::Left(e) => e.into(),
+ Either::Right(e) => e.into(),
+ }),
+ Err(_) => continue,
+ },
+ mir::MirSpan::Unknown => continue,
+ };
+ acc.push(
+ MovedOutOfRef { ty: Type::new_for_crate(krate, moof.ty.clone()), span }
+ .into(),
+ )
+ }
+ let mol = &borrowck_result.mutability_of_locals;
+ for (binding_id, binding_data) in hir_body.bindings.iter() {
+ if binding_data.problems.is_some() {
+ // We should report specific diagnostics for these problems, not `need-mut` and `unused-mut`.
+ continue;
+ }
+ let Some(&local) = mir_body.binding_locals.get(binding_id) else {
+ continue;
+ };
+ let need_mut = &mol[local];
+ let local = Local { parent: self.into(), binding_id };
+ match (need_mut, local.is_mut(db)) {
+ (mir::MutabilityReason::Mut { .. }, true)
+ | (mir::MutabilityReason::Not, false) => (),
+ (mir::MutabilityReason::Mut { spans }, false) => {
+ for span in spans {
+ let span: InFile<SyntaxNodePtr> = match span {
+ mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
+ Ok(s) => s.map(|x| x.into()),
+ Err(_) => continue,
+ },
+ mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
+ Ok(s) => s.map(|x| match x {
+ Either::Left(e) => e.into(),
+ Either::Right(e) => e.into(),
+ }),
+ Err(_) => continue,
+ },
+ mir::MirSpan::Unknown => continue,
+ };
+ acc.push(NeedMut { local, span }.into());
+ }
+ }
+ (mir::MutabilityReason::Not, true) => {
+ if !infer.mutated_bindings_in_closure.contains(&binding_id) {
+ let should_ignore = matches!(body[binding_id].name.as_str(), Some(x) if x.starts_with("_"));
+ if !should_ignore {
+ acc.push(UnusedMut { local }.into())
+ }
+ }
}
}
- (mir::MutabilityReason::Not, true) => acc.push(UnusedMut { local }.into()),
}
}
}
@@ -1665,6 +1807,8 @@ impl DefWithBody {
DefWithBody::Static(it) => it.into(),
DefWithBody::Const(it) => it.into(),
DefWithBody::Variant(it) => it.into(),
+ // FIXME: don't ignore diagnostics for in type const
+ DefWithBody::InTypeConst(_) => return,
};
for diag in hir_ty::diagnostics::incorrect_case(db, krate, def.into()) {
acc.push(diag.into())
@@ -1686,6 +1830,10 @@ impl Function {
db.function_data(self.id).name.clone()
}
+ pub fn ty(self, db: &dyn HirDatabase) -> Type {
+ Type::from_value_def(db, self.id)
+ }
+
/// Get this function's return type
pub fn ret_type(self, db: &dyn HirDatabase) -> Type {
let resolver = self.id.resolver(db.upcast());
@@ -1797,12 +1945,41 @@ impl Function {
def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() })
}
- pub fn eval(self, db: &dyn HirDatabase) -> Result<(), MirEvalError> {
- let body = db
- .mir_body(self.id.into())
- .map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?;
- interpret_mir(db, &body, false)?;
- Ok(())
+ pub fn eval(
+ self,
+ db: &dyn HirDatabase,
+ span_formatter: impl Fn(FileId, TextRange) -> String,
+ ) -> String {
+ let body = match db.monomorphized_mir_body(
+ self.id.into(),
+ Substitution::empty(Interner),
+ db.trait_environment(self.id.into()),
+ ) {
+ Ok(body) => body,
+ Err(e) => {
+ let mut r = String::new();
+ _ = e.pretty_print(&mut r, db, &span_formatter);
+ return r;
+ }
+ };
+ let (result, stdout, stderr) = interpret_mir(db, &body, false);
+ let mut text = match result {
+ Ok(_) => "pass".to_string(),
+ Err(e) => {
+ let mut r = String::new();
+ _ = e.pretty_print(&mut r, db, &span_formatter);
+ r
+ }
+ };
+ if !stdout.is_empty() {
+ text += "\n--------- stdout ---------\n";
+ text += &stdout;
+ }
+ if !stderr.is_empty() {
+ text += "\n--------- stderr ---------\n";
+ text += &stderr;
+ }
+ text
}
}
@@ -1837,7 +2014,7 @@ impl Param {
}
pub fn name(&self, db: &dyn HirDatabase) -> Option<Name> {
- db.function_data(self.func.id).params[self.idx].0.clone()
+ Some(self.as_local(db)?.name(db))
}
pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
@@ -1878,7 +2055,7 @@ impl SelfParam {
func_data
.params
.first()
- .map(|(_, param)| match &**param {
+ .map(|param| match &**param {
TypeRef::Reference(.., mutability) => match mutability {
hir_def::type_ref::Mutability::Shared => Access::Shared,
hir_def::type_ref::Mutability::Mut => Access::Exclusive,
@@ -1921,6 +2098,17 @@ impl HasVisibility for Function {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct InTypeConst {
+ pub(crate) id: InTypeConstId,
+}
+
+impl InTypeConst {
+ pub fn module(self, db: &dyn HirDatabase) -> Module {
+ Module { id: self.id.lookup(db.upcast()).owner.module(db.upcast()) }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Const {
pub(crate) id: ConstId,
}
@@ -1939,24 +2127,12 @@ impl Const {
}
pub fn ty(self, db: &dyn HirDatabase) -> Type {
- let data = db.const_data(self.id);
- let resolver = self.id.resolver(db.upcast());
- let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
- let ty = ctx.lower_ty(&data.type_ref);
- Type::new_with_resolver_inner(db, &resolver, ty)
+ Type::from_value_def(db, self.id)
}
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
- let c = db.const_eval(self.id)?;
+ let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?;
let r = format!("{}", HexifiedConst(c).display(db));
- // We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our
- // implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to
- // the user.
- if r.contains("not-supported>") {
- return Err(ConstEvalError::MirEvalError(MirEvalError::NotSupported(
- "rendering complex constants".to_string(),
- )));
- }
return Ok(r);
}
}
@@ -1990,11 +2166,7 @@ impl Static {
}
pub fn ty(self, db: &dyn HirDatabase) -> Type {
- let data = db.static_data(self.id);
- let resolver = self.id.resolver(db.upcast());
- let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
- let ty = ctx.lower_ty(&data.type_ref);
- Type::new_with_resolver_inner(db, &resolver, ty)
+ Type::from_value_def(db, self.id)
}
}
@@ -2366,7 +2538,7 @@ impl AsAssocItem for DefWithBody {
match self {
DefWithBody::Function(it) => it.as_assoc_item(db),
DefWithBody::Const(it) => it.as_assoc_item(db),
- DefWithBody::Static(_) | DefWithBody::Variant(_) => None,
+ DefWithBody::Static(_) | DefWithBody::Variant(_) | DefWithBody::InTypeConst(_) => None,
}
}
}
@@ -2492,14 +2664,22 @@ impl GenericDef {
Either::Right(x) => GenericParam::TypeParam(x),
}
});
- let lt_params = generics
+ self.lifetime_params(db)
+ .into_iter()
+ .map(GenericParam::LifetimeParam)
+ .chain(ty_params)
+ .collect()
+ }
+
+ pub fn lifetime_params(self, db: &dyn HirDatabase) -> Vec<LifetimeParam> {
+ let generics = db.generic_params(self.into());
+ generics
.lifetimes
.iter()
.map(|(local_id, _)| LifetimeParam {
id: LifetimeParamId { parent: self.into(), local_id },
})
- .map(GenericParam::LifetimeParam);
- lt_params.chain(ty_params).collect()
+ .collect()
}
pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeOrConstParam> {
@@ -2545,8 +2725,12 @@ impl LocalSource {
self.source.file_id.original_file(db.upcast())
}
- pub fn name(&self) -> Option<ast::Name> {
- self.source.value.name()
+ pub fn file(&self) -> HirFileId {
+ self.source.file_id
+ }
+
+ pub fn name(&self) -> Option<InFile<ast::Name>> {
+ self.source.as_ref().map(|it| it.name()).transpose()
}
pub fn syntax(&self) -> &SyntaxNode {
@@ -2616,6 +2800,22 @@ impl Local {
/// All definitions for this local. Example: `let (a$0, _) | (_, a$0) = x;`
pub fn sources(self, db: &dyn HirDatabase) -> Vec<LocalSource> {
let (body, source_map) = db.body_with_source_map(self.parent);
+ self.sources_(db, &body, &source_map).collect()
+ }
+
+ /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = x;`
+ pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource {
+ let (body, source_map) = db.body_with_source_map(self.parent);
+ let src = self.sources_(db, &body, &source_map).next().unwrap();
+ src
+ }
+
+ fn sources_<'a>(
+ self,
+ db: &'a dyn HirDatabase,
+ body: &'a hir_def::body::Body,
+ source_map: &'a hir_def::body::BodySourceMap,
+ ) -> impl Iterator<Item = LocalSource> + 'a {
body[self.binding_id]
.definitions
.iter()
@@ -2628,14 +2828,7 @@ impl Local {
Either::Right(it) => Either::Right(it.to_node(&root)),
})
})
- .map(|source| LocalSource { local: self, source })
- .collect()
- }
-
- /// The leftmost definition for this local. Example: `let (a$0, _) | (_, a) = x;`
- pub fn primary_source(self, db: &dyn HirDatabase) -> LocalSource {
- let all_sources = self.sources(db);
- all_sources.into_iter().next().unwrap()
+ .map(move |source| LocalSource { local: self, source })
}
}
@@ -2689,9 +2882,7 @@ impl BuiltinAttr {
}
fn builtin(name: &str) -> Option<Self> {
- hir_def::builtin_attr::INERT_ATTRIBUTES
- .iter()
- .position(|tool| tool.name == name)
+ hir_def::attr::builtin::find_builtin_attr_idx(name)
.map(|idx| BuiltinAttr { krate: None, idx: idx as u32 })
}
@@ -2699,14 +2890,14 @@ impl BuiltinAttr {
// FIXME: Return a `Name` here
match self.krate {
Some(krate) => db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(),
- None => SmolStr::new(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].name),
+ None => SmolStr::new(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].name),
}
}
pub fn template(&self, _: &dyn HirDatabase) -> Option<AttributeTemplate> {
match self.krate {
Some(_) => None,
- None => Some(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].template),
+ None => Some(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].template),
}
}
}
@@ -2729,7 +2920,7 @@ impl ToolModule {
}
fn builtin(name: &str) -> Option<Self> {
- hir_def::builtin_attr::TOOL_MODULES
+ hir_def::attr::builtin::TOOL_MODULES
.iter()
.position(|&tool| tool == name)
.map(|idx| ToolModule { krate: None, idx: idx as u32 })
@@ -2739,7 +2930,7 @@ impl ToolModule {
// FIXME: Return a `Name` here
match self.krate {
Some(krate) => db.crate_def_map(krate).registered_tools()[self.idx as usize].clone(),
- None => SmolStr::new(hir_def::builtin_attr::TOOL_MODULES[self.idx as usize]),
+ None => SmolStr::new(hir_def::attr::builtin::TOOL_MODULES[self.idx as usize]),
}
}
}
@@ -3117,6 +3308,103 @@ impl TraitRef {
}
}
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Closure {
+ id: ClosureId,
+ subst: Substitution,
+}
+
+impl From<Closure> for ClosureId {
+ fn from(value: Closure) -> Self {
+ value.id
+ }
+}
+
+impl Closure {
+ fn as_ty(self) -> Ty {
+ TyKind::Closure(self.id, self.subst).intern(Interner)
+ }
+
+ pub fn display_with_id(&self, db: &dyn HirDatabase) -> String {
+ self.clone().as_ty().display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string()
+ }
+
+ pub fn display_with_impl(&self, db: &dyn HirDatabase) -> String {
+ self.clone().as_ty().display(db).with_closure_style(ClosureStyle::ImplFn).to_string()
+ }
+
+ pub fn captured_items(&self, db: &dyn HirDatabase) -> Vec<ClosureCapture> {
+ let owner = db.lookup_intern_closure((self.id).into()).0;
+ let infer = &db.infer(owner);
+ let info = infer.closure_info(&self.id);
+ info.0
+ .iter()
+ .cloned()
+ .map(|capture| ClosureCapture { owner, closure: self.id, capture })
+ .collect()
+ }
+
+ pub fn capture_types(&self, db: &dyn HirDatabase) -> Vec<Type> {
+ let owner = db.lookup_intern_closure((self.id).into()).0;
+ let infer = &db.infer(owner);
+ let (captures, _) = infer.closure_info(&self.id);
+ captures
+ .iter()
+ .cloned()
+ .map(|capture| Type {
+ env: db.trait_environment_for_body(owner),
+ ty: capture.ty(&self.subst),
+ })
+ .collect()
+ }
+
+ pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait {
+ let owner = db.lookup_intern_closure((self.id).into()).0;
+ let infer = &db.infer(owner);
+ let info = infer.closure_info(&self.id);
+ info.1
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct ClosureCapture {
+ owner: DefWithBodyId,
+ closure: ClosureId,
+ capture: hir_ty::CapturedItem,
+}
+
+impl ClosureCapture {
+ pub fn local(&self) -> Local {
+ Local { parent: self.owner, binding_id: self.capture.local() }
+ }
+
+ pub fn kind(&self) -> CaptureKind {
+ match self.capture.kind() {
+ hir_ty::CaptureKind::ByRef(
+ hir_ty::mir::BorrowKind::Shallow | hir_ty::mir::BorrowKind::Shared,
+ ) => CaptureKind::SharedRef,
+ hir_ty::CaptureKind::ByRef(hir_ty::mir::BorrowKind::Unique) => {
+ CaptureKind::UniqueSharedRef
+ }
+ hir_ty::CaptureKind::ByRef(hir_ty::mir::BorrowKind::Mut { .. }) => {
+ CaptureKind::MutableRef
+ }
+ hir_ty::CaptureKind::ByValue => CaptureKind::Move,
+ }
+ }
+
+ pub fn display_place(&self, db: &dyn HirDatabase) -> String {
+ self.capture.display_place(self.owner, db)
+ }
+}
+
+pub enum CaptureKind {
+ SharedRef,
+ UniqueSharedRef,
+ MutableRef,
+ Move,
+}
+
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Type {
env: Arc<TraitEnvironment>,
@@ -3164,24 +3452,33 @@ impl Type {
Type { env: environment, ty }
}
- fn from_def(db: &dyn HirDatabase, def: impl HasResolver + Into<TyDefId>) -> Type {
- let ty_def = def.into();
- let parent_subst = match ty_def {
- TyDefId::TypeAliasId(id) => match id.lookup(db.upcast()).container {
- ItemContainerId::TraitId(id) => {
- let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build();
- Some(subst)
- }
- ItemContainerId::ImplId(id) => {
- let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build();
- Some(subst)
- }
- _ => None,
+ fn from_def(db: &dyn HirDatabase, def: impl Into<TyDefId> + HasResolver) -> Type {
+ let ty = db.ty(def.into());
+ let substs = TyBuilder::unknown_subst(
+ db,
+ match def.into() {
+ TyDefId::AdtId(it) => GenericDefId::AdtId(it),
+ TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it),
+ TyDefId::BuiltinType(_) => return Type::new(db, def, ty.skip_binders().clone()),
},
- _ => None,
- };
- let ty = TyBuilder::def_ty(db, ty_def, parent_subst).fill_with_unknown().build();
- Type::new(db, def, ty)
+ );
+ Type::new(db, def, ty.substitute(Interner, &substs))
+ }
+
+ fn from_value_def(db: &dyn HirDatabase, def: impl Into<ValueTyDefId> + HasResolver) -> Type {
+ let ty = db.value_ty(def.into());
+ let substs = TyBuilder::unknown_subst(
+ db,
+ match def.into() {
+ ValueTyDefId::ConstId(it) => GenericDefId::ConstId(it),
+ ValueTyDefId::FunctionId(it) => GenericDefId::FunctionId(it),
+ ValueTyDefId::StructId(it) => GenericDefId::AdtId(AdtId::StructId(it)),
+ ValueTyDefId::UnionId(it) => GenericDefId::AdtId(AdtId::UnionId(it)),
+ ValueTyDefId::EnumVariantId(it) => GenericDefId::EnumVariantId(it),
+ ValueTyDefId::StaticId(_) => return Type::new(db, def, ty.skip_binders().clone()),
+ },
+ );
+ Type::new(db, def, ty.substitute(Interner, &substs))
}
pub fn new_slice(ty: Type) -> Type {
@@ -3237,6 +3534,14 @@ impl Type {
}
}
+ pub fn is_scalar(&self) -> bool {
+ matches!(self.ty.kind(Interner), TyKind::Scalar(_))
+ }
+
+ pub fn is_tuple(&self) -> bool {
+ matches!(self.ty.kind(Interner), TyKind::Tuple(..))
+ }
+
pub fn remove_ref(&self) -> Option<Type> {
match &self.ty.kind(Interner) {
TyKind::Ref(.., ty) => Some(self.derived(ty.clone())),
@@ -3331,7 +3636,7 @@ impl Type {
binders: CanonicalVarKinds::empty(Interner),
};
- db.trait_solve(self.env.krate, goal).is_some()
+ db.trait_solve(self.env.krate, self.env.block, goal).is_some()
}
pub fn normalize_trait_assoc_type(
@@ -3378,7 +3683,12 @@ impl Type {
}
pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> {
+ let mut the_ty = &self.ty;
let callee = match self.ty.kind(Interner) {
+ TyKind::Ref(_, _, ty) if ty.as_closure().is_some() => {
+ the_ty = ty;
+ Callee::Closure(ty.as_closure().unwrap())
+ }
TyKind::Closure(id, _) => Callee::Closure(*id),
TyKind::Function(_) => Callee::FnPtr,
TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?),
@@ -3393,7 +3703,7 @@ impl Type {
}
};
- let sig = self.ty.callable_sig(db)?;
+ let sig = the_ty.callable_sig(db)?;
Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false })
}
@@ -3401,6 +3711,13 @@ impl Type {
matches!(self.ty.kind(Interner), TyKind::Closure { .. })
}
+ pub fn as_closure(&self) -> Option<Closure> {
+ match self.ty.kind(Interner) {
+ TyKind::Closure(id, subst) => Some(Closure { id: *id, subst: subst.clone() }),
+ _ => None,
+ }
+ }
+
pub fn is_fn(&self) -> bool {
matches!(self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. })
}
@@ -3502,23 +3819,24 @@ impl Type {
}
}
- pub fn as_array(&self, _db: &dyn HirDatabase) -> Option<(Type, usize)> {
+ pub fn as_array(&self, db: &dyn HirDatabase) -> Option<(Type, usize)> {
if let TyKind::Array(ty, len) = &self.ty.kind(Interner) {
- try_const_usize(len).map(|x| (self.derived(ty.clone()), x as usize))
+ try_const_usize(db, len).map(|x| (self.derived(ty.clone()), x as usize))
} else {
None
}
}
- pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a {
+ /// Returns types that this type dereferences to (including this type itself). The returned
+ /// iterator won't yield the same type more than once even if the deref chain contains a cycle.
+ pub fn autoderef(&self, db: &dyn HirDatabase) -> impl Iterator<Item = Type> + '_ {
self.autoderef_(db).map(move |ty| self.derived(ty))
}
- fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a {
+ fn autoderef_(&self, db: &dyn HirDatabase) -> impl Iterator<Item = Ty> {
// There should be no inference vars in types passed here
let canonical = hir_ty::replace_errors_with_variables(&self.ty);
- let environment = self.env.clone();
- autoderef(db, environment, canonical).map(|canonical| canonical.value)
+ autoderef(db, self.env.clone(), canonical)
}
// This would be nicer if it just returned an iterator, but that runs into
@@ -3636,7 +3954,7 @@ impl Type {
self.as_adt()
.and_then(|a| a.lifetime(db).and_then(|lt| Some((&lt.name).to_smol_str())))
.into_iter()
- // add the type and const paramaters
+ // add the type and const parameters
.chain(self.type_and_const_arguments(db))
}
@@ -3955,6 +4273,11 @@ impl Type {
.map(|id| TypeOrConstParam { id }.split(db).either_into())
.collect()
}
+
+ pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+ db.layout_of_ty(self.ty.clone(), self.env.krate)
+ .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap()))
+ }
}
// FIXME: Document this
@@ -4064,6 +4387,48 @@ fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option<ast::Closu
}
}
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Layout(Arc<TyLayout>, Arc<TargetDataLayout>);
+
+impl Layout {
+ pub fn size(&self) -> u64 {
+ self.0.size.bytes()
+ }
+
+ pub fn align(&self) -> u64 {
+ self.0.align.abi.bytes()
+ }
+
+ pub fn niches(&self) -> Option<u128> {
+ Some(self.0.largest_niche?.available(&*self.1))
+ }
+
+ pub fn field_offset(&self, idx: usize) -> Option<u64> {
+ match self.0.fields {
+ layout::FieldsShape::Primitive => None,
+ layout::FieldsShape::Union(_) => Some(0),
+ layout::FieldsShape::Array { stride, count } => {
+ let i = u64::try_from(idx).ok()?;
+ (i < count).then_some((stride * i).bytes())
+ }
+ layout::FieldsShape::Arbitrary { ref offsets, .. } => Some(offsets.get(idx)?.bytes()),
+ }
+ }
+
+ pub fn enum_tag_size(&self) -> Option<usize> {
+ let tag_size =
+ if let layout::Variants::Multiple { tag, tag_encoding, .. } = &self.0.variants {
+ match tag_encoding {
+ TagEncoding::Direct => tag.size(&*self.1).bytes_usize(),
+ TagEncoding::Niche { .. } => 0,
+ }
+ } else {
+ return None;
+ };
+ Some(tag_size)
+ }
+}
+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum BindingMode {
Move,
@@ -4215,6 +4580,12 @@ impl HasCrate for Union {
}
}
+impl HasCrate for Enum {
+ fn krate(&self, db: &dyn HirDatabase) -> Crate {
+ self.module(db).krate()
+ }
+}
+
impl HasCrate for Field {
fn krate(&self, db: &dyn HirDatabase) -> Crate {
self.parent_def(db).module(db).krate()
@@ -4286,3 +4657,90 @@ impl HasCrate for Module {
Module::krate(*self)
}
}
+
+pub trait HasContainer {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer;
+}
+
+impl HasContainer for Module {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ // FIXME: handle block expressions as modules (their parent is in a different DefMap)
+ let def_map = self.id.def_map(db.upcast());
+ match def_map[self.id.local_id].parent {
+ Some(parent_id) => ItemContainer::Module(Module { id: def_map.module_id(parent_id) }),
+ None => ItemContainer::Crate(def_map.krate()),
+ }
+ }
+}
+
+impl HasContainer for Function {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ container_id_to_hir(self.id.lookup(db.upcast()).container)
+ }
+}
+
+impl HasContainer for Struct {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container })
+ }
+}
+
+impl HasContainer for Union {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container })
+ }
+}
+
+impl HasContainer for Enum {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container })
+ }
+}
+
+impl HasContainer for TypeAlias {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ container_id_to_hir(self.id.lookup(db.upcast()).container)
+ }
+}
+
+impl HasContainer for Const {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ container_id_to_hir(self.id.lookup(db.upcast()).container)
+ }
+}
+
+impl HasContainer for Static {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ container_id_to_hir(self.id.lookup(db.upcast()).container)
+ }
+}
+
+impl HasContainer for Trait {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container })
+ }
+}
+
+impl HasContainer for TraitAlias {
+ fn container(&self, db: &dyn HirDatabase) -> ItemContainer {
+ ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container })
+ }
+}
+
+fn container_id_to_hir(c: ItemContainerId) -> ItemContainer {
+ match c {
+ ItemContainerId::ExternBlockId(_id) => ItemContainer::ExternBlock(),
+ ItemContainerId::ModuleId(id) => ItemContainer::Module(Module { id }),
+ ItemContainerId::ImplId(id) => ItemContainer::Impl(Impl { id }),
+ ItemContainerId::TraitId(id) => ItemContainer::Trait(Trait { id }),
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum ItemContainer {
+ Trait(Trait),
+ Impl(Impl),
+ Module(Module),
+ ExternBlock(),
+ Crate(CrateId),
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 407ba6f65..5a76a9185 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -7,9 +7,10 @@ use std::{cell::RefCell, fmt, iter, mem, ops};
use base_db::{FileId, FileRange};
use either::Either;
use hir_def::{
- body,
- expr::Expr,
+ hir::Expr,
+ lower::LowerCtx,
macro_id_to_def_id,
+ nameres::MacroSubNs,
resolver::{self, HasResolver, Resolver, TypeNs},
type_ref::Mutability,
AsMacroCall, DefWithBodyId, FieldId, FunctionId, MacroId, TraitId, VariantId,
@@ -140,7 +141,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.parse(file_id)
}
- pub fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode> {
+ pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode {
self.imp.parse_or_expand(file_id)
}
@@ -350,6 +351,13 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.type_of_pat(pat)
}
+ /// It also includes the changes that binding mode makes in the type. For example in
+ /// `let ref x @ Some(_) = None` the result of `type_of_pat` is `Option<T>` but the result
+ /// of this function is `&mut Option<T>`
+ pub fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
+ self.imp.type_of_binding_in_pat(pat)
+ }
+
pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
self.imp.type_of_self(param)
}
@@ -475,10 +483,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.scope_at_offset(node, offset)
}
- pub fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
- self.imp.scope_for_def(def)
- }
-
pub fn assert_contains_node(&self, node: &SyntaxNode) {
self.imp.assert_contains_node(node)
}
@@ -518,23 +522,23 @@ impl<'db> SemanticsImpl<'db> {
tree
}
- fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode> {
- let node = self.db.parse_or_expand(file_id)?;
+ fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode {
+ let node = self.db.parse_or_expand(file_id);
self.cache(node.clone(), file_id);
- Some(node)
+ node
}
fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
let sa = self.analyze_no_infer(macro_call.syntax())?;
let file_id = sa.expand(self.db, InFile::new(sa.file_id, macro_call))?;
- let node = self.parse_or_expand(file_id)?;
+ let node = self.parse_or_expand(file_id);
Some(node)
}
fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
let src = self.wrap_node_infile(item.clone());
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
- self.parse_or_expand(macro_call_id.as_file())
+ Some(self.parse_or_expand(macro_call_id.as_file()))
}
fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
@@ -543,7 +547,7 @@ impl<'db> SemanticsImpl<'db> {
let call_id = self.with_ctx(|ctx| {
ctx.attr_to_derive_macro_call(src.with_value(&adt), src).map(|(_, it, _)| it)
})?;
- self.parse_or_expand(call_id.as_file())
+ Some(self.parse_or_expand(call_id.as_file()))
}
fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<Option<Macro>>> {
@@ -566,7 +570,7 @@ impl<'db> SemanticsImpl<'db> {
.into_iter()
.flat_map(|call| {
let file_id = call?.as_file();
- let node = self.db.parse_or_expand(file_id)?;
+ let node = self.db.parse_or_expand(file_id);
self.cache(node.clone(), file_id);
Some(node)
})
@@ -609,7 +613,7 @@ impl<'db> SemanticsImpl<'db> {
let krate = resolver.krate();
let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| {
resolver
- .resolve_path_as_macro(self.db.upcast(), &path)
+ .resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang))
.map(|it| macro_id_to_def_id(self.db.upcast(), it))
})?;
hir_expand::db::expand_speculative(
@@ -990,7 +994,7 @@ impl<'db> SemanticsImpl<'db> {
}
fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
- let root = self.parse_or_expand(src.file_id).unwrap();
+ let root = self.parse_or_expand(src.file_id);
let node = src.map(|it| it.to_node(&root));
node.as_ref().original_file_range(self.db.upcast())
}
@@ -1065,21 +1069,22 @@ impl<'db> SemanticsImpl<'db> {
fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
let analyze = self.analyze(ty.syntax())?;
- let ctx = body::LowerCtx::new(self.db.upcast(), analyze.file_id);
- let ty = hir_ty::TyLoweringContext::new(self.db, &analyze.resolver)
- .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone()));
+ let ctx = LowerCtx::with_file_id(self.db.upcast(), analyze.file_id);
+ let ty = hir_ty::TyLoweringContext::new(
+ self.db,
+ &analyze.resolver,
+ analyze.resolver.module().into(),
+ )
+ .lower_ty(&crate::TypeRef::from_ast(&ctx, ty.clone()));
Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
}
fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> {
let analyze = self.analyze(path.syntax())?;
let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id);
- let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
+ let ctx = LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
let hir_path = Path::from_src(path.clone(), &ctx)?;
- match analyze
- .resolver
- .resolve_path_in_type_ns_fully(self.db.upcast(), hir_path.mod_path())?
- {
+ match analyze.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), &hir_path)? {
TypeNs::TraitId(id) => Some(Trait { id }),
_ => None,
}
@@ -1141,6 +1146,10 @@ impl<'db> SemanticsImpl<'db> {
.map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced })
}
+ fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
+ self.analyze(pat.syntax())?.type_of_binding_in_pat(self.db, pat)
+ }
+
fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
self.analyze(param.syntax())?.type_of_self(self.db, param)
}
@@ -1298,12 +1307,6 @@ impl<'db> SemanticsImpl<'db> {
)
}
- fn scope_for_def(&self, def: Trait) -> SemanticsScope<'db> {
- let file_id = self.db.lookup_intern_trait(def.id).id.file_id();
- let resolver = def.id.resolver(self.db.upcast());
- SemanticsScope { db: self.db, file_id, resolver }
- }
-
fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
where
Def::Ast: AstNode,
@@ -1647,6 +1650,7 @@ impl<'a> SemanticsScope<'a> {
VisibleTraits(resolver.traits_in_scope(self.db.upcast()))
}
+ /// Calls the passed closure `f` on all names in scope.
pub fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
let scope = self.resolver.names_in_scope(self.db.upcast());
for (name, entries) in scope {
@@ -1674,7 +1678,7 @@ impl<'a> SemanticsScope<'a> {
/// Resolve a path as-if it was written at the given scope. This is
/// necessary a heuristic, as it doesn't take hygiene into account.
pub fn speculative_resolve(&self, path: &ast::Path) -> Option<PathResolution> {
- let ctx = body::LowerCtx::new(self.db.upcast(), self.file_id);
+ let ctx = LowerCtx::with_file_id(self.db.upcast(), self.file_id);
let path = Path::from_src(path.clone(), &ctx)?;
resolve_hir_path(self.db, &self.resolver, &path)
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
index f6f8c9a25..c50ffa4f8 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
@@ -14,7 +14,7 @@
//! expression, an item definition.
//!
//! Knowing only the syntax gives us relatively little info. For example,
-//! looking at the syntax of the function we can realise that it is a part of an
+//! looking at the syntax of the function we can realize that it is a part of an
//! `impl` block, but we won't be able to tell what trait function the current
//! function overrides, and whether it does that correctly. For that, we need to
//! go from [`ast::Fn`] to [`crate::Function`], and that's exactly what this
@@ -88,9 +88,11 @@
use base_db::FileId;
use hir_def::{
child_by_source::ChildBySource,
- dyn_map::DynMap,
- expr::{BindingId, LabelId},
- keys::{self, Key},
+ dyn_map::{
+ keys::{self, Key},
+ DynMap,
+ },
+ hir::{BindingId, LabelId},
AdtId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId,
GenericDefId, GenericParamId, ImplId, LifetimeParamId, MacroId, ModuleId, StaticId, StructId,
TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId,
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index c24d196e1..ecb1b306a 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -5,21 +5,19 @@
//!
//! So, this modules should not be used during hir construction, it exists
//! purely for "IDE needs".
-use std::{
- iter::{self, once},
- sync::Arc,
-};
+use std::iter::{self, once};
use either::Either;
use hir_def::{
body::{
- self,
scope::{ExprScopes, ScopeId},
Body, BodySourceMap,
},
- expr::{ExprId, Pat, PatId},
+ hir::{BindingId, ExprId, Pat, PatId},
lang_item::LangItem,
+ lower::LowerCtx,
macro_id_to_def_id,
+ nameres::MacroSubNs,
path::{ModPath, Path, PathKind},
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
type_ref::Mutability,
@@ -39,8 +37,9 @@ use hir_ty::{
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
UnsafeExpr,
},
- method_resolution::{self, lang_items_for_bin_op},
- Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, TyLoweringContext,
+ lang_items::lang_items_for_bin_op,
+ method_resolution, Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
+ TyLoweringContext,
};
use itertools::Itertools;
use smallvec::SmallVec;
@@ -48,6 +47,7 @@ use syntax::{
ast::{self, AstNode},
SyntaxKind, SyntaxNode, TextRange, TextSize,
};
+use triomphe::Arc;
use crate::{
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
@@ -134,13 +134,22 @@ impl SourceAnalyzer {
self.body_source_map()?.node_pat(src)
}
+ fn binding_id_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingId> {
+ let pat_id = self.pat_id(&pat.clone().into())?;
+ if let Pat::Bind { id, .. } = self.body()?.pats[pat_id] {
+ Some(id)
+ } else {
+ None
+ }
+ }
+
fn expand_expr(
&self,
db: &dyn HirDatabase,
expr: InFile<ast::MacroCall>,
) -> Option<InFile<ast::Expr>> {
let macro_file = self.body_source_map()?.node_macro_file(expr.as_ref())?;
- let expanded = db.parse_or_expand(macro_file)?;
+ let expanded = db.parse_or_expand(macro_file);
let res = if let Some(stmts) = ast::MacroStmts::cast(expanded.clone()) {
match stmts.expr()? {
ast::Expr::MacroExpr(mac) => {
@@ -199,6 +208,18 @@ impl SourceAnalyzer {
Some((mk_ty(ty), coerced.map(mk_ty)))
}
+ pub(crate) fn type_of_binding_in_pat(
+ &self,
+ db: &dyn HirDatabase,
+ pat: &ast::IdentPat,
+ ) -> Option<Type> {
+ let binding_id = self.binding_id_of_pat(pat)?;
+ let infer = self.infer.as_ref()?;
+ let ty = infer[binding_id].clone();
+ let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
+ Some(mk_ty(ty))
+ }
+
pub(crate) fn type_of_self(
&self,
db: &dyn HirDatabase,
@@ -215,9 +236,9 @@ impl SourceAnalyzer {
_db: &dyn HirDatabase,
pat: &ast::IdentPat,
) -> Option<BindingMode> {
- let pat_id = self.pat_id(&pat.clone().into())?;
+ let binding_id = self.binding_id_of_pat(pat)?;
let infer = self.infer.as_ref()?;
- infer.pat_binding_modes.get(&pat_id).map(|bm| match bm {
+ infer.binding_modes.get(binding_id).map(|bm| match bm {
hir_ty::BindingMode::Move => BindingMode::Move,
hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut),
hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => {
@@ -420,7 +441,10 @@ impl SourceAnalyzer {
None
} else {
// Shorthand syntax, resolve to the local
- let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone()));
+ let path = Path::from_known_path_with_no_generic(ModPath::from_segments(
+ PathKind::Plain,
+ once(local_name.clone()),
+ ));
match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
Some(ValueNs::LocalBinding(binding_id)) => {
Some(Local { binding_id, parent: self.resolver.body_owner()? })
@@ -459,9 +483,11 @@ impl SourceAnalyzer {
db: &dyn HirDatabase,
macro_call: InFile<&ast::MacroCall>,
) -> Option<Macro> {
- let ctx = body::LowerCtx::new(db.upcast(), macro_call.file_id);
+ let ctx = LowerCtx::with_file_id(db.upcast(), macro_call.file_id);
let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
- self.resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(|it| it.into())
+ self.resolver
+ .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Bang))
+ .map(|it| it.into())
}
pub(crate) fn resolve_bind_pat_to_const(
@@ -571,7 +597,7 @@ impl SourceAnalyzer {
// This must be a normal source file rather than macro file.
let hygiene = Hygiene::new(db.upcast(), self.file_id);
- let ctx = body::LowerCtx::with_hygiene(db.upcast(), &hygiene);
+ let ctx = LowerCtx::with_hygiene(db.upcast(), &hygiene);
let hir_path = Path::from_src(path.clone(), &ctx)?;
// Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are
@@ -655,7 +681,7 @@ impl SourceAnalyzer {
}
}
}
- return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) {
+ return match resolve_hir_path_as_attr_macro(db, &self.resolver, &hir_path) {
Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))),
// this labels any path that starts with a tool module as the tool itself, this is technically wrong
// but there is no benefit in differentiating these two cases for the time being
@@ -733,7 +759,7 @@ impl SourceAnalyzer {
let krate = self.resolver.krate();
let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| {
self.resolver
- .resolve_path_as_macro(db.upcast(), &path)
+ .resolve_path_as_macro(db.upcast(), &path, Some(MacroSubNs::Bang))
.map(|it| macro_id_to_def_id(db.upcast(), it))
})?;
Some(macro_call_id.as_file()).filter(|it| it.expansion_level(db.upcast()) < 64)
@@ -801,15 +827,11 @@ impl SourceAnalyzer {
func: FunctionId,
substs: Substitution,
) -> FunctionId {
- let krate = self.resolver.krate();
let owner = match self.resolver.body_owner() {
Some(it) => it,
None => return func,
};
- let env = owner.as_generic_def_id().map_or_else(
- || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
- |d| db.trait_environment(d),
- );
+ let env = db.trait_environment_for_body(owner);
method_resolution::lookup_impl_method(db, env, func, substs).0
}
@@ -819,15 +841,11 @@ impl SourceAnalyzer {
const_id: ConstId,
subs: Substitution,
) -> ConstId {
- let krate = self.resolver.krate();
let owner = match self.resolver.body_owner() {
Some(it) => it,
None => return const_id,
};
- let env = owner.as_generic_def_id().map_or_else(
- || Arc::new(hir_ty::TraitEnvironment::empty(krate)),
- |d| db.trait_environment(d),
- );
+ let env = db.trait_environment_for_body(owner);
method_resolution::lookup_impl_const(db, env, const_id, subs).0
}
@@ -941,12 +959,14 @@ pub(crate) fn resolve_hir_path(
}
#[inline]
-pub(crate) fn resolve_hir_path_as_macro(
+pub(crate) fn resolve_hir_path_as_attr_macro(
db: &dyn HirDatabase,
resolver: &Resolver,
path: &Path,
) -> Option<Macro> {
- resolver.resolve_path_as_macro(db.upcast(), path.mod_path()).map(Into::into)
+ resolver
+ .resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Attr))
+ .map(Into::into)
}
fn resolve_hir_path_(
@@ -958,12 +978,12 @@ fn resolve_hir_path_(
let types = || {
let (ty, unresolved) = match path.type_anchor() {
Some(type_ref) => {
- let (_, res) = TyLoweringContext::new(db, resolver).lower_ty_ext(type_ref);
+ let (_, res) = TyLoweringContext::new(db, resolver, resolver.module().into())
+ .lower_ty_ext(type_ref);
res.map(|ty_ns| (ty_ns, path.segments().first()))
}
None => {
- let (ty, remaining_idx) =
- resolver.resolve_path_in_type_ns(db.upcast(), path.mod_path())?;
+ let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
match remaining_idx {
Some(remaining_idx) => {
if remaining_idx + 1 == path.segments().len() {
@@ -1019,7 +1039,7 @@ fn resolve_hir_path_(
let body_owner = resolver.body_owner();
let values = || {
- resolver.resolve_path_in_value_ns_fully(db.upcast(), path.mod_path()).and_then(|val| {
+ resolver.resolve_path_in_value_ns_fully(db.upcast(), path).and_then(|val| {
let res = match val {
ValueNs::LocalBinding(binding_id) => {
let var = Local { parent: body_owner?, binding_id };
@@ -1039,14 +1059,14 @@ fn resolve_hir_path_(
let items = || {
resolver
- .resolve_module_path_in_items(db.upcast(), path.mod_path())
+ .resolve_module_path_in_items(db.upcast(), path.mod_path()?)
.take_types()
.map(|it| PathResolution::Def(it.into()))
};
let macros = || {
resolver
- .resolve_path_as_macro(db.upcast(), path.mod_path())
+ .resolve_path_as_macro(db.upcast(), path.mod_path()?, None)
.map(|def| PathResolution::Def(ModuleDef::Macro(def.into())))
};
@@ -1074,7 +1094,7 @@ fn resolve_hir_path_qualifier(
path: &Path,
) -> Option<PathResolution> {
resolver
- .resolve_path_in_type_ns_fully(db.upcast(), path.mod_path())
+ .resolve_path_in_type_ns_fully(db.upcast(), &path)
.map(|ty| match ty {
TypeNs::SelfType(it) => PathResolution::SelfType(it.into()),
TypeNs::GenericParam(id) => PathResolution::TypeParam(id.into()),
@@ -1089,7 +1109,7 @@ fn resolve_hir_path_qualifier(
})
.or_else(|| {
resolver
- .resolve_module_path_in_items(db.upcast(), path.mod_path())
+ .resolve_module_path_in_items(db.upcast(), path.mod_path()?)
.take_types()
.map(|it| PathResolution::Def(it.into()))
})
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index a9afa1c6f..43d957412 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -2,23 +2,25 @@
use base_db::FileRange;
use hir_def::{
- item_tree::ItemTreeNode, src::HasSource, AdtId, AssocItemId, AssocItemLoc, DefWithBodyId,
- HasModule, ImplId, ItemContainerId, Lookup, MacroId, ModuleDefId, ModuleId, TraitId,
+ src::HasSource, AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId,
+ ModuleDefId, ModuleId, TraitId,
};
use hir_expand::{HirFileId, InFile};
use hir_ty::db::HirDatabase;
use syntax::{ast::HasName, AstNode, SmolStr, SyntaxNode, SyntaxNodePtr};
-use crate::{Module, Semantics};
+use crate::{Module, ModuleDef, Semantics};
/// The actual data that is stored in the index. It should be as compact as
/// possible.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FileSymbol {
+ // even though name can be derived from the def, we store it for efficiency
pub name: SmolStr,
+ pub def: ModuleDef,
pub loc: DeclarationLocation,
- pub kind: FileSymbolKind,
pub container_name: Option<SmolStr>,
+ pub is_alias: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -32,18 +34,26 @@ pub struct DeclarationLocation {
}
impl DeclarationLocation {
- pub fn syntax<DB: HirDatabase>(&self, sema: &Semantics<'_, DB>) -> Option<SyntaxNode> {
- let root = sema.parse_or_expand(self.hir_file_id)?;
- Some(self.ptr.to_node(&root))
+ pub fn syntax<DB: HirDatabase>(&self, sema: &Semantics<'_, DB>) -> SyntaxNode {
+ let root = sema.parse_or_expand(self.hir_file_id);
+ self.ptr.to_node(&root)
}
- pub fn original_range(&self, db: &dyn HirDatabase) -> Option<FileRange> {
- let node = resolve_node(db, self.hir_file_id, &self.ptr)?;
- Some(node.as_ref().original_file_range(db.upcast()))
+ pub fn original_range(&self, db: &dyn HirDatabase) -> FileRange {
+ if let Some(file_id) = self.hir_file_id.file_id() {
+ // fast path to prevent parsing
+ return FileRange { file_id, range: self.ptr.text_range() };
+ }
+ let node = resolve_node(db, self.hir_file_id, &self.ptr);
+ node.as_ref().original_file_range(db.upcast())
}
pub fn original_name_range(&self, db: &dyn HirDatabase) -> Option<FileRange> {
- let node = resolve_node(db, self.hir_file_id, &self.name_ptr)?;
+ if let Some(file_id) = self.hir_file_id.file_id() {
+ // fast path to prevent parsing
+ return Some(FileRange { file_id, range: self.name_ptr.text_range() });
+ }
+ let node = resolve_node(db, self.hir_file_id, &self.name_ptr);
node.as_ref().original_file_range_opt(db.upcast())
}
}
@@ -52,38 +62,10 @@ fn resolve_node(
db: &dyn HirDatabase,
file_id: HirFileId,
ptr: &SyntaxNodePtr,
-) -> Option<InFile<SyntaxNode>> {
- let root = db.parse_or_expand(file_id)?;
+) -> InFile<SyntaxNode> {
+ let root = db.parse_or_expand(file_id);
let node = ptr.to_node(&root);
- Some(InFile::new(file_id, node))
-}
-
-#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
-pub enum FileSymbolKind {
- Const,
- Enum,
- Function,
- Macro,
- Module,
- Static,
- Struct,
- Trait,
- TraitAlias,
- TypeAlias,
- Union,
-}
-
-impl FileSymbolKind {
- pub fn is_type(self: FileSymbolKind) -> bool {
- matches!(
- self,
- FileSymbolKind::Struct
- | FileSymbolKind::Enum
- | FileSymbolKind::Trait
- | FileSymbolKind::TypeAlias
- | FileSymbolKind::Union
- )
- }
+ InFile::new(file_id, node)
}
/// Represents an outstanding module that the symbol collector must collect symbols from.
@@ -102,21 +84,33 @@ pub struct SymbolCollector<'a> {
/// Given a [`ModuleId`] and a [`HirDatabase`], use the DefMap for the module's crate to collect
/// all symbols that should be indexed for the given module.
impl<'a> SymbolCollector<'a> {
- pub fn collect(db: &dyn HirDatabase, module: Module) -> Vec<FileSymbol> {
- let mut symbol_collector = SymbolCollector {
+ pub fn new(db: &'a dyn HirDatabase) -> Self {
+ SymbolCollector {
db,
symbols: Default::default(),
+ work: Default::default(),
current_container_name: None,
- // The initial work is the root module we're collecting, additional work will
- // be populated as we traverse the module's definitions.
- work: vec![SymbolCollectorWork { module_id: module.into(), parent: None }],
- };
+ }
+ }
+
+ pub fn collect(&mut self, module: Module) {
+ // The initial work is the root module we're collecting, additional work will
+ // be populated as we traverse the module's definitions.
+ self.work.push(SymbolCollectorWork { module_id: module.into(), parent: None });
- while let Some(work) = symbol_collector.work.pop() {
- symbol_collector.do_work(work);
+ while let Some(work) = self.work.pop() {
+ self.do_work(work);
}
+ }
+
+ pub fn finish(self) -> Vec<FileSymbol> {
+ self.symbols
+ }
- symbol_collector.symbols
+ pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Vec<FileSymbol> {
+ let mut symbol_collector = SymbolCollector::new(db);
+ symbol_collector.collect(module);
+ symbol_collector.finish()
}
fn do_work(&mut self, work: SymbolCollectorWork) {
@@ -134,36 +128,34 @@ impl<'a> SymbolCollector<'a> {
match module_def_id {
ModuleDefId::ModuleId(id) => self.push_module(id),
ModuleDefId::FunctionId(id) => {
- self.push_decl_assoc(id, FileSymbolKind::Function);
+ self.push_decl(id);
self.collect_from_body(id);
}
- ModuleDefId::AdtId(AdtId::StructId(id)) => {
- self.push_decl(id, FileSymbolKind::Struct)
- }
- ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id, FileSymbolKind::Enum),
- ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id, FileSymbolKind::Union),
+ ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id),
+ ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id),
+ ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id),
ModuleDefId::ConstId(id) => {
- self.push_decl_assoc(id, FileSymbolKind::Const);
+ self.push_decl(id);
self.collect_from_body(id);
}
ModuleDefId::StaticId(id) => {
- self.push_decl_assoc(id, FileSymbolKind::Static);
+ self.push_decl(id);
self.collect_from_body(id);
}
ModuleDefId::TraitId(id) => {
- self.push_decl(id, FileSymbolKind::Trait);
+ self.push_decl(id);
self.collect_from_trait(id);
}
ModuleDefId::TraitAliasId(id) => {
- self.push_decl(id, FileSymbolKind::TraitAlias);
+ self.push_decl(id);
}
ModuleDefId::TypeAliasId(id) => {
- self.push_decl_assoc(id, FileSymbolKind::TypeAlias);
+ self.push_decl(id);
}
ModuleDefId::MacroId(id) => match id {
- MacroId::Macro2Id(id) => self.push_decl(id, FileSymbolKind::Macro),
- MacroId::MacroRulesId(id) => self.push_decl(id, FileSymbolKind::Macro),
- MacroId::ProcMacroId(id) => self.push_decl(id, FileSymbolKind::Macro),
+ MacroId::Macro2Id(id) => self.push_decl(id),
+ MacroId::MacroRulesId(id) => self.push_decl(id),
+ MacroId::ProcMacroId(id) => self.push_decl(id),
},
// Don't index these.
ModuleDefId::BuiltinType(_) => {}
@@ -183,9 +175,9 @@ impl<'a> SymbolCollector<'a> {
for &id in id {
if id.module(self.db.upcast()) == module_id {
match id {
- MacroId::Macro2Id(id) => self.push_decl(id, FileSymbolKind::Macro),
- MacroId::MacroRulesId(id) => self.push_decl(id, FileSymbolKind::Macro),
- MacroId::ProcMacroId(id) => self.push_decl(id, FileSymbolKind::Macro),
+ MacroId::Macro2Id(id) => self.push_decl(id),
+ MacroId::MacroRulesId(id) => self.push_decl(id),
+ MacroId::ProcMacroId(id) => self.push_decl(id),
}
}
}
@@ -233,124 +225,95 @@ impl<'a> SymbolCollector<'a> {
}
}
- fn current_container_name(&self) -> Option<SmolStr> {
- self.current_container_name.clone()
- }
-
fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option<SmolStr> {
match body_id {
- DefWithBodyId::FunctionId(id) => Some(
- id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(),
- ),
- DefWithBodyId::StaticId(id) => Some(
- id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(),
- ),
- DefWithBodyId::ConstId(id) => Some(
- id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(),
- ),
- DefWithBodyId::VariantId(id) => Some({
- let db = self.db.upcast();
- id.parent.lookup(db).source(db).value.name()?.text().into()
- }),
+ DefWithBodyId::FunctionId(id) => Some(self.db.function_data(id).name.to_smol_str()),
+ DefWithBodyId::StaticId(id) => Some(self.db.static_data(id).name.to_smol_str()),
+ DefWithBodyId::ConstId(id) => Some(self.db.const_data(id).name.as_ref()?.to_smol_str()),
+ DefWithBodyId::VariantId(id) => {
+ Some(self.db.enum_data(id.parent).variants[id.local_id].name.to_smol_str())
+ }
+ DefWithBodyId::InTypeConstId(_) => Some("in type const".into()),
}
}
fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) {
match assoc_item_id {
- AssocItemId::FunctionId(id) => self.push_decl_assoc(id, FileSymbolKind::Function),
- AssocItemId::ConstId(id) => self.push_decl_assoc(id, FileSymbolKind::Const),
- AssocItemId::TypeAliasId(id) => self.push_decl_assoc(id, FileSymbolKind::TypeAlias),
+ AssocItemId::FunctionId(id) => self.push_decl(id),
+ AssocItemId::ConstId(id) => self.push_decl(id),
+ AssocItemId::TypeAliasId(id) => self.push_decl(id),
}
}
- fn push_decl_assoc<L, T>(&mut self, id: L, kind: FileSymbolKind)
+ fn push_decl<L>(&mut self, id: L)
where
- L: Lookup<Data = AssocItemLoc<T>>,
- T: ItemTreeNode,
- <T as ItemTreeNode>::Source: HasName,
+ L: Lookup + Into<ModuleDefId>,
+ <L as Lookup>::Data: HasSource,
+ <<L as Lookup>::Data as HasSource>::Value: HasName,
{
- fn container_name(db: &dyn HirDatabase, container: ItemContainerId) -> Option<SmolStr> {
- match container {
- ItemContainerId::ModuleId(module_id) => {
- let module = Module::from(module_id);
- module.name(db).and_then(|name| name.as_text())
- }
- ItemContainerId::TraitId(trait_id) => {
- let trait_data = db.trait_data(trait_id);
- trait_data.name.as_text()
- }
- ItemContainerId::ImplId(_) | ItemContainerId::ExternBlockId(_) => None,
+ let loc = id.lookup(self.db.upcast());
+ let source = loc.source(self.db.upcast());
+ let Some(name_node) = source.value.name() else { return };
+ let def = ModuleDef::from(id.into());
+ let dec_loc = DeclarationLocation {
+ hir_file_id: source.file_id,
+ ptr: SyntaxNodePtr::new(source.value.syntax()),
+ name_ptr: SyntaxNodePtr::new(name_node.syntax()),
+ };
+
+ if let Some(attrs) = def.attrs(self.db) {
+ for alias in attrs.doc_aliases() {
+ self.symbols.push(FileSymbol {
+ name: alias,
+ def,
+ loc: dec_loc.clone(),
+ container_name: self.current_container_name.clone(),
+ is_alias: true,
+ });
}
}
- self.push_file_symbol(|s| {
- let loc = id.lookup(s.db.upcast());
- let source = loc.source(s.db.upcast());
- let name_node = source.value.name()?;
- let container_name =
- container_name(s.db, loc.container).or_else(|| s.current_container_name());
-
- Some(FileSymbol {
- name: name_node.text().into(),
- kind,
- container_name,
- loc: DeclarationLocation {
- hir_file_id: source.file_id,
- ptr: SyntaxNodePtr::new(source.value.syntax()),
- name_ptr: SyntaxNodePtr::new(name_node.syntax()),
- },
- })
- })
- }
-
- fn push_decl<L>(&mut self, id: L, kind: FileSymbolKind)
- where
- L: Lookup,
- <L as Lookup>::Data: HasSource,
- <<L as Lookup>::Data as HasSource>::Value: HasName,
- {
- self.push_file_symbol(|s| {
- let loc = id.lookup(s.db.upcast());
- let source = loc.source(s.db.upcast());
- let name_node = source.value.name()?;
-
- Some(FileSymbol {
- name: name_node.text().into(),
- kind,
- container_name: s.current_container_name(),
- loc: DeclarationLocation {
- hir_file_id: source.file_id,
- ptr: SyntaxNodePtr::new(source.value.syntax()),
- name_ptr: SyntaxNodePtr::new(name_node.syntax()),
- },
- })
- })
+ self.symbols.push(FileSymbol {
+ name: name_node.text().into(),
+ def,
+ container_name: self.current_container_name.clone(),
+ loc: dec_loc,
+ is_alias: false,
+ });
}
fn push_module(&mut self, module_id: ModuleId) {
- self.push_file_symbol(|s| {
- let def_map = module_id.def_map(s.db.upcast());
- let module_data = &def_map[module_id.local_id];
- let declaration = module_data.origin.declaration()?;
- let module = declaration.to_node(s.db.upcast());
- let name_node = module.name()?;
-
- Some(FileSymbol {
- name: name_node.text().into(),
- kind: FileSymbolKind::Module,
- container_name: s.current_container_name(),
- loc: DeclarationLocation {
- hir_file_id: declaration.file_id,
- ptr: SyntaxNodePtr::new(module.syntax()),
- name_ptr: SyntaxNodePtr::new(name_node.syntax()),
- },
- })
- })
- }
+ let def_map = module_id.def_map(self.db.upcast());
+ let module_data = &def_map[module_id.local_id];
+ let Some(declaration) = module_data.origin.declaration() else { return };
+ let module = declaration.to_node(self.db.upcast());
+ let Some(name_node) = module.name() else { return };
+ let dec_loc = DeclarationLocation {
+ hir_file_id: declaration.file_id,
+ ptr: SyntaxNodePtr::new(module.syntax()),
+ name_ptr: SyntaxNodePtr::new(name_node.syntax()),
+ };
- fn push_file_symbol(&mut self, f: impl FnOnce(&Self) -> Option<FileSymbol>) {
- if let Some(file_symbol) = f(self) {
- self.symbols.push(file_symbol);
+ let def = ModuleDef::Module(module_id.into());
+
+ if let Some(attrs) = def.attrs(self.db) {
+ for alias in attrs.doc_aliases() {
+ self.symbols.push(FileSymbol {
+ name: alias,
+ def,
+ loc: dec_loc.clone(),
+ container_name: self.current_container_name.clone(),
+ is_alias: true,
+ });
+ }
}
+
+ self.symbols.push(FileSymbol {
+ name: name_node.text().into(),
+ def: ModuleDef::Module(module_id.into()),
+ container_name: self.current_container_name.clone(),
+ loc: dec_loc,
+ is_alias: false,
+ });
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
index 785ae3d09..8bc285614 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_explicit_type.rs
@@ -69,7 +69,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
return None;
}
- let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
+ let inferred_type = ty.display_source_code(ctx.db(), module.into(), false).ok()?;
acc.add(
AssistId("add_explicit_type", AssistKind::RefactorRewrite),
format!("Insert explicit type `{inferred_type}`"),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 4e11b31de..d07c63726 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -1,13 +1,9 @@
use hir::HasSource;
-use ide_db::syntax_helpers::insert_whitespace_into_node::insert_ws_into;
use syntax::ast::{self, make, AstNode};
use crate::{
assist_context::{AssistContext, Assists},
- utils::{
- add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, render_snippet,
- Cursor, DefaultMethods,
- },
+ utils::{add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, DefaultMethods},
AssistId, AssistKind,
};
@@ -130,50 +126,36 @@ fn add_missing_impl_members_inner(
}
let target = impl_def.syntax().text_range();
- acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
- let missing_items = missing_items
- .into_iter()
- .map(|it| {
- if ctx.sema.hir_file_for(it.syntax()).is_macro() {
- if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
- return it;
- }
- }
- it.clone_for_update()
- })
- .collect();
- let (new_impl_def, first_new_item) = add_trait_assoc_items_to_impl(
+ acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |edit| {
+ let new_impl_def = edit.make_mut(impl_def.clone());
+ let first_new_item = add_trait_assoc_items_to_impl(
&ctx.sema,
- missing_items,
+ &missing_items,
trait_,
- impl_def.clone(),
+ &new_impl_def,
target_scope,
);
- match ctx.config.snippet_cap {
- None => builder.replace(target, new_impl_def.to_string()),
- Some(cap) => {
- let mut cursor = Cursor::Before(first_new_item.syntax());
- let placeholder;
- if let DefaultMethods::No = mode {
- if let ast::AssocItem::Fn(func) = &first_new_item {
- if try_gen_trait_body(ctx, func, trait_ref, &impl_def).is_none() {
- if let Some(m) =
- func.syntax().descendants().find_map(ast::MacroCall::cast)
- {
- if m.syntax().text() == "todo!()" {
- placeholder = m;
- cursor = Cursor::Replace(placeholder.syntax());
- }
+
+ if let Some(cap) = ctx.config.snippet_cap {
+ let mut placeholder = None;
+ if let DefaultMethods::No = mode {
+ if let ast::AssocItem::Fn(func) = &first_new_item {
+ if try_gen_trait_body(ctx, func, trait_ref, &impl_def).is_none() {
+ if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
+ {
+ if m.syntax().text() == "todo!()" {
+ placeholder = Some(m);
}
}
}
}
- builder.replace_snippet(
- cap,
- target,
- render_snippet(cap, new_impl_def.syntax(), cursor),
- )
}
+
+ if let Some(macro_call) = placeholder {
+ edit.add_placeholder_snippet(cap, macro_call);
+ } else {
+ edit.add_tabstop_before(cap, first_new_item);
+ };
};
})
}
@@ -184,7 +166,8 @@ fn try_gen_trait_body(
trait_ref: hir::TraitRef,
impl_def: &ast::Impl,
) -> Option<()> {
- let trait_path = make::ext::ident_path(&trait_ref.trait_().name(ctx.db()).to_string());
+ let trait_path =
+ make::ext::ident_path(&trait_ref.trait_().name(ctx.db()).display(ctx.db()).to_string());
let hir_ty = ctx.sema.resolve_type(&impl_def.self_ty()?)?;
let adt = hir_ty.as_adt()?.source(ctx.db())?;
gen_trait_fn_body(func, &trait_path, &adt.value, Some(trait_ref))
@@ -252,7 +235,7 @@ impl Foo for S {
}
#[test]
- fn test_copied_overriden_members() {
+ fn test_copied_overridden_members() {
check_assist(
add_missing_impl_members,
r#"
@@ -365,6 +348,125 @@ impl<U> Foo<U> for S {
}
#[test]
+ fn test_lifetime_substitution() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+pub trait Trait<'a, 'b, A, B, C> {
+ fn foo(&self, one: &'a A, anoter: &'b B) -> &'a C;
+}
+
+impl<'x, 'y, T, V, U> Trait<'x, 'y, T, V, U> for () {$0}"#,
+ r#"
+pub trait Trait<'a, 'b, A, B, C> {
+ fn foo(&self, one: &'a A, anoter: &'b B) -> &'a C;
+}
+
+impl<'x, 'y, T, V, U> Trait<'x, 'y, T, V, U> for () {
+ fn foo(&self, one: &'x T, anoter: &'y V) -> &'x U {
+ ${0:todo!()}
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn test_lifetime_substitution_with_body() {
+ check_assist(
+ add_missing_default_members,
+ r#"
+pub trait Trait<'a, 'b, A, B, C: Default> {
+ fn foo(&self, _one: &'a A, _anoter: &'b B) -> (C, &'a i32) {
+ let value: &'a i32 = &0;
+ (C::default(), value)
+ }
+}
+
+impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {$0}"#,
+ r#"
+pub trait Trait<'a, 'b, A, B, C: Default> {
+ fn foo(&self, _one: &'a A, _anoter: &'b B) -> (C, &'a i32) {
+ let value: &'a i32 = &0;
+ (C::default(), value)
+ }
+}
+
+impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {
+ $0fn foo(&self, _one: &'x T, _anoter: &'y V) -> (U, &'x i32) {
+ let value: &'x i32 = &0;
+ (<U>::default(), value)
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn test_const_substitution() {
+ check_assist(
+ add_missing_default_members,
+ r#"
+struct Bar<const: N: bool> {
+ bar: [i32, N]
+}
+
+trait Foo<const N: usize, T> {
+ fn get_n_sq(&self, arg: &T) -> usize { N * N }
+ fn get_array(&self, arg: Bar<N>) -> [i32; N] { [1; N] }
+}
+
+struct S<T> {
+ wrapped: T
+}
+
+impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
+ $0
+}"#,
+ r#"
+struct Bar<const: N: bool> {
+ bar: [i32, N]
+}
+
+trait Foo<const N: usize, T> {
+ fn get_n_sq(&self, arg: &T) -> usize { N * N }
+ fn get_array(&self, arg: Bar<N>) -> [i32; N] { [1; N] }
+}
+
+struct S<T> {
+ wrapped: T
+}
+
+impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
+ $0fn get_n_sq(&self, arg: &Z) -> usize { X * X }
+
+ fn get_array(&self, arg: Bar<X>) -> [i32; X] { [1; X] }
+}"#,
+ )
+ }
+
+ #[test]
+ fn test_const_substitution_2() {
+ check_assist(
+ add_missing_default_members,
+ r#"
+trait Foo<const N: usize, const M: usize, T> {
+ fn get_sum(&self, arg: &T) -> usize { N + M }
+}
+
+impl<X> Foo<42, {20 + 22}, X> for () {
+ $0
+}"#,
+ r#"
+trait Foo<const N: usize, const M: usize, T> {
+ fn get_sum(&self, arg: &T) -> usize { N + M }
+}
+
+impl<X> Foo<42, {20 + 22}, X> for () {
+ $0fn get_sum(&self, arg: &X) -> usize { 42 + {20 + 22} }
+}"#,
+ )
+ }
+
+ #[test]
fn test_cursor_after_empty_impl_def() {
check_assist(
add_missing_impl_members,
@@ -746,6 +848,115 @@ impl Foo<T> for S<T> {
}
#[test]
+ fn test_qualify_generic_default_parameter() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+mod m {
+ pub struct S;
+ pub trait Foo<T = S> {
+ fn bar(&self, other: &T);
+ }
+}
+
+struct S;
+impl m::Foo for S { $0 }"#,
+ r#"
+mod m {
+ pub struct S;
+ pub trait Foo<T = S> {
+ fn bar(&self, other: &T);
+ }
+}
+
+struct S;
+impl m::Foo for S {
+ fn bar(&self, other: &m::S) {
+ ${0:todo!()}
+ }
+}"#,
+ )
+ }
+
+ #[test]
+ fn test_qualify_generic_default_parameter_2() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+mod m {
+ pub struct Wrapper<T, V> {
+ one: T,
+ another: V
+ };
+ pub struct S;
+ pub trait Foo<T = Wrapper<S, bool>> {
+ fn bar(&self, other: &T);
+ }
+}
+
+struct S;
+impl m::Foo for S { $0 }"#,
+ r#"
+mod m {
+ pub struct Wrapper<T, V> {
+ one: T,
+ another: V
+ };
+ pub struct S;
+ pub trait Foo<T = Wrapper<S, bool>> {
+ fn bar(&self, other: &T);
+ }
+}
+
+struct S;
+impl m::Foo for S {
+ fn bar(&self, other: &m::Wrapper<m::S, bool>) {
+ ${0:todo!()}
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn test_qualify_generic_default_parameter_3() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+mod m {
+ pub struct Wrapper<T, V> {
+ one: T,
+ another: V
+ };
+ pub struct S;
+ pub trait Foo<T = S, V = Wrapper<T, S>> {
+ fn bar(&self, other: &V);
+ }
+}
+
+struct S;
+impl m::Foo for S { $0 }"#,
+ r#"
+mod m {
+ pub struct Wrapper<T, V> {
+ one: T,
+ another: V
+ };
+ pub struct S;
+ pub trait Foo<T = S, V = Wrapper<T, S>> {
+ fn bar(&self, other: &V);
+ }
+}
+
+struct S;
+impl m::Foo for S {
+ fn bar(&self, other: &m::Wrapper<m::S, m::S>) {
+ ${0:todo!()}
+ }
+}"#,
+ );
+ }
+
+ #[test]
fn test_assoc_type_bounds_are_removed() {
check_assist(
add_missing_impl_members,
@@ -1346,8 +1557,8 @@ struct SomeStruct {
}
impl PartialEq for SomeStruct {
$0fn ne(&self, other: &Self) -> bool {
- !self.eq(other)
- }
+ !self.eq(other)
+ }
}
"#,
);
@@ -1511,8 +1722,245 @@ fn main() {
struct S;
impl Tr for S {
fn method() {
- ${0:todo!()}
+ ${0:todo!()}
+ }
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_add_missing_preserves_indentation() {
+ // in different modules
+ check_assist(
+ add_missing_impl_members,
+ r#"
+mod m {
+ pub trait Foo {
+ const CONST_MULTILINE: (
+ i32,
+ i32
+ );
+
+ fn foo(&self);
+ }
+}
+struct S;
+impl m::Foo for S { $0 }"#,
+ r#"
+mod m {
+ pub trait Foo {
+ const CONST_MULTILINE: (
+ i32,
+ i32
+ );
+
+ fn foo(&self);
+ }
+}
+struct S;
+impl m::Foo for S {
+ $0const CONST_MULTILINE: (
+ i32,
+ i32
+ );
+
+ fn foo(&self) {
+ todo!()
+ }
+}"#,
+ );
+ // in the same module
+ check_assist(
+ add_missing_impl_members,
+ r#"
+mod m {
+ trait Foo {
+ type Output;
+
+ const CONST: usize = 42;
+ const CONST_2: i32;
+ const CONST_MULTILINE: (
+ i32,
+ i32
+ );
+
+ fn foo(&self);
+ fn bar(&self);
+ fn baz(&self);
}
+
+ struct S;
+
+ impl Foo for S {
+ fn bar(&self) {}
+$0
+ }
+}"#,
+ r#"
+mod m {
+ trait Foo {
+ type Output;
+
+ const CONST: usize = 42;
+ const CONST_2: i32;
+ const CONST_MULTILINE: (
+ i32,
+ i32
+ );
+
+ fn foo(&self);
+ fn bar(&self);
+ fn baz(&self);
+ }
+
+ struct S;
+
+ impl Foo for S {
+ fn bar(&self) {}
+
+ $0type Output;
+
+ const CONST_2: i32;
+
+ const CONST_MULTILINE: (
+ i32,
+ i32
+ );
+
+ fn foo(&self) {
+ todo!()
+ }
+
+ fn baz(&self) {
+ todo!()
+ }
+
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn test_add_default_preserves_indentation() {
+ check_assist(
+ add_missing_default_members,
+ r#"
+mod m {
+ pub trait Foo {
+ type Output;
+
+ const CONST: usize = 42;
+ const CONST_2: i32;
+ const CONST_MULTILINE: = (
+ i32,
+ i32,
+ ) = (3, 14);
+
+ fn valid(some: u32) -> bool { false }
+ fn foo(some: u32) -> bool;
+ }
+}
+struct S;
+impl m::Foo for S { $0 }"#,
+ r#"
+mod m {
+ pub trait Foo {
+ type Output;
+
+ const CONST: usize = 42;
+ const CONST_2: i32;
+ const CONST_MULTILINE: = (
+ i32,
+ i32,
+ ) = (3, 14);
+
+ fn valid(some: u32) -> bool { false }
+ fn foo(some: u32) -> bool;
+ }
+}
+struct S;
+impl m::Foo for S {
+ $0const CONST: usize = 42;
+
+ const CONST_MULTILINE: = (
+ i32,
+ i32,
+ ) = (3, 14);
+
+ fn valid(some: u32) -> bool { false }
+}"#,
+ )
+ }
+
+ #[test]
+ fn nested_macro_should_not_cause_crash() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+macro_rules! ty { () => { i32 } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ () => {
+ fn method(&mut self, params: <ty!() as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait { define_method!(); }
+impl $0AnotherTrait for () {
+}
+"#,
+ r#"
+macro_rules! ty { () => { i32 } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ () => {
+ fn method(&mut self, params: <ty!() as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait { define_method!(); }
+impl AnotherTrait for () {
+ $0fn method(&mut self,params: <ty!()as SomeTrait>::Output) {
+ todo!()
+ }
+}
+"#,
+ );
+ }
+
+ // FIXME: `T` in `ty!(T)` should be replaced by `PathTransform`.
+ #[test]
+ fn paths_in_nested_macro_should_get_transformed() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+macro_rules! ty { ($me:ty) => { $me } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ ($t:ty) => {
+ fn method(&mut self, params: <ty!($t) as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait<T: SomeTrait> { define_method!(T); }
+impl $0AnotherTrait<i32> for () {
+}
+"#,
+ r#"
+macro_rules! ty { ($me:ty) => { $me } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ ($t:ty) => {
+ fn method(&mut self, params: <ty!($t) as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait<T: SomeTrait> { define_method!(T); }
+impl AnotherTrait<i32> for () {
+ $0fn method(&mut self,params: <ty!(T)as SomeTrait>::Output) {
+ todo!()
}
}
"#,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index 5d81e8cfe..7384390f2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -148,7 +148,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
return None;
}
- let variants_of_enums = vec![variants.clone(); len];
+ let variants_of_enums = vec![variants; len];
let missing_pats = variants_of_enums
.into_iter()
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
index 879c478ac..e5f0201bd 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_return_type.rs
@@ -22,7 +22,7 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
if ty.is_unit() {
return None;
}
- let ty = ty.display_source_code(ctx.db(), module.into()).ok()?;
+ let ty = ty.display_source_code(ctx.db(), module.into(), true).ok()?;
acc.add(
AssistId("add_return_type", AssistKind::RefactorRewrite),
@@ -34,8 +34,8 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
|builder| {
match builder_edit_pos {
InsertOrReplace::Insert(insert_pos, needs_whitespace) => {
- let preceeding_whitespace = if needs_whitespace { " " } else { "" };
- builder.insert(insert_pos, format!("{preceeding_whitespace}-> {ty} "))
+ let preceding_whitespace = if needs_whitespace { " " } else { "" };
+ builder.insert(insert_pos, format!("{preceding_whitespace}-> {ty} "))
}
InsertOrReplace::Replace(text_range) => {
builder.replace(text_range, format!("-> {ty}"))
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
index 698ad78cc..7acf2ea0a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
@@ -132,7 +132,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
acc.add_group(
&group_label,
AssistId("auto_import", AssistKind::QuickFix),
- format!("Import `{import_path}`"),
+ format!("Import `{}`", import_path.display(ctx.db())),
range,
|builder| {
let scope = match scope.clone() {
@@ -203,7 +203,7 @@ fn relevance_score(
// get the distance between the imported path and the current module
// (prefer items that are more local)
Some((item_module, current_module)) => {
- score -= module_distance_hueristic(db, current_module, &item_module) as i32;
+ score -= module_distance_heuristic(db, current_module, &item_module) as i32;
}
// could not find relevant modules, so just use the length of the path as an estimate
@@ -214,7 +214,7 @@ fn relevance_score(
}
/// A heuristic that gives a higher score to modules that are more separated.
-fn module_distance_hueristic(db: &dyn HirDatabase, current: &Module, item: &Module) -> usize {
+fn module_distance_heuristic(db: &dyn HirDatabase, current: &Module, item: &Module) -> usize {
// get the path starting from the item to the respective crate roots
let mut current_path = current.path_to_root(db);
let mut item_path = item.path_to_root(db);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
index db96ad330..1af52c592 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
@@ -160,7 +160,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>
};
// Verify this is `bool::then` that is being called.
let func = ctx.sema.resolve_method_call(&mcall)?;
- if func.name(ctx.sema.db).to_string() != "then" {
+ if func.name(ctx.sema.db).display(ctx.db()).to_string() != "then" {
return None;
}
let assoc = func.as_assoc_item(ctx.sema.db)?;
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
index 9e1d9a702..db96c8fe4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -119,7 +119,7 @@ pub(crate) fn convert_for_loop_with_for_each(
{
// We have either "for x in &col" and col implements a method called iter
// or "for x in &mut col" and col implements a method called iter_mut
- format_to!(buf, "{expr_behind_ref}.{method}()");
+ format_to!(buf, "{expr_behind_ref}.{}()", method.display(ctx.db()));
} else if let ast::Expr::RangeExpr(..) = iterable {
// range expressions need to be parenthesized for the syntax to be correct
format_to!(buf, "({iterable})");
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
index c82a3b530..5f7056b9c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
@@ -5,6 +5,88 @@ use syntax::T;
use crate::{AssistContext, AssistId, AssistKind, Assists};
+// Assist: convert_let_else_to_match
+//
+// Converts let-else statement to let statement and match expression.
+//
+// ```
+// fn main() {
+// let Ok(mut x) = f() else$0 { return };
+// }
+// ```
+// ->
+// ```
+// fn main() {
+// let mut x = match f() {
+// Ok(x) => x,
+// _ => return,
+// };
+// }
+// ```
+pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ // should focus on else token to trigger
+ let let_stmt = ctx
+ .find_token_syntax_at_offset(T![else])
+ .and_then(|it| it.parent()?.parent())
+ .or_else(|| ctx.find_token_syntax_at_offset(T![let])?.parent())?;
+ let let_stmt = LetStmt::cast(let_stmt)?;
+ let let_else_block = let_stmt.let_else()?.block_expr()?;
+ let let_init = let_stmt.initializer()?;
+ if let_stmt.ty().is_some() {
+ // don't support let with type annotation
+ return None;
+ }
+ let pat = let_stmt.pat()?;
+ let mut binders = Vec::new();
+ binders_in_pat(&mut binders, &pat, &ctx.sema)?;
+
+ let target = let_stmt.syntax().text_range();
+ acc.add(
+ AssistId("convert_let_else_to_match", AssistKind::RefactorRewrite),
+ "Convert let-else to let and match",
+ target,
+ |edit| {
+ let indent_level = let_stmt.indent_level().0 as usize;
+ let indent = " ".repeat(indent_level);
+ let indent1 = " ".repeat(indent_level + 1);
+
+ let binders_str = binders_to_str(&binders, false);
+ let binders_str_mut = binders_to_str(&binders, true);
+
+ let init_expr = let_init.syntax().text();
+ let mut pat_no_mut = pat.syntax().text().to_string();
+ // remove the mut from the pattern
+ for (b, ismut) in binders.iter() {
+ if *ismut {
+ pat_no_mut = pat_no_mut.replace(&format!("mut {b}"), &b.to_string());
+ }
+ }
+
+ let only_expr = let_else_block.statements().next().is_none();
+ let branch2 = match &let_else_block.tail_expr() {
+ Some(tail) if only_expr => format!("{tail},"),
+ _ => let_else_block.syntax().text().to_string(),
+ };
+ let replace = if binders.is_empty() {
+ format!(
+ "match {init_expr} {{
+{indent1}{pat_no_mut} => {binders_str}
+{indent1}_ => {branch2}
+{indent}}}"
+ )
+ } else {
+ format!(
+ "let {binders_str_mut} = match {init_expr} {{
+{indent1}{pat_no_mut} => {binders_str},
+{indent1}_ => {branch2}
+{indent}}};"
+ )
+ };
+ edit.replace(target, replace);
+ },
+ )
+}
+
/// Gets a list of binders in a pattern, and whether they are mut.
fn binders_in_pat(
acc: &mut Vec<(Name, bool)>,
@@ -97,85 +179,6 @@ fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String {
}
}
-// Assist: convert_let_else_to_match
-//
-// Converts let-else statement to let statement and match expression.
-//
-// ```
-// fn main() {
-// let Ok(mut x) = f() else$0 { return };
-// }
-// ```
-// ->
-// ```
-// fn main() {
-// let mut x = match f() {
-// Ok(x) => x,
-// _ => return,
-// };
-// }
-// ```
-pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- // should focus on else token to trigger
- let else_token = ctx.find_token_syntax_at_offset(T![else])?;
- let let_stmt = LetStmt::cast(else_token.parent()?.parent()?)?;
- let let_else_block = let_stmt.let_else()?.block_expr()?;
- let let_init = let_stmt.initializer()?;
- if let_stmt.ty().is_some() {
- // don't support let with type annotation
- return None;
- }
- let pat = let_stmt.pat()?;
- let mut binders = Vec::new();
- binders_in_pat(&mut binders, &pat, &ctx.sema)?;
-
- let target = let_stmt.syntax().text_range();
- acc.add(
- AssistId("convert_let_else_to_match", AssistKind::RefactorRewrite),
- "Convert let-else to let and match",
- target,
- |edit| {
- let indent_level = let_stmt.indent_level().0 as usize;
- let indent = " ".repeat(indent_level);
- let indent1 = " ".repeat(indent_level + 1);
-
- let binders_str = binders_to_str(&binders, false);
- let binders_str_mut = binders_to_str(&binders, true);
-
- let init_expr = let_init.syntax().text();
- let mut pat_no_mut = pat.syntax().text().to_string();
- // remove the mut from the pattern
- for (b, ismut) in binders.iter() {
- if *ismut {
- pat_no_mut = pat_no_mut.replace(&format!("mut {b}"), &b.to_string());
- }
- }
-
- let only_expr = let_else_block.statements().next().is_none();
- let branch2 = match &let_else_block.tail_expr() {
- Some(tail) if only_expr => format!("{tail},"),
- _ => let_else_block.syntax().text().to_string(),
- };
- let replace = if binders.is_empty() {
- format!(
- "match {init_expr} {{
-{indent1}{pat_no_mut} => {binders_str}
-{indent1}_ => {branch2}
-{indent}}}"
- )
- } else {
- format!(
- "let {binders_str_mut} = match {init_expr} {{
-{indent1}{pat_no_mut} => {binders_str},
-{indent1}_ => {branch2}
-{indent}}};"
- )
- };
- edit.replace(target, replace);
- },
- )
-}
-
#[cfg(test)]
mod tests {
use super::*;
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
index 7f2c01772..fc6236a17 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -16,7 +16,7 @@ use crate::{
// ```
// # //- minicore: option
// fn foo(opt: Option<()>) {
-// let val = $0match opt {
+// let val$0 = match opt {
// Some(it) => it,
// None => return,
// };
@@ -30,7 +30,10 @@ use crate::{
// ```
pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let let_stmt: ast::LetStmt = ctx.find_node_at_offset()?;
- let binding = let_stmt.pat()?;
+ let pat = let_stmt.pat()?;
+ if ctx.offset() > pat.syntax().text_range().end() {
+ return None;
+ }
let Some(ast::Expr::MatchExpr(initializer)) = let_stmt.initializer() else { return None };
let initializer_expr = initializer.expr()?;
@@ -56,7 +59,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'
let_stmt.syntax().text_range(),
|builder| {
let extracting_arm_pat =
- rename_variable(&extracting_arm_pat, &extracted_variable_positions, binding);
+ rename_variable(&extracting_arm_pat, &extracted_variable_positions, pat);
builder.replace(
let_stmt.syntax().text_range(),
format!("let {extracting_arm_pat} = {initializer_expr} else {diverging_arm_expr};"),
@@ -161,7 +164,7 @@ mod tests {
r#"
//- minicore: option
fn foo(opt: Option<()>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it,
None => (),
};
@@ -211,7 +214,7 @@ fn foo(opt: Option<Foo>) -> Result<u32, ()> {
r#"
//- minicore: option
fn foo(opt: Option<i32>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it + 1,
None => return,
};
@@ -224,7 +227,7 @@ fn foo(opt: Option<i32>) {
r#"
//- minicore: option
fn foo(opt: Option<()>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => {
let _ = 1 + 1;
it
@@ -244,7 +247,7 @@ fn foo(opt: Option<()>) {
r#"
//- minicore: option
fn foo(opt: Option<()>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) if 2 > 1 => it,
None => return,
};
@@ -260,7 +263,7 @@ fn foo(opt: Option<()>) {
r#"
//- minicore: option
fn foo(opt: Option<()>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it,
None => return,
};
@@ -281,7 +284,7 @@ fn foo(opt: Option<()>) {
r#"
//- minicore: option
fn foo(opt: Option<()>) {
- let ref mut val = $0match opt {
+ let ref mut val$0 = match opt {
Some(it) => it,
None => return,
};
@@ -302,7 +305,7 @@ fn foo(opt: Option<()>) {
r#"
//- minicore: option, result
fn foo(opt: Option<Result<()>>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(Ok(it)) => it,
_ => return,
};
@@ -324,7 +327,7 @@ fn foo(opt: Option<Result<()>>) {
//- minicore: option
fn foo(opt: Option<()>) {
loop {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it,
None => break,
};
@@ -346,7 +349,7 @@ fn foo(opt: Option<()>) {
//- minicore: option
fn foo(opt: Option<()>) {
loop {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it,
None => continue,
};
@@ -370,7 +373,7 @@ fn panic() -> ! {}
fn foo(opt: Option<()>) {
loop {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it,
None => panic(),
};
@@ -401,7 +404,7 @@ struct Point {
}
fn foo(opt: Option<Point>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(Point { x: 0, y }) => y,
_ => return,
};
@@ -427,7 +430,7 @@ fn foo(opt: Option<Point>) {
r#"
//- minicore: option
fn foo(opt: Option<i32>) -> Option<i32> {
- let val = $0match opt {
+ let val$0 = match opt {
it @ Some(42) => it,
_ => return None,
};
@@ -450,7 +453,7 @@ fn foo(opt: Option<i32>) -> Option<i32> {
r#"
//- minicore: option
fn f() {
- let (x, y) = $0match Some((0, 1)) {
+ let (x, y)$0 = match Some((0, 1)) {
Some(it) => it,
None => return,
};
@@ -471,7 +474,7 @@ fn f() {
r#"
//- minicore: option
fn f() {
- let x = $0match Some(()) {
+ let x$0 = match Some(()) {
Some(it) => it,
None => {//comment
println!("nope");
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
index 9dc1da246..fe1cb6fce 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
@@ -1,9 +1,9 @@
use either::Either;
-use ide_db::defs::Definition;
+use ide_db::{defs::Definition, search::FileReference};
use itertools::Itertools;
use syntax::{
ast::{self, AstNode, HasGenericParams, HasVisibility},
- match_ast, SyntaxKind, SyntaxNode,
+ match_ast, SyntaxKind,
};
use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists};
@@ -52,7 +52,11 @@ pub(crate) fn convert_named_struct_to_tuple_struct(
acc: &mut Assists,
ctx: &AssistContext<'_>,
) -> Option<()> {
- let strukt = ctx.find_node_at_offset::<Either<ast::Struct, ast::Variant>>()?;
+ // XXX: We don't currently provide this assist for struct definitions inside macros, but if we
+ // are to lift this limitation, don't forget to make `edit_struct_def()` consider macro files
+ // too.
+ let name = ctx.find_node_at_offset::<ast::Name>()?;
+ let strukt = name.syntax().parent().and_then(<Either<ast::Struct, ast::Variant>>::cast)?;
let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
let record_fields = match field_list {
ast::FieldList::RecordFieldList(it) => it,
@@ -62,12 +66,11 @@ pub(crate) fn convert_named_struct_to_tuple_struct(
Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
};
- let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
acc.add(
AssistId("convert_named_struct_to_tuple_struct", AssistKind::RefactorRewrite),
"Convert to tuple struct",
- target,
+ strukt.syntax().text_range(),
|edit| {
edit_field_references(ctx, edit, record_fields.fields());
edit_struct_references(ctx, edit, strukt_def);
@@ -82,6 +85,8 @@ fn edit_struct_def(
strukt: &Either<ast::Struct, ast::Variant>,
record_fields: ast::RecordFieldList,
) {
+ // Note that we don't need to consider macro files in this function because this this is
+ // currently not triggered for struct definitions inside macro calls.
let tuple_fields = record_fields
.fields()
.filter_map(|f| Some(ast::make::tuple_field(f.visibility(), f.ty()?)));
@@ -137,50 +142,72 @@ fn edit_struct_references(
};
let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
- let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> {
- match_ast! {
- match node {
- ast::RecordPat(record_struct_pat) => {
- edit.replace(
- record_struct_pat.syntax().text_range(),
- ast::make::tuple_struct_pat(
- record_struct_pat.path()?,
- record_struct_pat
- .record_pat_field_list()?
- .fields()
- .filter_map(|pat| pat.pat())
- )
- .to_string()
- );
- },
- ast::RecordExpr(record_expr) => {
- let path = record_expr.path()?;
- let args = record_expr
- .record_expr_field_list()?
- .fields()
- .filter_map(|f| f.expr())
- .join(", ");
-
- edit.replace(record_expr.syntax().text_range(), format!("{path}({args})"));
- },
- _ => return None,
- }
- }
- Some(())
- };
-
for (file_id, refs) in usages {
edit.edit_file(file_id);
for r in refs {
- for node in r.name.syntax().ancestors() {
- if edit_node(edit, node).is_some() {
- break;
- }
- }
+ process_struct_name_reference(ctx, r, edit);
}
}
}
+fn process_struct_name_reference(
+ ctx: &AssistContext<'_>,
+ r: FileReference,
+ edit: &mut SourceChangeBuilder,
+) -> Option<()> {
+ // First check if it's the last semgnet of a path that directly belongs to a record
+ // expression/pattern.
+ let name_ref = r.name.as_name_ref()?;
+ let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?;
+ // A `PathSegment` always belongs to a `Path`, so there's at least one `Path` at this point.
+ let full_path =
+ path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last().unwrap();
+
+ if full_path.segment().unwrap().name_ref()? != *name_ref {
+ // `name_ref` isn't the last segment of the path, so `full_path` doesn't point to the
+ // struct we want to edit.
+ return None;
+ }
+
+ let parent = full_path.syntax().parent()?;
+ match_ast! {
+ match parent {
+ ast::RecordPat(record_struct_pat) => {
+ // When we failed to get the original range for the whole struct expression node,
+ // we can't provide any reasonable edit. Leave it untouched.
+ let file_range = ctx.sema.original_range_opt(record_struct_pat.syntax())?;
+ edit.replace(
+ file_range.range,
+ ast::make::tuple_struct_pat(
+ record_struct_pat.path()?,
+ record_struct_pat
+ .record_pat_field_list()?
+ .fields()
+ .filter_map(|pat| pat.pat())
+ )
+ .to_string()
+ );
+ },
+ ast::RecordExpr(record_expr) => {
+ // When we failed to get the original range for the whole struct pattern node,
+ // we can't provide any reasonable edit. Leave it untouched.
+ let file_range = ctx.sema.original_range_opt(record_expr.syntax())?;
+ let path = record_expr.path()?;
+ let args = record_expr
+ .record_expr_field_list()?
+ .fields()
+ .filter_map(|f| f.expr())
+ .join(", ");
+
+ edit.replace(file_range.range, format!("{path}({args})"));
+ },
+ _ => {}
+ }
+ }
+
+ Some(())
+}
+
fn edit_field_references(
ctx: &AssistContext<'_>,
edit: &mut SourceChangeBuilder,
@@ -199,7 +226,7 @@ fn edit_field_references(
if let Some(name_ref) = r.name.as_name_ref() {
// Only edit the field reference if it's part of a `.field` access
if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() {
- edit.replace(name_ref.syntax().text_range(), index.to_string());
+ edit.replace(r.range, index.to_string());
}
}
}
@@ -816,4 +843,139 @@ fn f() {
"#,
);
}
+
+ #[test]
+ fn field_access_inside_macro_call() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+struct $0Struct {
+ inner: i32,
+}
+
+macro_rules! id {
+ ($e:expr) => { $e }
+}
+
+fn test(c: Struct) {
+ id!(c.inner);
+}
+"#,
+ r#"
+struct Struct(i32);
+
+macro_rules! id {
+ ($e:expr) => { $e }
+}
+
+fn test(c: Struct) {
+ id!(c.0);
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn struct_usage_inside_macro_call() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+macro_rules! id {
+ ($($t:tt)*) => { $($t)* }
+}
+
+struct $0Struct {
+ inner: i32,
+}
+
+fn test() {
+ id! {
+ let s = Struct {
+ inner: 42,
+ };
+ let Struct { inner: value } = s;
+ let Struct { inner } = s;
+ }
+}
+"#,
+ r#"
+macro_rules! id {
+ ($($t:tt)*) => { $($t)* }
+}
+
+struct Struct(i32);
+
+fn test() {
+ id! {
+ let s = Struct(42);
+ let Struct(value) = s;
+ let Struct(inner) = s;
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn struct_name_ref_may_not_be_part_of_struct_expr_or_struct_pat() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+struct $0Struct {
+ inner: i32,
+}
+struct Outer<T> {
+ value: T,
+}
+fn foo<T>() -> T { loop {} }
+
+fn test() {
+ Outer {
+ value: foo::<Struct>();
+ }
+}
+
+trait HasAssoc {
+ type Assoc;
+ fn test();
+}
+impl HasAssoc for Struct {
+ type Assoc = Outer<i32>;
+ fn test() {
+ let a = Self::Assoc {
+ value: 42,
+ };
+ let Self::Assoc { value } = a;
+ }
+}
+"#,
+ r#"
+struct Struct(i32);
+struct Outer<T> {
+ value: T,
+}
+fn foo<T>() -> T { loop {} }
+
+fn test() {
+ Outer {
+ value: foo::<Struct>();
+ }
+}
+
+trait HasAssoc {
+ type Assoc;
+ fn test();
+}
+impl HasAssoc for Struct {
+ type Assoc = Outer<i32>;
+ fn test() {
+ let a = Self::Assoc {
+ value: 42,
+ };
+ let Self::Assoc { value } = a;
+ }
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs
new file mode 100644
index 000000000..399f87c8f
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs
@@ -0,0 +1,209 @@
+use ide_db::assists::{AssistId, AssistKind};
+use syntax::ast::{self, HasGenericParams, HasName};
+use syntax::{AstNode, SyntaxKind};
+
+use crate::assist_context::{AssistContext, Assists};
+
+// Assist: convert_nested_function_to_closure
+//
+// Converts a function that is defined within the body of another function into a closure.
+//
+// ```
+// fn main() {
+// fn fo$0o(label: &str, number: u64) {
+// println!("{}: {}", label, number);
+// }
+//
+// foo("Bar", 100);
+// }
+// ```
+// ->
+// ```
+// fn main() {
+// let foo = |label: &str, number: u64| {
+// println!("{}: {}", label, number);
+// };
+//
+// foo("Bar", 100);
+// }
+// ```
+pub(crate) fn convert_nested_function_to_closure(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_>,
+) -> Option<()> {
+ let name = ctx.find_node_at_offset::<ast::Name>()?;
+ let function = name.syntax().parent().and_then(ast::Fn::cast)?;
+
+ if !is_nested_function(&function) || is_generic(&function) || has_modifiers(&function) {
+ return None;
+ }
+
+ let target = function.syntax().text_range();
+ let body = function.body()?;
+ let name = function.name()?;
+ let param_list = function.param_list()?;
+
+ acc.add(
+ AssistId("convert_nested_function_to_closure", AssistKind::RefactorRewrite),
+ "Convert nested function to closure",
+ target,
+ |edit| {
+ let params = &param_list.syntax().text().to_string();
+ let params = params.strip_prefix("(").unwrap_or(params);
+ let params = params.strip_suffix(")").unwrap_or(params);
+
+ let mut body = body.to_string();
+ if !has_semicolon(&function) {
+ body.push(';');
+ }
+ edit.replace(target, format!("let {name} = |{params}| {body}"));
+ },
+ )
+}
+
+/// Returns whether the given function is nested within the body of another function.
+fn is_nested_function(function: &ast::Fn) -> bool {
+ function.syntax().ancestors().skip(1).find_map(ast::Item::cast).map_or(false, |it| {
+ matches!(it, ast::Item::Fn(_) | ast::Item::Static(_) | ast::Item::Const(_))
+ })
+}
+
+/// Returns whether the given nested function has generic parameters.
+fn is_generic(function: &ast::Fn) -> bool {
+ function.generic_param_list().is_some()
+}
+
+/// Returns whether the given nested function has any modifiers:
+///
+/// - `async`,
+/// - `const` or
+/// - `unsafe`
+fn has_modifiers(function: &ast::Fn) -> bool {
+ function.async_token().is_some()
+ || function.const_token().is_some()
+ || function.unsafe_token().is_some()
+}
+
+/// Returns whether the given nested function has a trailing semicolon.
+fn has_semicolon(function: &ast::Fn) -> bool {
+ function
+ .syntax()
+ .next_sibling_or_token()
+ .map(|t| t.kind() == SyntaxKind::SEMICOLON)
+ .unwrap_or(false)
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ use super::convert_nested_function_to_closure;
+
+ #[test]
+ fn convert_nested_function_to_closure_works() {
+ check_assist(
+ convert_nested_function_to_closure,
+ r#"
+fn main() {
+ fn $0foo(a: u64, b: u64) -> u64 {
+ 2 * (a + b)
+ }
+
+ _ = foo(3, 4);
+}
+ "#,
+ r#"
+fn main() {
+ let foo = |a: u64, b: u64| {
+ 2 * (a + b)
+ };
+
+ _ = foo(3, 4);
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn convert_nested_function_to_closure_works_with_existing_semicolon() {
+ check_assist(
+ convert_nested_function_to_closure,
+ r#"
+fn main() {
+ fn foo$0(a: u64, b: u64) -> u64 {
+ 2 * (a + b)
+ };
+
+ _ = foo(3, 4);
+}
+ "#,
+ r#"
+fn main() {
+ let foo = |a: u64, b: u64| {
+ 2 * (a + b)
+ };
+
+ _ = foo(3, 4);
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn convert_nested_function_to_closure_is_not_suggested_on_top_level_function() {
+ check_assist_not_applicable(
+ convert_nested_function_to_closure,
+ r#"
+fn ma$0in() {}
+ "#,
+ );
+ }
+
+ #[test]
+ fn convert_nested_function_to_closure_is_not_suggested_when_cursor_off_name() {
+ check_assist_not_applicable(
+ convert_nested_function_to_closure,
+ r#"
+fn main() {
+ fn foo(a: u64, $0b: u64) -> u64 {
+ 2 * (a + b)
+ }
+
+ _ = foo(3, 4);
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn convert_nested_function_to_closure_is_not_suggested_if_function_has_generic_params() {
+ check_assist_not_applicable(
+ convert_nested_function_to_closure,
+ r#"
+fn main() {
+ fn fo$0o<S: Into<String>>(s: S) -> String {
+ s.into()
+ }
+
+ _ = foo("hello");
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn convert_nested_function_to_closure_is_not_suggested_if_function_has_modifier() {
+ check_assist_not_applicable(
+ convert_nested_function_to_closure,
+ r#"
+fn main() {
+ const fn fo$0o(s: String) -> String {
+ s
+ }
+
+ _ = foo("hello");
+}
+ "#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
index b97be34c5..dcb96ab8a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
@@ -504,7 +504,7 @@ fn main() {
}
#[test]
- fn ignore_statements_aftert_if() {
+ fn ignore_statements_after_if() {
check_assist_not_applicable(
convert_to_guarded_return,
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index 772e032fb..017853a4a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -50,7 +50,8 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
acc: &mut Assists,
ctx: &AssistContext<'_>,
) -> Option<()> {
- let strukt = ctx.find_node_at_offset::<Either<ast::Struct, ast::Variant>>()?;
+ let name = ctx.find_node_at_offset::<ast::Name>()?;
+ let strukt = name.syntax().parent().and_then(<Either<ast::Struct, ast::Variant>>::cast)?;
let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
let tuple_fields = match field_list {
ast::FieldList::TupleFieldList(it) => it,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
index 31c2ce7c1..ea71d165e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
@@ -91,7 +91,7 @@ fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleDat
return None;
}
- let ty = ctx.sema.type_of_pat(&ident_pat.clone().into())?.adjusted();
+ let ty = ctx.sema.type_of_binding_in_pat(&ident_pat)?;
let ref_type = if ty.is_mutable_reference() {
Some(RefType::Mutable)
} else if ty.is_reference() {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
index 87f5018fb..5c435dd9c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_glob_import.rs
@@ -65,7 +65,7 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs);
let expanded = make::use_tree_list(names_to_import.iter().map(|n| {
- let path = make::ext::ident_path(&n.to_string());
+ let path = make::ext::ident_path(&n.display(ctx.db()).to_string());
make::use_tree(path, None, None, false)
}))
.clone_for_update();
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index 0b90c9ba3..2a67909e6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -70,6 +70,11 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
}
let node = ctx.covering_element();
+ if matches!(node.kind(), T!['{'] | T!['}'] | T!['('] | T![')'] | T!['['] | T![']']) {
+ cov_mark::hit!(extract_function_in_braces_is_not_applicable);
+ return None;
+ }
+
if node.kind() == COMMENT {
cov_mark::hit!(extract_function_in_comment_is_not_applicable);
return None;
@@ -178,7 +183,9 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef {
let mut names_in_scope = vec![];
- semantics_scope.process_all_names(&mut |name, _| names_in_scope.push(name.to_string()));
+ semantics_scope.process_all_names(&mut |name, _| {
+ names_in_scope.push(name.display(semantics_scope.db.upcast()).to_string())
+ });
let default_name = "fun_name";
@@ -369,7 +376,7 @@ struct OutlivedLocal {
/// Container of local variable usages
///
-/// Semanticall same as `UsageSearchResult`, but provides more convenient interface
+/// Semantically same as `UsageSearchResult`, but provides more convenient interface
struct LocalUsages(ide_db::search::UsageSearchResult);
impl LocalUsages {
@@ -438,7 +445,7 @@ impl Param {
}
fn to_param(&self, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Param {
- let var = self.var.name(ctx.db()).to_string();
+ let var = self.var.name(ctx.db()).display(ctx.db()).to_string();
let var_name = make::name(&var);
let pat = match self.kind() {
ParamKind::MutValue => make::ident_pat(false, true, var_name),
@@ -468,7 +475,8 @@ impl TryKind {
let name = adt.name(ctx.db());
// FIXME: use lang items to determine if it is std type or user defined
// E.g. if user happens to define type named `Option`, we would have false positive
- match name.to_string().as_str() {
+ let name = &name.display(ctx.db()).to_string();
+ match name.as_str() {
"Option" => Some(TryKind::Option),
"Result" => Some(TryKind::Result { ty }),
_ => None,
@@ -702,7 +710,7 @@ impl FunctionBody {
) -> (FxIndexSet<Local>, Option<ast::SelfParam>) {
let mut self_param = None;
let mut res = FxIndexSet::default();
- let mut cb = |name_ref: Option<_>| {
+ let mut add_name_if_local = |name_ref: Option<_>| {
let local_ref =
match name_ref.and_then(|name_ref| NameRefClass::classify(sema, &name_ref)) {
Some(
@@ -726,21 +734,24 @@ impl FunctionBody {
};
self.walk_expr(&mut |expr| match expr {
ast::Expr::PathExpr(path_expr) => {
- cb(path_expr.path().and_then(|it| it.as_single_name_ref()))
+ add_name_if_local(path_expr.path().and_then(|it| it.as_single_name_ref()))
}
ast::Expr::ClosureExpr(closure_expr) => {
if let Some(body) = closure_expr.body() {
- body.syntax().descendants().map(ast::NameRef::cast).for_each(|it| cb(it));
+ body.syntax()
+ .descendants()
+ .map(ast::NameRef::cast)
+ .for_each(&mut add_name_if_local);
}
}
ast::Expr::MacroExpr(expr) => {
if let Some(tt) = expr.macro_call().and_then(|call| call.token_tree()) {
tt.syntax()
- .children_with_tokens()
- .flat_map(SyntaxElement::into_token)
- .filter(|it| it.kind() == SyntaxKind::IDENT)
+ .descendants_with_tokens()
+ .filter_map(SyntaxElement::into_token)
+ .filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self]))
.flat_map(|t| sema.descend_into_macros(t))
- .for_each(|t| cb(t.parent().and_then(ast::NameRef::cast)));
+ .for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast)));
}
}
_ => (),
@@ -1286,8 +1297,8 @@ fn find_non_trait_impl(trait_impl: &SyntaxNode) -> Option<ast::Impl> {
let as_impl = ast::Impl::cast(trait_impl.clone())?;
let impl_type = Some(impl_type_name(&as_impl)?);
- let sibblings = trait_impl.parent()?.children();
- sibblings
+ let siblings = trait_impl.parent()?.children();
+ siblings
.filter_map(ast::Impl::cast)
.find(|s| impl_type_name(s) == impl_type && !is_trait_impl(s))
}
@@ -1333,14 +1344,15 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> St
[var] => {
let modifier = mut_modifier(var);
let name = var.local.name(ctx.db());
- format_to!(buf, "let {modifier}{name} = ")
+ format_to!(buf, "let {modifier}{} = ", name.display(ctx.db()))
}
vars => {
buf.push_str("let (");
let bindings = vars.iter().format_with(", ", |local, f| {
let modifier = mut_modifier(local);
let name = local.local.name(ctx.db());
- f(&format_args!("{modifier}{name}"))
+ f(&format_args!("{modifier}{}", name.display(ctx.db())))?;
+ Ok(())
});
format_to!(buf, "{bindings}");
buf.push_str(") = ");
@@ -1479,7 +1491,7 @@ impl FlowHandler {
}
fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local) -> ast::Expr {
- let name = var.name(ctx.db()).to_string();
+ let name = var.name(ctx.db()).display(ctx.db()).to_string();
make::expr_path(make::ext::ident_path(&name))
}
@@ -1879,7 +1891,7 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr
}
fn format_type(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> String {
- ty.display_source_code(ctx.db(), module.into()).ok().unwrap_or_else(|| "_".to_string())
+ ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_string())
}
fn make_ty(ty: &hir::Type, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {
@@ -4340,6 +4352,82 @@ fn $0fun_name(n: i32) -> i32 {
}
#[test]
+ fn param_usage_in_macro_with_nested_tt() {
+ check_assist(
+ extract_function,
+ r#"
+macro_rules! m {
+ ($val:expr) => { $val };
+}
+
+fn foo() {
+ let n = 1;
+ let t = 1;
+ $0let k = n * m!((n) + { t });$0
+ let m = k + 1;
+}
+"#,
+ r#"
+macro_rules! m {
+ ($val:expr) => { $val };
+}
+
+fn foo() {
+ let n = 1;
+ let t = 1;
+ let k = fun_name(n, t);
+ let m = k + 1;
+}
+
+fn $0fun_name(n: i32, t: i32) -> i32 {
+ let k = n * m!((n) + { t });
+ k
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn param_usage_in_macro_with_nested_tt_2() {
+ check_assist(
+ extract_function,
+ r#"
+macro_rules! m {
+ ($val:expr) => { $val };
+}
+
+struct S(i32);
+impl S {
+ fn foo(&self) {
+ let n = 1;
+ $0let k = n * m!((n) + { self.0 });$0
+ let m = k + 1;
+ }
+}
+"#,
+ r#"
+macro_rules! m {
+ ($val:expr) => { $val };
+}
+
+struct S(i32);
+impl S {
+ fn foo(&self) {
+ let n = 1;
+ let k = self.fun_name(n);
+ let m = k + 1;
+ }
+
+ fn $0fun_name(&self, n: i32) -> i32 {
+ let k = n * m!((n) + { self.0 });
+ k
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
fn extract_with_await() {
check_assist(
extract_function,
@@ -4640,6 +4728,7 @@ const fn $0fun_name() {
check_assist(
extract_function,
r#"
+//- minicore: iterator
fn foo() {
let mut x = 5;
for _ in 0..10 {
@@ -4663,6 +4752,7 @@ fn $0fun_name(x: &mut i32) {
check_assist(
extract_function,
r#"
+//- minicore: iterator
fn foo() {
for _ in 0..10 {
let mut x = 5;
@@ -4686,6 +4776,7 @@ fn $0fun_name(mut x: i32) {
check_assist(
extract_function,
r#"
+//- minicore: iterator
fn foo() {
loop {
let mut x = 5;
@@ -5388,6 +5479,30 @@ fn $0fun_name<T: Debug>(i: T) {
}
#[test]
+ fn dont_emit_type_with_hidden_lifetime_parameter() {
+ // FIXME: We should emit a `<T: Debug>` generic argument for the generated function
+ check_assist(
+ extract_function,
+ r#"
+struct Struct<'a, T>(&'a T);
+fn func<T: Debug>(i: Struct<'_, T>) {
+ $0foo(i);$0
+}
+"#,
+ r#"
+struct Struct<'a, T>(&'a T);
+fn func<T: Debug>(i: Struct<'_, T>) {
+ fun_name(i);
+}
+
+fn $0fun_name(i: Struct<'_, T>) {
+ foo(i);
+}
+"#,
+ );
+ }
+
+ #[test]
fn preserve_generics_from_body() {
check_assist(
extract_function,
@@ -5800,4 +5915,40 @@ fn $0fun_name() -> ControlFlow<()> {
"#,
);
}
+
+ #[test]
+ fn in_left_curly_is_not_applicable() {
+ cov_mark::check!(extract_function_in_braces_is_not_applicable);
+ check_assist_not_applicable(extract_function, r"fn foo() { $0}$0");
+ }
+
+ #[test]
+ fn in_right_curly_is_not_applicable() {
+ cov_mark::check!(extract_function_in_braces_is_not_applicable);
+ check_assist_not_applicable(extract_function, r"fn foo() $0{$0 }");
+ }
+
+ #[test]
+ fn in_left_paren_is_not_applicable() {
+ cov_mark::check!(extract_function_in_braces_is_not_applicable);
+ check_assist_not_applicable(extract_function, r"fn foo( $0)$0 { }");
+ }
+
+ #[test]
+ fn in_right_paren_is_not_applicable() {
+ cov_mark::check!(extract_function_in_braces_is_not_applicable);
+ check_assist_not_applicable(extract_function, r"fn foo $0($0 ) { }");
+ }
+
+ #[test]
+ fn in_left_brack_is_not_applicable() {
+ cov_mark::check!(extract_function_in_braces_is_not_applicable);
+ check_assist_not_applicable(extract_function, r"fn foo(arr: &mut [i32$0]$0) {}");
+ }
+
+ #[test]
+ fn in_right_brack_is_not_applicable() {
+ cov_mark::check!(extract_function_in_braces_is_not_applicable);
+ check_assist_not_applicable(extract_function, r"fn foo(arr: &mut $0[$0i32]) {}");
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
index 0fa7bd558..de37f5f13 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs
@@ -357,7 +357,7 @@ impl Module {
fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>) {
let (mut replacements, record_field_parents, impls) =
- get_replacements_for_visibilty_change(&mut self.body_items, false);
+ get_replacements_for_visibility_change(&mut self.body_items, false);
let mut impl_items: Vec<ast::Item> = impls
.into_iter()
@@ -366,7 +366,7 @@ impl Module {
.collect();
let (mut impl_item_replacements, _, _) =
- get_replacements_for_visibilty_change(&mut impl_items, true);
+ get_replacements_for_visibility_change(&mut impl_items, true);
replacements.append(&mut impl_item_replacements);
@@ -824,7 +824,7 @@ fn does_source_exists_outside_sel_in_same_mod(
source_exists_outside_sel_in_same_mod
}
-fn get_replacements_for_visibilty_change(
+fn get_replacements_for_visibility_change(
items: &mut [ast::Item],
is_clone_for_updated: bool,
) -> (
@@ -904,7 +904,7 @@ fn compare_hir_and_ast_module(
) -> Option<()> {
let hir_mod_name = hir_module.name(ctx.db())?;
let ast_mod_name = ast_module.name()?;
- if hir_mod_name.to_string() != ast_mod_name.to_string() {
+ if hir_mod_name.display(ctx.db()).to_string() != ast_mod_name.to_string() {
return None;
}
@@ -1236,7 +1236,8 @@ mod modname {
}
#[test]
- fn test_extract_module_for_correspoding_adt_of_impl_present_in_same_mod_but_not_in_selection() {
+ fn test_extract_module_for_corresponding_adt_of_impl_present_in_same_mod_but_not_in_selection()
+ {
check_assist(
extract_module,
r"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
index 49debafe1..e4f64ccc7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -158,7 +158,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
),
_ => false,
})
- .any(|(name, _)| name.to_string() == variant_name.to_string())
+ .any(|(name, _)| name.display(db).to_string() == variant_name.to_string())
}
fn extract_generic_params(
@@ -1006,7 +1006,7 @@ enum X<'a, 'b, 'x> {
}
#[test]
- fn test_extract_struct_with_liftime_type_const() {
+ fn test_extract_struct_with_lifetime_type_const() {
check_assist(
extract_struct_from_enum_variant,
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
index b310c2db9..b6e7d6209 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs
@@ -1,6 +1,9 @@
use either::Either;
use ide_db::syntax_helpers::node_ext::walk_ty;
-use syntax::ast::{self, edit::IndentLevel, make, AstNode, HasGenericParams, HasName};
+use syntax::{
+ ast::{self, edit::IndentLevel, make, AstNode, HasGenericParams, HasName},
+ ted,
+};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -34,14 +37,16 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|| item.syntax(),
|impl_| impl_.as_ref().either(AstNode::syntax, AstNode::syntax),
);
- let insert_pos = node.text_range().start();
let target = ty.syntax().text_range();
acc.add(
AssistId("extract_type_alias", AssistKind::RefactorExtract),
"Extract type as type alias",
target,
- |builder| {
+ |edit| {
+ let node = edit.make_syntax_mut(node.clone());
+ let target_ty = edit.make_mut(ty.clone());
+
let mut known_generics = match item.generic_param_list() {
Some(it) => it.generic_params().collect(),
None => Vec::new(),
@@ -56,27 +61,29 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let generic_params =
generics.map(|it| make::generic_param_list(it.into_iter().cloned()));
+ // Replace original type with the alias
let ty_args = generic_params
.as_ref()
.map_or(String::new(), |it| it.to_generic_args().to_string());
- let replacement = format!("Type{ty_args}");
- builder.replace(target, replacement);
-
- let indent = IndentLevel::from_node(node);
- let generic_params = generic_params.map_or(String::new(), |it| it.to_string());
- match ctx.config.snippet_cap {
- Some(cap) => {
- builder.insert_snippet(
- cap,
- insert_pos,
- format!("type $0Type{generic_params} = {ty};\n\n{indent}"),
- );
- }
- None => {
- builder.insert(
- insert_pos,
- format!("type Type{generic_params} = {ty};\n\n{indent}"),
- );
+ // FIXME: replace with a `ast::make` constructor
+ let new_ty = make::ty(&format!("Type{ty_args}")).clone_for_update();
+ ted::replace(target_ty.syntax(), new_ty.syntax());
+
+ // Insert new alias
+ let indent = IndentLevel::from_node(&node);
+ let ty_alias = make::ty_alias("Type", generic_params, None, None, Some((ty, None)))
+ .clone_for_update();
+ ted::insert_all(
+ ted::Position::before(node),
+ vec![
+ ty_alias.syntax().clone().into(),
+ make::tokens::whitespace(&format!("\n\n{indent}")).into(),
+ ],
+ );
+
+ if let Some(cap) = ctx.config.snippet_cap {
+ if let Some(name) = ty_alias.name() {
+ edit.add_tabstop_before(cap, name);
}
}
},
@@ -151,7 +158,7 @@ fn collect_used_generics<'gp>(
.and_then(|lt| known_generics.iter().find(find_lifetime(&lt.text()))),
),
ast::Type::ArrayType(ar) => {
- if let Some(ast::Expr::PathExpr(p)) = ar.expr() {
+ if let Some(ast::Expr::PathExpr(p)) = ar.const_arg().and_then(|x| x.expr()) {
if let Some(path) = p.path() {
if let Some(name_ref) = path.as_single_name_ref() {
if let Some(param) = known_generics.iter().find(|gp| {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
index 163561412..014c23197 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
@@ -1,3 +1,4 @@
+use hir::TypeInfo;
use stdx::format_to;
use syntax::{
ast::{self, AstNode},
@@ -46,21 +47,24 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
.take_while(|it| ctx.selection_trimmed().contains_range(it.text_range()))
.find_map(valid_target_expr)?;
- if let Some(ty_info) = ctx.sema.type_of_expr(&to_extract) {
- if ty_info.adjusted().is_unit() {
- return None;
- }
+ let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted);
+ if matches!(&ty, Some(ty_info) if ty_info.is_unit()) {
+ return None;
}
- let reference_modifier = match get_receiver_type(ctx, &to_extract) {
+ let parent = to_extract.syntax().parent().and_then(ast::Expr::cast);
+ let needs_adjust = parent
+ .as_ref()
+ .map_or(false, |it| matches!(it, ast::Expr::FieldExpr(_) | ast::Expr::MethodCallExpr(_)));
+
+ let reference_modifier = match ty.filter(|_| needs_adjust) {
Some(receiver_type) if receiver_type.is_mutable_reference() => "&mut ",
Some(receiver_type) if receiver_type.is_reference() => "&",
_ => "",
};
- let parent_ref_expr = to_extract.syntax().parent().and_then(ast::RefExpr::cast);
- let var_modifier = match parent_ref_expr {
- Some(expr) if expr.mut_token().is_some() => "mut ",
+ let var_modifier = match parent {
+ Some(ast::Expr::RefExpr(expr)) if expr.mut_token().is_some() => "mut ",
_ => "",
};
@@ -164,22 +168,6 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
}
}
-fn get_receiver_type(ctx: &AssistContext<'_>, expression: &ast::Expr) -> Option<hir::Type> {
- let receiver = get_receiver(expression.clone())?;
- Some(ctx.sema.type_of_expr(&receiver)?.original())
-}
-
-/// In the expression `a.b.c.x()`, find `a`
-fn get_receiver(expression: ast::Expr) -> Option<ast::Expr> {
- match expression {
- ast::Expr::FieldExpr(field) if field.expr().is_some() => {
- let nested_expression = &field.expr()?;
- get_receiver(nested_expression.to_owned())
- }
- _ => Some(expression),
- }
-}
-
#[derive(Debug)]
enum Anchor {
Before(SyntaxNode),
@@ -944,6 +932,11 @@ struct S {
vec: Vec<u8>
}
+struct Vec<T>;
+impl<T> Vec<T> {
+ fn push(&mut self, _:usize) {}
+}
+
fn foo(s: &mut S) {
$0s.vec$0.push(0);
}"#,
@@ -952,6 +945,11 @@ struct S {
vec: Vec<u8>
}
+struct Vec<T>;
+impl<T> Vec<T> {
+ fn push(&mut self, _:usize) {}
+}
+
fn foo(s: &mut S) {
let $0vec = &mut s.vec;
vec.push(0);
@@ -973,6 +971,10 @@ struct X {
struct S {
vec: Vec<u8>
}
+struct Vec<T>;
+impl<T> Vec<T> {
+ fn push(&mut self, _:usize) {}
+}
fn foo(f: &mut Y) {
$0f.field.field.vec$0.push(0);
@@ -987,6 +989,10 @@ struct X {
struct S {
vec: Vec<u8>
}
+struct Vec<T>;
+impl<T> Vec<T> {
+ fn push(&mut self, _:usize) {}
+}
fn foo(f: &mut Y) {
let $0vec = &mut f.field.field.vec;
@@ -1123,7 +1129,7 @@ struct S {
}
fn foo(s: S) {
- let $0x = s.sub;
+ let $0x = &s.sub;
x.do_thing();
}"#,
);
@@ -1189,7 +1195,7 @@ impl X {
fn foo() {
let local = &mut S::new();
- let $0x = &mut local.sub;
+ let $0x = &local.sub;
x.do_thing();
}"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
index 4c61678ea..d6c59a9c8 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs
@@ -62,7 +62,9 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
let assist_label = match target_name {
None => format!("Change visibility to {missing_visibility}"),
- Some(name) => format!("Change visibility of {name} to {missing_visibility}"),
+ Some(name) => {
+ format!("Change visibility of {} to {missing_visibility}", name.display(ctx.db()))
+ }
};
acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
@@ -117,8 +119,11 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_>
let target_file = in_file_source.file_id.original_file(ctx.db());
let target_name = record_field_def.name(ctx.db());
- let assist_label =
- format!("Change visibility of {parent_name}.{target_name} to {missing_visibility}");
+ let assist_label = format!(
+ "Change visibility of {}.{} to {missing_visibility}",
+ parent_name.display(ctx.db()),
+ target_name.display(ctx.db())
+ );
acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
builder.edit_file(target_file);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs
index ccdfcb0d9..eccd7675f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs
@@ -46,7 +46,8 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
let ty = ctx.sema.type_of_expr(&expr)?;
let scope = ctx.sema.scope(statement.syntax())?;
let constant_module = scope.module();
- let type_name = ty.original().display_source_code(ctx.db(), constant_module.into()).ok()?;
+ let type_name =
+ ty.original().display_source_code(ctx.db(), constant_module.into(), false).ok()?;
let target = statement.syntax().parent()?.text_range();
let path = constant_token.syntax().ancestors().find_map(ast::Path::cast)?;
@@ -106,10 +107,10 @@ fn get_text_for_generate_constant(
let mut text = format!("{vis}const {constant_token}: {type_name} = $0;");
while let Some(name_ref) = not_exist_name_ref.pop() {
let vis = if not_exist_name_ref.len() == 0 && !outer_exists { "" } else { "\npub " };
- text = text.replace("\n", "\n ");
+ text = text.replace('\n', "\n ");
text = format!("{vis}mod {name_ref} {{{text}\n}}");
}
- Some(text.replace("\n", &format!("\n{indent}")))
+ Some(text.replace('\n', &format!("\n{indent}")))
}
fn target_data_for_generate_constant(
@@ -131,7 +132,7 @@ fn target_data_for_generate_constant(
let siblings_has_newline = l_curly_token
.siblings_with_tokens(Direction::Next)
- .find(|it| it.kind() == SyntaxKind::WHITESPACE && it.to_string().contains("\n"))
+ .find(|it| it.kind() == SyntaxKind::WHITESPACE && it.to_string().contains('\n'))
.is_some();
let post_string =
if siblings_has_newline { format!("{indent}") } else { format!("\n{indent}") };
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index ed1b8f4e2..b68c766e6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -1,3 +1,5 @@
+use std::collections::HashSet;
+
use hir::{self, HasCrate, HasSource, HasVisibility};
use syntax::ast::{self, make, AstNode, HasGenericParams, HasName, HasVisibility as _};
@@ -63,25 +65,34 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
};
let sema_field_ty = ctx.sema.resolve_type(&field_ty)?;
- let krate = sema_field_ty.krate(ctx.db());
let mut methods = vec![];
- sema_field_ty.iterate_assoc_items(ctx.db(), krate, |item| {
- if let hir::AssocItem::Function(f) = item {
- if f.self_param(ctx.db()).is_some() && f.is_visible_from(ctx.db(), current_module) {
- methods.push(f)
- }
- }
- Option::<()>::None
- });
+ let mut seen_names = HashSet::new();
- for method in methods {
+ for ty in sema_field_ty.autoderef(ctx.db()) {
+ let krate = ty.krate(ctx.db());
+ ty.iterate_assoc_items(ctx.db(), krate, |item| {
+ if let hir::AssocItem::Function(f) = item {
+ let name = f.name(ctx.db());
+ if f.self_param(ctx.db()).is_some()
+ && f.is_visible_from(ctx.db(), current_module)
+ && seen_names.insert(name.clone())
+ {
+ methods.push((name, f))
+ }
+ }
+ Option::<()>::None
+ });
+ }
+ methods.sort_by(|(a, _), (b, _)| a.cmp(b));
+ for (name, method) in methods {
let adt = ast::Adt::Struct(strukt.clone());
- let name = method.name(ctx.db()).to_string();
- let impl_def = find_struct_impl(ctx, &adt, &[name]).flatten();
+ let name = name.display(ctx.db()).to_string();
+ // if `find_struct_impl` returns None, that means that a function named `name` already exists.
+ let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else { continue; };
acc.add_group(
&GroupLabel("Generate delegate methods…".to_owned()),
AssistId("generate_delegate_methods", AssistKind::Generate),
- format!("Generate delegate for `{field_name}.{}()`", method.name(ctx.db())),
+ format!("Generate delegate for `{field_name}.{name}()`",),
target,
|builder| {
// Create the function
@@ -89,9 +100,8 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
Some(source) => source.value,
None => return,
};
- let method_name = method.name(ctx.db());
let vis = method_source.visibility();
- let name = make::name(&method.name(ctx.db()).to_string());
+ let fn_name = make::name(&name);
let params =
method_source.param_list().unwrap_or_else(|| make::param_list(None, []));
let type_params = method_source.generic_param_list();
@@ -101,17 +111,30 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
};
let tail_expr = make::expr_method_call(
make::ext::field_from_idents(["self", &field_name]).unwrap(), // This unwrap is ok because we have at least 1 arg in the list
- make::name_ref(&method_name.to_string()),
+ make::name_ref(&name),
arg_list,
);
let ret_type = method_source.ret_type();
let is_async = method_source.async_token().is_some();
+ let is_const = method_source.const_token().is_some();
+ let is_unsafe = method_source.unsafe_token().is_some();
let tail_expr_finished =
if is_async { make::expr_await(tail_expr) } else { tail_expr };
let body = make::block_expr([], Some(tail_expr_finished));
- let f = make::fn_(vis, name, type_params, None, params, body, ret_type, is_async)
- .indent(ast::edit::IndentLevel(1))
- .clone_for_update();
+ let f = make::fn_(
+ vis,
+ fn_name,
+ type_params,
+ None,
+ params,
+ body,
+ ret_type,
+ is_async,
+ is_const,
+ is_unsafe,
+ )
+ .indent(ast::edit::IndentLevel(1))
+ .clone_for_update();
let cursor = Cursor::Before(f.syntax());
@@ -143,8 +166,16 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
let name = &strukt_name.to_string();
let params = strukt.generic_param_list();
let ty_params = params.clone();
- let impl_def = make::impl_(make::ext::ident_path(name), params, ty_params)
- .clone_for_update();
+ let where_clause = strukt.where_clause();
+
+ let impl_def = make::impl_(
+ ty_params,
+ None,
+ make::ty_path(make::ext::ident_path(name)),
+ where_clause,
+ None,
+ )
+ .clone_for_update();
let assoc_items = impl_def.get_or_create_assoc_item_list();
assoc_items.add_item(f.clone().into());
@@ -315,6 +346,44 @@ impl<T> Person<T> {
}
#[test]
+ fn test_generates_delegate_autoderef() {
+ check_assist(
+ generate_delegate_methods,
+ r#"
+//- minicore: deref
+struct Age(u8);
+impl Age {
+ fn age(&self) -> u8 {
+ self.0
+ }
+}
+struct AgeDeref(Age);
+impl core::ops::Deref for AgeDeref { type Target = Age; }
+struct Person {
+ ag$0e: AgeDeref,
+}
+impl Person {}"#,
+ r#"
+struct Age(u8);
+impl Age {
+ fn age(&self) -> u8 {
+ self.0
+ }
+}
+struct AgeDeref(Age);
+impl core::ops::Deref for AgeDeref { type Target = Age; }
+struct Person {
+ age: AgeDeref,
+}
+impl Person {
+ $0fn age(&self) -> u8 {
+ self.age.age()
+ }
+}"#,
+ );
+ }
+
+ #[test]
fn test_generate_delegate_visibility() {
check_assist_not_applicable(
generate_delegate_methods,
@@ -333,4 +402,26 @@ struct Person {
}"#,
)
}
+
+ #[test]
+ fn test_generate_not_eligible_if_fn_exists() {
+ check_assist_not_applicable(
+ generate_delegate_methods,
+ r#"
+struct Age(u8);
+impl Age {
+ fn age(&self) -> u8 {
+ self.0
+ }
+}
+
+struct Person {
+ ag$0e: Age,
+}
+impl Person {
+ fn age(&self) -> u8 { 0 }
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs
index b6958e291..815453961 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs
@@ -70,6 +70,7 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
target,
|edit| {
generate_edit(
+ ctx.db(),
edit,
strukt,
field_type.syntax(),
@@ -109,6 +110,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
target,
|edit| {
generate_edit(
+ ctx.db(),
edit,
strukt,
field_type.syntax(),
@@ -121,6 +123,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
}
fn generate_edit(
+ db: &RootDatabase,
edit: &mut SourceChangeBuilder,
strukt: ast::Struct,
field_type_syntax: &SyntaxNode,
@@ -144,7 +147,8 @@ fn generate_edit(
),
};
let strukt_adt = ast::Adt::Struct(strukt);
- let deref_impl = generate_trait_impl_text(&strukt_adt, &trait_path.to_string(), &impl_code);
+ let deref_impl =
+ generate_trait_impl_text(&strukt_adt, &trait_path.display(db).to_string(), &impl_code);
edit.insert(start_offset, deref_impl);
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs
index 339245b94..78ac2eb30 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs
@@ -1,5 +1,5 @@
use syntax::{
- ast::{self, AstNode, HasAttrs},
+ ast::{self, edit::IndentLevel, AstNode, HasAttrs},
SyntaxKind::{COMMENT, WHITESPACE},
TextSize,
};
@@ -42,7 +42,12 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
.next();
match derive_attr {
None => {
- builder.insert_snippet(cap, node_start, "#[derive($0)]\n");
+ let indent_level = IndentLevel::from_node(nominal.syntax());
+ builder.insert_snippet(
+ cap,
+ node_start,
+ format!("#[derive($0)]\n{indent_level}"),
+ );
}
Some(tt) => {
// Just move the cursor.
@@ -84,6 +89,20 @@ mod tests {
"struct Foo { $0 a: i32, }",
"#[derive($0)]\nstruct Foo { a: i32, }",
);
+ check_assist(
+ generate_derive,
+ "
+mod m {
+ struct Foo { a: i32,$0 }
+}
+ ",
+ "
+mod m {
+ #[derive($0)]
+ struct Foo { a: i32, }
+}
+ ",
+ );
}
#[test]
@@ -111,6 +130,24 @@ struct Foo { a: i32$0, }
struct Foo { a: i32, }
",
);
+ check_assist(
+ generate_derive,
+ "
+mod m {
+ /// `Foo` is a pretty important struct.
+ /// It does stuff.
+ struct Foo { a: i32,$0 }
+}
+ ",
+ "
+mod m {
+ /// `Foo` is a pretty important struct.
+ /// It does stuff.
+ #[derive($0)]
+ struct Foo { a: i32, }
+}
+ ",
+ );
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs
index b8415c72a..e87132218 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs
@@ -324,7 +324,7 @@ fn self_name(ast_func: &ast::Fn) -> Option<String> {
self_partial_type(ast_func).map(|name| to_lower_snake_case(&name))
}
-/// Heper function to get the name of the type of `self`
+/// Helper function to get the name of the type of `self`
fn self_type(ast_func: &ast::Fn) -> Option<ast::Type> {
ast_func.syntax().ancestors().find_map(ast::Impl::cast).and_then(|i| i.self_ty())
}
@@ -350,7 +350,7 @@ fn self_type_without_lifetimes(ast_func: &ast::Fn) -> Option<String> {
Some(name)
}
-/// Heper function to get the name of the type of `self` without generic arguments
+/// Helper function to get the name of the type of `self` without generic arguments
fn self_partial_type(ast_func: &ast::Fn) -> Option<String> {
let mut self_type = self_type(ast_func)?.to_string();
if let Some(idx) = self_type.find(|c| ['<', ' '].contains(&c)) {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs
index cd037f749..184f523e0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_variant.rs
@@ -192,7 +192,7 @@ fn expr_ty(
scope: &hir::SemanticsScope<'_>,
) -> Option<ast::Type> {
let ty = ctx.sema.type_of_expr(&arg).map(|it| it.adjusted())?;
- let text = ty.display_source_code(ctx.db(), scope.module().into()).ok()?;
+ let text = ty.display_source_code(ctx.db(), scope.module().into(), false).ok()?;
Some(make::ty(&text))
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
index 076838928..c579f6780 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
@@ -196,7 +196,7 @@ fn add_func_to_accumulator(
let mut func = function_template.to_string(ctx.config.snippet_cap);
if let Some(name) = adt_name {
// FIXME: adt may have generic params.
- func = format!("\n{indent}impl {name} {{\n{func}\n{indent}}}");
+ func = format!("\n{indent}impl {} {{\n{func}\n{indent}}}", name.display(ctx.db()));
}
builder.edit_file(file);
match ctx.config.snippet_cap {
@@ -291,12 +291,9 @@ impl FunctionBuilder {
let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
let is_async = await_expr.is_some();
- let (ret_type, should_focus_return_type) = make_return_type(
- ctx,
- &ast::Expr::CallExpr(call.clone()),
- target_module,
- &mut necessary_generic_params,
- );
+ let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into());
+ let (ret_type, should_focus_return_type) =
+ make_return_type(ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params);
let (generic_param_list, where_clause) =
fn_generic_params(ctx, necessary_generic_params, &target)?;
@@ -338,12 +335,9 @@ impl FunctionBuilder {
let await_expr = call.syntax().parent().and_then(ast::AwaitExpr::cast);
let is_async = await_expr.is_some();
- let (ret_type, should_focus_return_type) = make_return_type(
- ctx,
- &ast::Expr::MethodCallExpr(call.clone()),
- target_module,
- &mut necessary_generic_params,
- );
+ let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into());
+ let (ret_type, should_focus_return_type) =
+ make_return_type(ctx, &expr_for_ret_ty, target_module, &mut necessary_generic_params);
let (generic_param_list, where_clause) =
fn_generic_params(ctx, necessary_generic_params, &target)?;
@@ -378,6 +372,8 @@ impl FunctionBuilder {
fn_body,
self.ret_type,
self.is_async,
+ false, // FIXME : const and unsafe are not handled yet.
+ false,
);
let leading_ws;
let trailing_ws;
@@ -427,18 +423,18 @@ impl FunctionBuilder {
/// user can change the `todo!` function body.
fn make_return_type(
ctx: &AssistContext<'_>,
- call: &ast::Expr,
+ expr: &ast::Expr,
target_module: Module,
necessary_generic_params: &mut FxHashSet<hir::GenericParam>,
) -> (Option<ast::RetType>, bool) {
let (ret_ty, should_focus_return_type) = {
- match ctx.sema.type_of_expr(call).map(TypeInfo::original) {
+ match ctx.sema.type_of_expr(expr).map(TypeInfo::original) {
Some(ty) if ty.is_unknown() => (Some(make::ty_placeholder()), true),
None => (Some(make::ty_placeholder()), true),
Some(ty) if ty.is_unit() => (None, false),
Some(ty) => {
necessary_generic_params.extend(ty.generic_params(ctx.db()));
- let rendered = ty.display_source_code(ctx.db(), target_module.into());
+ let rendered = ty.display_source_code(ctx.db(), target_module.into(), true);
match rendered {
Ok(rendered) => (Some(make::ty(&rendered)), false),
Err(_) => (Some(make::ty_placeholder()), true),
@@ -893,14 +889,14 @@ fn filter_bounds_in_scope(
let target_impl = target.parent().ancestors().find_map(ast::Impl::cast)?;
let target_impl = ctx.sema.to_def(&target_impl)?;
// It's sufficient to test only the first element of `generic_params` because of the order of
- // insertion (see `relevant_parmas_and_where_clauses()`).
+ // insertion (see `params_and_where_preds_in_scope()`).
let def = generic_params.first()?.self_ty_param.parent();
if def != hir::GenericDef::Impl(target_impl) {
return None;
}
// Now we know every element that belongs to an impl would be in scope at `target`, we can
- // filter them out just by lookint at their parent.
+ // filter them out just by looking at their parent.
generic_params.retain(|it| !matches!(it.self_ty_param.parent(), hir::GenericDef::Impl(_)));
where_preds.retain(|it| {
it.node.syntax().parent().and_then(|it| it.parent()).and_then(ast::Impl::cast).is_none()
@@ -992,9 +988,9 @@ fn fn_arg_type(
let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate());
convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)
.map(|conversion| conversion.convert_type(ctx.db()))
- .or_else(|| ty.display_source_code(ctx.db(), target_module.into()).ok())
+ .or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok())
} else {
- ty.display_source_code(ctx.db(), target_module.into()).ok()
+ ty.display_source_code(ctx.db(), target_module.into(), true).ok()
}
}
@@ -1087,7 +1083,7 @@ fn calculate_necessary_visibility(
}
}
-// This is never intended to be used as a generic graph strucuture. If there's ever another need of
+// This is never intended to be used as a generic graph structure. If there's ever another need of
// graph algorithm, consider adding a library for that (and replace the following).
/// Minimally implemented directed graph structure represented by adjacency list.
struct Graph {
@@ -1910,7 +1906,6 @@ fn bar(new: fn) ${0:-> _} {
#[test]
fn add_function_with_closure_arg() {
- // FIXME: The argument in `bar` is wrong.
check_assist(
generate_function,
r"
@@ -1925,7 +1920,7 @@ fn foo() {
bar(closure)
}
-fn bar(closure: _) {
+fn bar(closure: impl Fn(i64) -> i64) {
${0:todo!()}
}
",
@@ -2267,13 +2262,13 @@ impl Foo {
check_assist(
generate_function,
r"
-fn foo() {
- $0bar(42).await();
+async fn foo() {
+ $0bar(42).await;
}
",
r"
-fn foo() {
- bar(42).await();
+async fn foo() {
+ bar(42).await;
}
async fn bar(arg: i32) ${0:-> _} {
@@ -2284,6 +2279,28 @@ async fn bar(arg: i32) ${0:-> _} {
}
#[test]
+ fn return_type_for_async_fn() {
+ check_assist(
+ generate_function,
+ r"
+//- minicore: result
+async fn foo() {
+ if Err(()) = $0bar(42).await {}
+}
+",
+ r"
+async fn foo() {
+ if Err(()) = bar(42).await {}
+}
+
+async fn bar(arg: i32) -> Result<_, ()> {
+ ${0:todo!()}
+}
+",
+ );
+ }
+
+ #[test]
fn create_method() {
check_assist(
generate_function,
@@ -2381,7 +2398,7 @@ mod s {
}
#[test]
- fn create_method_with_cursor_anywhere_on_call_expresion() {
+ fn create_method_with_cursor_anywhere_on_call_expression() {
check_assist(
generate_function,
r"
@@ -2401,6 +2418,31 @@ fn foo() {S.bar();}
}
#[test]
+ fn create_async_method() {
+ check_assist(
+ generate_function,
+ r"
+//- minicore: result
+struct S;
+async fn foo() {
+ if let Err(()) = S.$0bar(42).await {}
+}
+",
+ r"
+struct S;
+impl S {
+ async fn bar(&self, arg: i32) -> Result<_, ()> {
+ ${0:todo!()}
+ }
+}
+async fn foo() {
+ if let Err(()) = S.bar(42).await {}
+}
+",
+ )
+ }
+
+ #[test]
fn create_static_method() {
check_assist(
generate_function,
@@ -2421,6 +2463,31 @@ fn foo() {S::bar();}
}
#[test]
+ fn create_async_static_method() {
+ check_assist(
+ generate_function,
+ r"
+//- minicore: result
+struct S;
+async fn foo() {
+ if let Err(()) = S::$0bar(42).await {}
+}
+",
+ r"
+struct S;
+impl S {
+ async fn bar(arg: i32) -> Result<_, ()> {
+ ${0:todo!()}
+ }
+}
+async fn foo() {
+ if let Err(()) = S::bar(42).await {}
+}
+",
+ )
+ }
+
+ #[test]
fn create_generic_static_method() {
check_assist(
generate_function,
@@ -2488,7 +2555,7 @@ fn foo() {s::S::bar();}
}
#[test]
- fn create_static_method_with_cursor_anywhere_on_call_expresion() {
+ fn create_static_method_with_cursor_anywhere_on_call_expression() {
check_assist(
generate_function,
r"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs
index 4595cfe29..dd6bbd84a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs
@@ -174,7 +174,7 @@ pub(crate) fn generate_getter_impl(
// this buf inserts a newline at the end of a getter
// automatically, if one wants to add one more newline
// for separating it from other assoc items, that needs
- // to be handled spearately
+ // to be handled separately
let mut getter_buf =
generate_getter_from_info(ctx, &getter_info, record_field_info);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
index e30a3e942..824255e4f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
@@ -98,9 +98,9 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
.fields()
.enumerate()
.filter_map(|(i, f)| {
- let contructor = trivial_constructors[i].clone();
- if contructor.is_some() {
- contructor
+ let constructor = trivial_constructors[i].clone();
+ if constructor.is_some() {
+ constructor
} else {
Some(f.name()?.to_string())
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
index 28d815e81..797180fa1 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
@@ -958,7 +958,6 @@ fn main() {
);
}
- // FIXME: const generics aren't being substituted, this is blocked on better support for them
#[test]
fn inline_substitutes_generics() {
check_assist(
@@ -982,7 +981,7 @@ fn foo<T, const N: usize>() {
fn bar<U, const M: usize>() {}
fn main() {
- bar::<usize, N>();
+ bar::<usize, {0}>();
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
new file mode 100644
index 000000000..5b1540b50
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs
@@ -0,0 +1,722 @@
+use syntax::{ast, AstNode};
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: inline_const_as_literal
+//
+// Evaluate and inline const variable as literal.
+//
+// ```
+// const STRING: &str = "Hello, World!";
+//
+// fn something() -> &'static str {
+// STRING$0
+// }
+// ```
+// ->
+// ```
+// const STRING: &str = "Hello, World!";
+//
+// fn something() -> &'static str {
+// "Hello, World!"
+// }
+// ```
+pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let variable = ctx.find_node_at_offset::<ast::PathExpr>()?;
+
+ if let hir::PathResolution::Def(hir::ModuleDef::Const(konst)) =
+ ctx.sema.resolve_path(&variable.path()?)?
+ {
+ let konst_ty = konst.ty(ctx.sema.db);
+
+ // Used as the upper limit for recursive calls if no TCO is available
+ let fuel = 20;
+
+ // There is no way to have a const static reference to a type that contains a interior
+ // mutability cell.
+
+ // FIXME: Add support to handle type aliases for builtin scalar types.
+ validate_type_recursively(ctx, Some(&konst_ty), false, fuel)?;
+
+ let expr = konst.value(ctx.sema.db)?;
+
+ let value = match expr {
+ ast::Expr::BlockExpr(_)
+ | ast::Expr::Literal(_)
+ | ast::Expr::RefExpr(_)
+ | ast::Expr::ArrayExpr(_)
+ | ast::Expr::TupleExpr(_)
+ | ast::Expr::IfExpr(_)
+ | ast::Expr::ParenExpr(_)
+ | ast::Expr::MatchExpr(_)
+ | ast::Expr::MacroExpr(_)
+ | ast::Expr::BinExpr(_)
+ | ast::Expr::CallExpr(_) => match konst.render_eval(ctx.sema.db) {
+ Ok(result) => result,
+ Err(_) => return None,
+ },
+ _ => return None,
+ };
+
+ let id = AssistId("inline_const_as_literal", AssistKind::RefactorInline);
+
+ let label = format!("Inline const as literal");
+ let target = variable.syntax().text_range();
+
+ return acc.add(id, label, target, |edit| {
+ edit.replace(variable.syntax().text_range(), value);
+ });
+ }
+ None
+}
+
+fn validate_type_recursively(
+ ctx: &AssistContext<'_>,
+ ty_hir: Option<&hir::Type>,
+ refed: bool,
+ fuel: i32,
+) -> Option<()> {
+ match (fuel > 0, ty_hir) {
+ (true, Some(ty)) if ty.is_reference() => validate_type_recursively(
+ ctx,
+ ty.as_reference().map(|(ty, _)| ty).as_ref(),
+ true,
+ // FIXME: Saving fuel when `&` repeating might not be a good idea if there's no TCO.
+ if refed { fuel } else { fuel - 1 },
+ ),
+ (true, Some(ty)) if ty.is_array() => validate_type_recursively(
+ ctx,
+ ty.as_array(ctx.db()).map(|(ty, _)| ty).as_ref(),
+ false,
+ fuel - 1,
+ ),
+ (true, Some(ty)) if ty.is_tuple() => ty
+ .tuple_fields(ctx.db())
+ .iter()
+ .all(|ty| validate_type_recursively(ctx, Some(ty), false, fuel - 1).is_some())
+ .then_some(()),
+ (true, Some(ty)) if refed && ty.is_slice() => {
+ validate_type_recursively(ctx, ty.as_slice().as_ref(), false, fuel - 1)
+ }
+ (_, Some(ty)) => match ty.as_builtin() {
+ // `const A: str` is not correct, but `const A: &builtin` is.
+ Some(builtin) if refed || (!refed && !builtin.is_str()) => Some(()),
+ _ => None,
+ },
+ _ => None,
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ const NUMBER: u8 = 1;
+ const BOOL: u8 = 2;
+ const STR: u8 = 4;
+ const CHAR: u8 = 8;
+
+ const TEST_PAIRS: &[(&str, &str, u8)] = &[
+ ("u8", "0", NUMBER),
+ ("u16", "0", NUMBER),
+ ("u32", "0", NUMBER),
+ ("u64", "0", NUMBER),
+ ("u128", "0", NUMBER),
+ ("usize", "0", NUMBER),
+ ("i8", "0", NUMBER),
+ ("i16", "0", NUMBER),
+ ("i32", "0", NUMBER),
+ ("i64", "0", NUMBER),
+ ("i128", "0", NUMBER),
+ ("isize", "0", NUMBER),
+ ("bool", "false", BOOL),
+ ("&str", "\"str\"", STR),
+ ("char", "'c'", CHAR),
+ ];
+
+ // -----------Not supported-----------
+ #[test]
+ fn inline_const_as_literal_const_fn_call_slice() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const fn abc() -> &[{ty}] {{ &[{val}] }}
+ const ABC: &[{ty}] = abc();
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_expr_as_str_lit_not_applicable_const() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ const STR$0ING: &str = "Hello, World!";
+
+ fn something() -> &'static str {
+ STRING
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_struct_() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ struct A;
+ const STRUKT: A = A;
+
+ fn something() -> A {
+ STRU$0KT
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_enum_() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ enum A { A, B, C }
+ const ENUM: A = A::A;
+
+ fn something() -> A {
+ EN$0UM
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_tuple_closure() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ const CLOSURE: (&dyn Fn(i32) -> i32) = (&|num| -> i32 { num });
+ fn something() -> (&dyn Fn(i32) -> i32) {
+ STRU$0KT
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_closure_() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ const CLOSURE: &dyn Fn(i32) -> i32 = &|num| -> i32 { num };
+ fn something() -> &dyn Fn(i32) -> i32 {
+ STRU$0KT
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_fn_() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ struct S(i32);
+ const CON: fn(i32) -> S = S;
+ fn something() {
+ let x = CO$0N;
+ }
+ "#,
+ );
+ }
+
+ // ----------------------------
+
+ #[test]
+ fn inline_const_as_literal_const_expr() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const ABC: {ty} = {val};
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const ABC: {ty} = {val};
+ fn a() {{ {val} }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_const_block_expr() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const ABC: {ty} = {{ {val} }};
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const ABC: {ty} = {{ {val} }};
+ fn a() {{ {val} }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_const_block_eval_expr() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const ABC: {ty} = {{ true; {val} }};
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const ABC: {ty} = {{ true; {val} }};
+ fn a() {{ {val} }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_const_block_eval_block_expr() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const ABC: {ty} = {{ true; {{ {val} }} }};
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const ABC: {ty} = {{ true; {{ {val} }} }};
+ fn a() {{ {val} }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_const_fn_call_block_nested_builtin() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const fn abc() -> {ty} {{ {{ {{ {{ {val} }} }} }} }}
+ const ABC: {ty} = abc();
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const fn abc() -> {ty} {{ {{ {{ {{ {val} }} }} }} }}
+ const ABC: {ty} = abc();
+ fn a() {{ {val} }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_const_fn_call_tuple() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const fn abc() -> ({ty}, {ty}) {{ ({val}, {val}) }}
+ const ABC: ({ty}, {ty}) = abc();
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const fn abc() -> ({ty}, {ty}) {{ ({val}, {val}) }}
+ const ABC: ({ty}, {ty}) = abc();
+ fn a() {{ ({val}, {val}) }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_const_fn_call_builtin() {
+ TEST_PAIRS.into_iter().for_each(|(ty, val, _)| {
+ check_assist(
+ inline_const_as_literal,
+ &format!(
+ r#"
+ const fn abc() -> {ty} {{ {val} }}
+ const ABC: {ty} = abc();
+ fn a() {{ A$0BC }}
+ "#
+ ),
+ &format!(
+ r#"
+ const fn abc() -> {ty} {{ {val} }}
+ const ABC: {ty} = abc();
+ fn a() {{ {val} }}
+ "#
+ ),
+ );
+ });
+ }
+
+ #[test]
+ fn inline_const_as_literal_scalar_operators() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: i32 = 1 + 2 + 3;
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: i32 = 1 + 2 + 3;
+ fn a() { 6 }
+ "#,
+ );
+ }
+ #[test]
+ fn inline_const_as_literal_block_scalar_calculate_expr() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: i32 = { 1 + 2 + 3 };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: i32 = { 1 + 2 + 3 };
+ fn a() { 6 }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_block_scalar_calculate_param_expr() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: i32 = { (1 + 2 + 3) };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: i32 = { (1 + 2 + 3) };
+ fn a() { 6 }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_block_tuple_scalar_calculate_block_expr() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: (i32, i32) = { (1, { 2 + 3 }) };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: (i32, i32) = { (1, { 2 + 3 }) };
+ fn a() { (1, 5) }
+ "#,
+ );
+ }
+
+ // FIXME: Add support for nested ref slices when using `render_eval`
+ #[test]
+ fn inline_const_as_literal_block_slice() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ const ABC: &[&[&[&[&[&[i32]]]]]] = { &[&[&[&[&[&[10, 20, 30]]]]]] };
+ fn a() { A$0BC }
+ "#,
+ );
+ }
+
+ // FIXME: Add support for unary tuple expressions when using `render_eval`.
+ // `const fn abc() -> (i32) { (1) }` will results in `1` instead of `(1)` because it's evaluated
+ // as a paren expr.
+ #[test]
+ fn inline_const_as_literal_block_tuple() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: (([i32; 3]), (i32), ((&str, i32), i32), i32) = { (([1, 2, 3]), (10), (("hello", 10), 20), 30) };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: (([i32; 3]), (i32), ((&str, i32), i32), i32) = { (([1, 2, 3]), (10), (("hello", 10), 20), 30) };
+ fn a() { ([1, 2, 3], 10, (("hello", 10), 20), 30) }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_block_slice_single() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: [i32; 1] = { [10] };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: [i32; 1] = { [10] };
+ fn a() { [10] }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_block_array() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: [[[i32; 1]; 1]; 1] = { [[[10]]] };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: [[[i32; 1]; 1]; 1] = { [[[10]]] };
+ fn a() { [[[10]]] }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_block_recursive() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const ABC: &str = { { { { "hello" } } } };
+ fn a() { A$0BC }
+ "#,
+ r#"
+ const ABC: &str = { { { { "hello" } } } };
+ fn a() { "hello" }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_expr_as_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const STRING: &str = "Hello, World!";
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ const STRING: &str = "Hello, World!";
+
+ fn something() -> &'static str {
+ "Hello, World!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_eval_const_block_expr_to_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const STRING: &str = {
+ let x = 9;
+ if x + 10 == 21 {
+ "Hello, World!"
+ } else {
+ "World, Hello!"
+ }
+ };
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ const STRING: &str = {
+ let x = 9;
+ if x + 10 == 21 {
+ "Hello, World!"
+ } else {
+ "World, Hello!"
+ }
+ };
+
+ fn something() -> &'static str {
+ "World, Hello!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_eval_const_block_macro_expr_to_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ macro_rules! co {() => {"World, Hello!"};}
+ const STRING: &str = { co!() };
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ macro_rules! co {() => {"World, Hello!"};}
+ const STRING: &str = { co!() };
+
+ fn something() -> &'static str {
+ "World, Hello!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_eval_const_match_expr_to_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const STRING: &str = match 9 + 10 {
+ 0..18 => "Hello, World!",
+ _ => "World, Hello!"
+ };
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ const STRING: &str = match 9 + 10 {
+ 0..18 => "Hello, World!",
+ _ => "World, Hello!"
+ };
+
+ fn something() -> &'static str {
+ "World, Hello!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_eval_const_if_expr_to_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const STRING: &str = if 1 + 2 == 4 {
+ "Hello, World!"
+ } else {
+ "World, Hello!"
+ }
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ const STRING: &str = if 1 + 2 == 4 {
+ "Hello, World!"
+ } else {
+ "World, Hello!"
+ }
+
+ fn something() -> &'static str {
+ "World, Hello!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_eval_const_macro_expr_to_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ macro_rules! co {() => {"World, Hello!"};}
+ const STRING: &str = co!();
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ macro_rules! co {() => {"World, Hello!"};}
+ const STRING: &str = co!();
+
+ fn something() -> &'static str {
+ "World, Hello!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_eval_const_call_expr_to_str_lit() {
+ check_assist(
+ inline_const_as_literal,
+ r#"
+ const fn const_call() -> &'static str {"World, Hello!"}
+ const STRING: &str = const_call();
+
+ fn something() -> &'static str {
+ STR$0ING
+ }
+ "#,
+ r#"
+ const fn const_call() -> &'static str {"World, Hello!"}
+ const STRING: &str = const_call();
+
+ fn something() -> &'static str {
+ "World, Hello!"
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn inline_const_as_literal_expr_as_str_lit_not_applicable() {
+ check_assist_not_applicable(
+ inline_const_as_literal,
+ r#"
+ const STRING: &str = "Hello, World!";
+
+ fn something() -> &'static str {
+ STRING $0
+ }
+ "#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs
index 3fc552306..5aa8e56f5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs
@@ -148,7 +148,7 @@ macro_rules! num {
#[test]
fn inline_macro_simple_not_applicable_broken_macro() {
// FIXME: This is a bug. The macro should not expand, but it's
- // the same behaviour as the "Expand Macro Recursively" commmand
+ // the same behaviour as the "Expand Macro Recursively" command
// so it's presumably OK for the time being.
check_assist(
inline_macro,
@@ -254,4 +254,49 @@ fn f() { if true{}; }
"#,
)
}
+
+ #[test]
+ fn whitespace_between_text_and_pound() {
+ check_assist(
+ inline_macro,
+ r#"
+macro_rules! foo {
+ () => {
+ cfg_if! {
+ if #[cfg(test)] {
+ 1;
+ } else {
+ 1;
+ }
+ }
+ }
+}
+fn main() {
+ $0foo!();
+}
+"#,
+ r#"
+macro_rules! foo {
+ () => {
+ cfg_if! {
+ if #[cfg(test)] {
+ 1;
+ } else {
+ 1;
+ }
+ }
+ }
+}
+fn main() {
+ cfg_if!{
+ if #[cfg(test)]{
+ 1;
+ }else {
+ 1;
+ }
+};
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
index 062c816ae..b0d35c02d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs
@@ -1,5 +1,5 @@
use syntax::{
- ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode},
+ ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams},
ted,
};
@@ -14,7 +14,7 @@ use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
// ```
// ->
// ```
-// fn foo<B: Bar>(bar: B) {}
+// fn foo<$0B: Bar>(bar: B) {}
// ```
pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
@@ -39,7 +39,15 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_>
let new_ty = make::ty(&type_param_name).clone_for_update();
ted::replace(impl_trait_type.syntax(), new_ty.syntax());
- fn_.get_or_create_generic_param_list().add_generic_param(type_param.into())
+ fn_.get_or_create_generic_param_list().add_generic_param(type_param.into());
+
+ if let Some(cap) = ctx.config.snippet_cap {
+ if let Some(generic_param) =
+ fn_.generic_param_list().and_then(|it| it.generic_params().last())
+ {
+ edit.add_tabstop_before(cap, generic_param);
+ }
+ }
},
)
}
@@ -55,7 +63,7 @@ mod tests {
check_assist(
introduce_named_generic,
r#"fn foo<G>(bar: $0impl Bar) {}"#,
- r#"fn foo<G, B: Bar>(bar: B) {}"#,
+ r#"fn foo<G, $0B: Bar>(bar: B) {}"#,
);
}
@@ -64,7 +72,7 @@ mod tests {
check_assist(
introduce_named_generic,
r#"fn foo(bar: $0impl Bar) {}"#,
- r#"fn foo<B: Bar>(bar: B) {}"#,
+ r#"fn foo<$0B: Bar>(bar: B) {}"#,
);
}
@@ -73,7 +81,7 @@ mod tests {
check_assist(
introduce_named_generic,
r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#,
- r#"fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}"#,
+ r#"fn foo<G, $0B: Bar>(foo: impl Foo, bar: B) {}"#,
);
}
@@ -82,7 +90,7 @@ mod tests {
check_assist(
introduce_named_generic,
r#"fn foo<>(bar: $0impl Bar) {}"#,
- r#"fn foo<B: Bar>(bar: B) {}"#,
+ r#"fn foo<$0B: Bar>(bar: B) {}"#,
);
}
@@ -95,7 +103,7 @@ fn foo<
>(bar: $0impl Bar) {}
"#,
r#"
-fn foo<B: Bar
+fn foo<$0B: Bar
>(bar: B) {}
"#,
);
@@ -108,7 +116,7 @@ fn foo<B: Bar
check_assist(
introduce_named_generic,
r#"fn foo<B>(bar: $0impl Bar) {}"#,
- r#"fn foo<B, B: Bar>(bar: B) {}"#,
+ r#"fn foo<B, $0B: Bar>(bar: B) {}"#,
);
}
@@ -127,7 +135,7 @@ fn foo<
fn foo<
G: Foo,
F,
- H, B: Bar,
+ H, $0B: Bar,
>(bar: B) {}
"#,
);
@@ -138,7 +146,7 @@ fn foo<
check_assist(
introduce_named_generic,
r#"fn foo(bar: $0impl Foo + Bar) {}"#,
- r#"fn foo<F: Foo + Bar>(bar: F) {}"#,
+ r#"fn foo<$0F: Foo + Bar>(bar: F) {}"#,
);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
index a54dc4f96..c5aa9755b 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
@@ -66,7 +66,7 @@ fn generate_fn_def_assist(
// if we have a self reference, use that
Some(NeedsLifetime::SelfParam(self_param))
} else {
- // otherwise, if there's a single reference parameter without a named liftime, use that
+ // otherwise, if there's a single reference parameter without a named lifetime, use that
let fn_params_without_lifetime: Vec<_> = param_list
.params()
.filter_map(|param| match param.ty() {
@@ -79,7 +79,7 @@ fn generate_fn_def_assist(
match fn_params_without_lifetime.len() {
1 => Some(fn_params_without_lifetime.into_iter().next()?),
0 => None,
- // multiple unnnamed is invalid. assist is not applicable
+ // multiple unnamed is invalid. assist is not applicable
_ => return None,
}
};
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
index 641c90885..aae9f20d4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/merge_match_arms.rs
@@ -1,4 +1,4 @@
-use hir::TypeInfo;
+use hir::Type;
use std::{collections::HashMap, iter::successors};
use syntax::{
algo::neighbor,
@@ -95,7 +95,7 @@ fn contains_placeholder(a: &ast::MatchArm) -> bool {
}
fn are_same_types(
- current_arm_types: &HashMap<String, Option<TypeInfo>>,
+ current_arm_types: &HashMap<String, Option<Type>>,
arm: &ast::MatchArm,
ctx: &AssistContext<'_>,
) -> bool {
@@ -103,7 +103,7 @@ fn are_same_types(
for (other_arm_type_name, other_arm_type) in arm_types {
match (current_arm_types.get(&other_arm_type_name), other_arm_type) {
(Some(Some(current_arm_type)), Some(other_arm_type))
- if other_arm_type.original == current_arm_type.original => {}
+ if other_arm_type == *current_arm_type => {}
_ => return false,
}
}
@@ -114,44 +114,44 @@ fn are_same_types(
fn get_arm_types(
context: &AssistContext<'_>,
arm: &ast::MatchArm,
-) -> HashMap<String, Option<TypeInfo>> {
- let mut mapping: HashMap<String, Option<TypeInfo>> = HashMap::new();
+) -> HashMap<String, Option<Type>> {
+ let mut mapping: HashMap<String, Option<Type>> = HashMap::new();
fn recurse(
- map: &mut HashMap<String, Option<TypeInfo>>,
+ map: &mut HashMap<String, Option<Type>>,
ctx: &AssistContext<'_>,
pat: &Option<ast::Pat>,
) {
if let Some(local_pat) = pat {
- match pat {
- Some(ast::Pat::TupleStructPat(tuple)) => {
+ match local_pat {
+ ast::Pat::TupleStructPat(tuple) => {
for field in tuple.fields() {
recurse(map, ctx, &Some(field));
}
}
- Some(ast::Pat::TuplePat(tuple)) => {
+ ast::Pat::TuplePat(tuple) => {
for field in tuple.fields() {
recurse(map, ctx, &Some(field));
}
}
- Some(ast::Pat::RecordPat(record)) => {
+ ast::Pat::RecordPat(record) => {
if let Some(field_list) = record.record_pat_field_list() {
for field in field_list.fields() {
recurse(map, ctx, &field.pat());
}
}
}
- Some(ast::Pat::ParenPat(parentheses)) => {
+ ast::Pat::ParenPat(parentheses) => {
recurse(map, ctx, &parentheses.pat());
}
- Some(ast::Pat::SlicePat(slice)) => {
+ ast::Pat::SlicePat(slice) => {
for slice_pat in slice.pats() {
recurse(map, ctx, &Some(slice_pat));
}
}
- Some(ast::Pat::IdentPat(ident_pat)) => {
+ ast::Pat::IdentPat(ident_pat) => {
if let Some(name) = ident_pat.name() {
- let pat_type = ctx.sema.type_of_pat(local_pat);
+ let pat_type = ctx.sema.type_of_binding_in_pat(ident_pat);
map.insert(name.text().to_string(), pat_type);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
index d848fce4b..b6027eac5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_const_to_impl.rs
@@ -98,7 +98,7 @@ pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
};
builder.delete(range_to_delete);
- let const_ref = format!("Self::{name}");
+ let const_ref = format!("Self::{}", name.display(ctx.db()));
for range in usages.all().file_ranges().map(|it| it.range) {
builder.replace(range, const_ref.clone());
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
index 1728c03cd..917d0b367 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs
@@ -39,7 +39,7 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
}
let target = source_file.syntax().text_range();
- let module_name = module.name(ctx.db())?.to_string();
+ let module_name = module.name(ctx.db())?.display(ctx.db()).to_string();
let path = format!("../{module_name}.rs");
let dst = AnchoredPathBuf { anchor: ctx.file_id(), path };
acc.add(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
index a7c605325..166b25c69 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs
@@ -52,7 +52,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let mut buf = String::from("./");
match parent_module.name(ctx.db()) {
Some(name) if !parent_module.is_mod_rs(ctx.db()) => {
- format_to!(buf, "{name}/")
+ format_to!(buf, "{}/", name.display(ctx.db()))
}
_ => (),
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
index 076d25411..b73270cd0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
@@ -39,7 +39,7 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
}
let target = source_file.syntax().text_range();
- let module_name = module.name(ctx.db())?.to_string();
+ let module_name = module.name(ctx.db())?.display(ctx.db()).to_string();
let path = format!("./{module_name}/mod.rs");
let dst = AnchoredPathBuf { anchor: ctx.file_id(), path };
acc.add(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
index cbbea6c1e..23153b4c5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
@@ -57,11 +57,13 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>)
let local = ctx.sema.to_def(&pat)?;
let ty = ctx.sema.type_of_pat(&pat.into())?.original;
- if ty.contains_unknown() || ty.is_closure() {
- cov_mark::hit!(promote_lcoal_not_applicable_if_ty_not_inferred);
- return None;
- }
- let ty = ty.display_source_code(ctx.db(), module.into()).ok()?;
+ let ty = match ty.display_source_code(ctx.db(), module.into(), false) {
+ Ok(ty) => ty,
+ Err(_) => {
+ cov_mark::hit!(promote_local_not_applicable_if_ty_not_inferred);
+ return None;
+ }
+ };
let initializer = let_stmt.initializer()?;
if !is_body_const(&ctx.sema, &initializer) {
@@ -187,7 +189,7 @@ fn foo() {
#[test]
fn not_applicable_unknown_ty() {
- cov_mark::check!(promote_lcoal_not_applicable_if_ty_not_inferred);
+ cov_mark::check!(promote_local_not_applicable_if_ty_not_inferred);
check_assist_not_applicable(
promote_local_to_const,
r"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs
index 4cfe6c99b..a5c7fea40 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs
@@ -386,7 +386,7 @@ fn foo() {
}
#[test]
- fn pull_assignment_up_if_missing_assigment_not_applicable() {
+ fn pull_assignment_up_if_missing_assignment_not_applicable() {
check_assist_not_applicable(
pull_assignment_up,
r#"
@@ -401,7 +401,7 @@ fn foo() {
}
#[test]
- fn pull_assignment_up_match_missing_assigment_not_applicable() {
+ fn pull_assignment_up_match_missing_assignment_not_applicable() {
check_assist_not_applicable(
pull_assignment_up,
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
index e7014597a..4bf974a56 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
@@ -507,7 +507,7 @@ fn main() {
}
#[test]
- fn struct_method_over_stuct_instance() {
+ fn struct_method_over_struct_instance() {
check_assist_not_applicable(
qualify_method_call,
r#"
@@ -525,7 +525,7 @@ fn main() {
}
#[test]
- fn trait_method_over_stuct_instance() {
+ fn trait_method_over_struct_instance() {
check_assist_not_applicable(
qualify_method_call,
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
index e759e1561..239149dc4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
@@ -86,7 +86,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
acc.add_group(
&group_label,
AssistId("qualify_path", AssistKind::QuickFix),
- label(candidate, &import),
+ label(ctx.db(), candidate, &import),
range,
|builder| {
qualify_candidate.qualify(
@@ -186,7 +186,7 @@ fn find_trait_method(
if let Some(hir::AssocItem::Function(method)) =
trait_.items(db).into_iter().find(|item: &hir::AssocItem| {
item.name(db)
- .map(|name| name.to_string() == trait_method_name.to_string())
+ .map(|name| name.display(db).to_string() == trait_method_name.to_string())
.unwrap_or(false)
})
{
@@ -216,14 +216,14 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
GroupLabel(format!("Qualify {name}"))
}
-fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String {
+fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String {
let import_path = &import.import_path;
match candidate {
ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => {
- format!("Qualify as `{import_path}`")
+ format!("Qualify as `{}`", import_path.display(db))
}
- _ => format!("Qualify with `{import_path}`"),
+ _ => format!("Qualify with `{}`", import_path.display(db)),
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
index 01420430b..63db60633 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/raw_string.rs
@@ -20,6 +20,7 @@ use crate::{utils::required_hashes, AssistContext, AssistId, AssistKind, Assists
// }
// ```
pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ // FIXME: This should support byte and c strings as well.
let token = ctx.find_token_at_offset::<ast::String>()?;
if token.is_raw() {
return None;
@@ -157,9 +158,8 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
#[cfg(test)]
mod tests {
- use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
-
use super::*;
+ use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
#[test]
fn make_raw_string_target() {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs
index e9c7c6bae..ffc32f804 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs
@@ -124,7 +124,7 @@ mod tests {
}
#[test]
- fn remove_parens_doesnt_apply_weird_syntax_and_adge_cases() {
+ fn remove_parens_doesnt_apply_weird_syntax_and_edge_cases() {
// removing `()` would break code because {} would be counted as the loop/if body
check_assist_not_applicable(remove_parentheses, r#"fn f() { for _ in $0(0..{3}) {} }"#);
check_assist_not_applicable(remove_parentheses, r#"fn f() { for _ in $0(S {}) {} }"#);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs
index bd2e8fbe3..0772b168d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_param.rs
@@ -232,7 +232,7 @@ fn b() { foo( ) }
}
#[test]
- fn remove_unused_surrounded_by_parms() {
+ fn remove_unused_surrounded_by_params() {
check_assist(
remove_unused_param,
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs
index 58dcaf9a2..025625669 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs
@@ -20,9 +20,10 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
// const test: Foo = Foo {foo: 1, bar: 0}
// ```
pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- let record = ctx.find_node_at_offset::<Either<ast::RecordExpr, ast::RecordPat>>()?;
+ let path = ctx.find_node_at_offset::<ast::Path>()?;
+ let record =
+ path.syntax().parent().and_then(<Either<ast::RecordExpr, ast::RecordPat>>::cast)?;
- let path = record.as_ref().either(|it| it.path(), |it| it.path())?;
let ranks = compute_fields_ranks(&path, ctx)?;
let get_rank_of_field =
|of: Option<_>| *ranks.get(&of.unwrap_or_default()).unwrap_or(&usize::MAX);
@@ -96,7 +97,7 @@ fn compute_fields_ranks(
.fields(ctx.db())
.into_iter()
.enumerate()
- .map(|(idx, field)| (field.name(ctx.db()).to_string(), idx))
+ .map(|(idx, field)| (field.name(ctx.db()).display(ctx.db()).to_string(), idx))
.collect();
Some(res)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
index 208c3e109..666696623 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs
@@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
// }
//
// struct Bar;
-// $0impl Foo for Bar {
+// $0impl Foo for Bar$0 {
// const B: u8 = 17;
// fn c() {}
// type A = String;
@@ -45,6 +45,16 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
let items = impl_ast.assoc_item_list()?;
+
+ // restrict the range
+ // if cursor is in assoc_items, abort
+ let assoc_range = items.syntax().text_range();
+ let cursor_position = ctx.offset();
+ if assoc_range.contains_inclusive(cursor_position) {
+ cov_mark::hit!(not_applicable_editing_assoc_items);
+ return None;
+ }
+
let assoc_items = items.assoc_items().collect::<Vec<_>>();
let path = impl_ast
@@ -104,7 +114,7 @@ fn compute_item_ranks(
.iter()
.flat_map(|i| i.name(ctx.db()))
.enumerate()
- .map(|(idx, name)| (name.to_string(), idx))
+ .map(|(idx, name)| (name.display(ctx.db()).to_string(), idx))
.collect(),
)
}
@@ -264,9 +274,9 @@ trait Bar {
}
struct Foo;
-impl Bar for Foo {
+$0impl Bar for Foo {
type Fooo = ();
- type Foo = ();$0
+ type Foo = ();
}"#,
r#"
trait Bar {
@@ -281,4 +291,29 @@ impl Bar for Foo {
}"#,
)
}
+
+ #[test]
+ fn not_applicable_editing_assoc_items() {
+ cov_mark::check!(not_applicable_editing_assoc_items);
+ check_assist_not_applicable(
+ reorder_impl_items,
+ r#"
+trait Bar {
+ type T;
+ const C: ();
+ fn a() {}
+ fn z() {}
+ fn b() {}
+}
+struct Foo;
+impl Bar for Foo {
+ type T = ();$0
+ const C: () = ();
+ fn z() {}
+ fn a() {}
+ fn b() {}
+}
+ "#,
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 4cfae0c72..3bdd795be 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,8 +1,5 @@
use hir::{InFile, ModuleDef};
-use ide_db::{
- helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator,
- syntax_helpers::insert_whitespace_into_node::insert_ws_into,
-};
+use ide_db::{helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator};
use itertools::Itertools;
use syntax::{
ast::{self, AstNode, HasName},
@@ -59,7 +56,7 @@ pub(crate) fn replace_derive_with_manual_impl(
// collect the derive paths from the #[derive] expansion
let current_derives = ctx
.sema
- .parse_or_expand(hir_file)?
+ .parse_or_expand(hir_file)
.descendants()
.filter_map(ast::Attr::cast)
.filter_map(|attr| attr.path())
@@ -182,7 +179,11 @@ fn impl_def_from_trait(
let impl_def = {
use syntax::ast::Impl;
let text = generate_trait_impl_text(adt, trait_path.to_string().as_str(), "");
- let parse = syntax::SourceFile::parse(&text);
+ // FIXME: `generate_trait_impl_text` currently generates two newlines
+ // at the front, but these leading newlines should really instead be
+ // inserted at the same time the impl is inserted
+ assert_eq!(&text[..2], "\n\n", "`generate_trait_impl_text` output changed");
+ let parse = syntax::SourceFile::parse(&text[2..]);
let node = match parse.tree().syntax().descendants().find_map(Impl::cast) {
Some(it) => it,
None => {
@@ -193,24 +194,13 @@ fn impl_def_from_trait(
)
}
};
- let node = node.clone_subtree();
+ let node = node.clone_for_update();
assert_eq!(node.syntax().text_range().start(), 0.into());
node
};
- let trait_items = trait_items
- .into_iter()
- .map(|it| {
- if sema.hir_file_for(it.syntax()).is_macro() {
- if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
- return it;
- }
- }
- it.clone_for_update()
- })
- .collect();
- let (impl_def, first_assoc_item) =
- add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
+ let first_assoc_item =
+ add_trait_assoc_items_to_impl(sema, &trait_items, trait_, &impl_def, target_scope);
// Generate a default `impl` function body for the derived trait.
if let ast::AssocItem::Fn(ref func) = first_assoc_item {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
new file mode 100644
index 000000000..e7b62d49b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
@@ -0,0 +1,351 @@
+use hir::Semantics;
+use ide_db::{
+ base_db::{FileId, FileRange},
+ defs::Definition,
+ search::{SearchScope, UsageSearchResult},
+ RootDatabase,
+};
+use syntax::{
+ ast::{
+ self, make::impl_trait_type, HasGenericParams, HasName, HasTypeBounds, Name, NameLike,
+ PathType,
+ },
+ match_ast, ted, AstNode,
+};
+use text_edit::TextRange;
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: replace_named_generic_with_impl
+//
+// Replaces named generic with an `impl Trait` in function argument.
+//
+// ```
+// fn new<P$0: AsRef<Path>>(location: P) -> Self {}
+// ```
+// ->
+// ```
+// fn new(location: impl AsRef<Path>) -> Self {}
+// ```
+pub(crate) fn replace_named_generic_with_impl(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_>,
+) -> Option<()> {
+ // finds `<P: AsRef<Path>>`
+ let type_param = ctx.find_node_at_offset::<ast::TypeParam>()?;
+ // returns `P`
+ let type_param_name = type_param.name()?;
+
+ // The list of type bounds / traits: `AsRef<Path>`
+ let type_bound_list = type_param.type_bound_list()?;
+
+ let fn_ = type_param.syntax().ancestors().find_map(ast::Fn::cast)?;
+ let param_list_text_range = fn_.param_list()?.syntax().text_range();
+
+ let type_param_hir_def = ctx.sema.to_def(&type_param)?;
+ let type_param_def = Definition::GenericParam(hir::GenericParam::TypeParam(type_param_hir_def));
+
+ // get all usage references for the type param
+ let usage_refs = find_usages(&ctx.sema, &fn_, type_param_def, ctx.file_id());
+ if usage_refs.is_empty() {
+ return None;
+ }
+
+ // All usage references need to be valid (inside the function param list)
+ if !check_valid_usages(&usage_refs, param_list_text_range) {
+ return None;
+ }
+
+ let mut path_types_to_replace = Vec::new();
+ for (_a, refs) in usage_refs.iter() {
+ for usage_ref in refs {
+ let param_node = find_path_type(&ctx.sema, &type_param_name, &usage_ref.name)?;
+ path_types_to_replace.push(param_node);
+ }
+ }
+
+ let target = type_param.syntax().text_range();
+
+ acc.add(
+ AssistId("replace_named_generic_with_impl", AssistKind::RefactorRewrite),
+ "Replace named generic with impl trait",
+ target,
+ |edit| {
+ let type_param = edit.make_mut(type_param);
+ let fn_ = edit.make_mut(fn_);
+
+ let path_types_to_replace = path_types_to_replace
+ .into_iter()
+ .map(|param| edit.make_mut(param))
+ .collect::<Vec<_>>();
+
+ // remove trait from generic param list
+ if let Some(generic_params) = fn_.generic_param_list() {
+ generic_params.remove_generic_param(ast::GenericParam::TypeParam(type_param));
+ if generic_params.generic_params().count() == 0 {
+ ted::remove(generic_params.syntax());
+ }
+ }
+
+ let new_bounds = impl_trait_type(type_bound_list);
+ for path_type in path_types_to_replace.iter().rev() {
+ ted::replace(path_type.syntax(), new_bounds.clone_for_update().syntax());
+ }
+ },
+ )
+}
+
+fn find_path_type(
+ sema: &Semantics<'_, RootDatabase>,
+ type_param_name: &Name,
+ param: &NameLike,
+) -> Option<PathType> {
+ let path_type =
+ sema.ancestors_with_macros(param.syntax().clone()).find_map(ast::PathType::cast)?;
+
+ // Ignore any path types that look like `P::Assoc`
+ if path_type.path()?.as_single_name_ref()?.text() != type_param_name.text() {
+ return None;
+ }
+
+ let ancestors = sema.ancestors_with_macros(path_type.syntax().clone());
+
+ let mut in_generic_arg_list = false;
+ let mut is_associated_type = false;
+
+ // walking the ancestors checks them in a heuristic way until the `Fn` node is reached.
+ for ancestor in ancestors {
+ match_ast! {
+ match ancestor {
+ ast::PathSegment(ps) => {
+ match ps.kind()? {
+ ast::PathSegmentKind::Name(_name_ref) => (),
+ ast::PathSegmentKind::Type { .. } => return None,
+ _ => return None,
+ }
+ },
+ ast::GenericArgList(_) => {
+ in_generic_arg_list = true;
+ },
+ ast::AssocTypeArg(_) => {
+ is_associated_type = true;
+ },
+ ast::ImplTraitType(_) => {
+ if in_generic_arg_list && !is_associated_type {
+ return None;
+ }
+ },
+ ast::DynTraitType(_) => {
+ if !is_associated_type {
+ return None;
+ }
+ },
+ ast::Fn(_) => return Some(path_type),
+ _ => (),
+ }
+ }
+ }
+
+ None
+}
+
+/// Returns all usage references for the given type parameter definition.
+fn find_usages(
+ sema: &Semantics<'_, RootDatabase>,
+ fn_: &ast::Fn,
+ type_param_def: Definition,
+ file_id: FileId,
+) -> UsageSearchResult {
+ let file_range = FileRange { file_id, range: fn_.syntax().text_range() };
+ type_param_def.usages(sema).in_scope(SearchScope::file_range(file_range)).all()
+}
+
+fn check_valid_usages(usages: &UsageSearchResult, param_list_range: TextRange) -> bool {
+ usages
+ .iter()
+ .flat_map(|(_, usage_refs)| usage_refs)
+ .all(|usage_ref| param_list_range.contains_range(usage_ref.range))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ #[test]
+ fn replace_generic_moves_into_function() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<T$0: ToString>(input: T) -> Self {}"#,
+ r#"fn new(input: impl ToString) -> Self {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_with_inner_associated_type() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<P$0: AsRef<Path>>(input: P) -> Self {}"#,
+ r#"fn new(input: impl AsRef<Path>) -> Self {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_trait_applies_to_all_matching_params() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<T$0: ToString>(a: T, b: T) -> Self {}"#,
+ r#"fn new(a: impl ToString, b: impl ToString) -> Self {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_trait_applies_to_generic_arguments_in_params() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"
+ fn foo<P$0: Trait>(
+ _: P,
+ _: Option<P>,
+ _: Option<Option<P>>,
+ _: impl Iterator<Item = P>,
+ _: &dyn Iterator<Item = P>,
+ ) {}
+ "#,
+ r#"
+ fn foo(
+ _: impl Trait,
+ _: Option<impl Trait>,
+ _: Option<Option<impl Trait>>,
+ _: impl Iterator<Item = impl Trait>,
+ _: &dyn Iterator<Item = impl Trait>,
+ ) {}
+ "#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_when_one_param_type_is_invalid() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"
+ fn foo<P$0: Trait>(
+ _: i32,
+ _: Option<P>,
+ _: Option<Option<P>>,
+ _: impl Iterator<Item = P>,
+ _: &dyn Iterator<Item = P>,
+ _: <P as Trait>::Assoc,
+ ) {}
+ "#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_when_referenced_in_where_clause() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn foo<P$0: Trait, I>() where I: FromRef<P> {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_when_used_with_type_alias() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn foo<P$0: Trait>(p: <P as Trait>::Assoc) {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_when_used_as_argument_in_outer_trait_alias() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn foo<P$0: Trait>(_: <() as OtherTrait<P>>::Assoc) {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_with_inner_associated_type() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn foo<P$0: Trait>(_: P::Assoc) {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_when_passed_into_outer_impl_trait() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn foo<P$0: Trait>(_: impl OtherTrait<P>) {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_when_used_in_passed_function_parameter() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn foo<P$0: Trait>(_: &dyn Fn(P)) {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_with_multiple_generic_params() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<P: AsRef<Path>, T$0: ToString>(t: T, p: P) -> Self {}"#,
+ r#"fn new<P: AsRef<Path>>(t: impl ToString, p: P) -> Self {}"#,
+ );
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<T$0: ToString, P: AsRef<Path>>(t: T, p: P) -> Self {}"#,
+ r#"fn new<P: AsRef<Path>>(t: impl ToString, p: P) -> Self {}"#,
+ );
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<A: Send, B$0: ToString, C: Debug>(a: A, b: B, c: C) -> Self {}"#,
+ r#"fn new<A: Send, C: Debug>(a: A, b: impl ToString, c: C) -> Self {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_with_multiple_trait_bounds() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn new<P$0: Send + Sync>(p: P) -> Self {}"#,
+ r#"fn new(p: impl Send + Sync) -> Self {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_if_param_used_as_return_type() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn new<P$0: Send + Sync>(p: P) -> P {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_not_applicable_if_param_used_in_fn_body() {
+ check_assist_not_applicable(
+ replace_named_generic_with_impl,
+ r#"fn new<P$0: ToString>(p: P) { let x: &dyn P = &O; }"#,
+ );
+ }
+
+ #[test]
+ fn replace_generic_ignores_another_function_with_same_param_type() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"
+ fn new<P$0: Send + Sync>(p: P) {}
+ fn hello<P: Debug>(p: P) { println!("{:?}", p); }
+ "#,
+ r#"
+ fn new(p: impl Send + Sync) {}
+ fn hello<P: Debug>(p: P) { println!("{:?}", p); }
+ "#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs
index decb5fb62..6310981cc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_string_with_char.rs
@@ -31,14 +31,14 @@ pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_
if value.chars().take(2).count() != 1 {
return None;
}
- let quote_offets = token.quote_offsets()?;
+ let quote_offsets = token.quote_offsets()?;
acc.add(
AssistId("replace_string_with_char", AssistKind::RefactorRewrite),
"Replace string with char",
target,
|edit| {
- let (left, right) = quote_offets.quotes;
+ let (left, right) = quote_offsets.quotes;
edit.replace(left, '\'');
edit.replace(right, '\'');
if value == "'" {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
index 38fccb338..2e26f59d0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_try_expr_with_match.rs
@@ -20,7 +20,7 @@ use crate::assist_context::{AssistContext, Assists};
// Replaces a `try` expression with a `match` expression.
//
// ```
-// # //- minicore:option
+// # //- minicore: try, option
// fn handle() {
// let pat = Some(true)$0?;
// }
@@ -111,7 +111,7 @@ mod tests {
check_assist(
replace_try_expr_with_match,
r#"
-//- minicore:option
+//- minicore: try, option
fn test() {
let pat = Some(true)$0?;
}
@@ -132,7 +132,7 @@ fn test() {
check_assist(
replace_try_expr_with_match,
r#"
-//- minicore:result
+//- minicore: try, from, result
fn test() {
let pat = Ok(true)$0?;
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
index 6626ce079..43a97d7d3 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
@@ -55,7 +55,7 @@ pub(crate) fn replace_turbofish_with_explicit_type(
let returned_type = match ctx.sema.type_of_expr(&initializer) {
Some(returned_type) if !returned_type.original.contains_unknown() => {
let module = ctx.sema.scope(let_stmt.syntax())?.module();
- returned_type.original.display_source_code(ctx.db(), module.into()).ok()?
+ returned_type.original.display_source_code(ctx.db(), module.into(), false).ok()?
}
_ => {
cov_mark::hit!(fallback_to_turbofish_type_if_type_info_not_available);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs
index a93704b39..3a0121f55 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/sort_items.rs
@@ -87,11 +87,7 @@ pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
return None;
}
- if let Some(trait_ast) = ctx.find_node_at_offset::<ast::Trait>() {
- add_sort_methods_assist(acc, trait_ast.assoc_item_list()?)
- } else if let Some(impl_ast) = ctx.find_node_at_offset::<ast::Impl>() {
- add_sort_methods_assist(acc, impl_ast.assoc_item_list()?)
- } else if let Some(struct_ast) = ctx.find_node_at_offset::<ast::Struct>() {
+ if let Some(struct_ast) = ctx.find_node_at_offset::<ast::Struct>() {
add_sort_field_list_assist(acc, struct_ast.field_list())
} else if let Some(union_ast) = ctx.find_node_at_offset::<ast::Union>() {
add_sort_fields_assist(acc, union_ast.record_field_list()?)
@@ -103,6 +99,10 @@ pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
add_sort_fields_assist(acc, enum_struct_variant_ast)
} else if let Some(enum_ast) = ctx.find_node_at_offset::<ast::Enum>() {
add_sort_variants_assist(acc, enum_ast.variant_list()?)
+ } else if let Some(trait_ast) = ctx.find_node_at_offset::<ast::Trait>() {
+ add_sort_methods_assist(acc, ctx, trait_ast.assoc_item_list()?)
+ } else if let Some(impl_ast) = ctx.find_node_at_offset::<ast::Impl>() {
+ add_sort_methods_assist(acc, ctx, impl_ast.assoc_item_list()?)
} else {
None
}
@@ -116,9 +116,11 @@ trait AddRewrite {
new: Vec<T>,
target: TextRange,
) -> Option<()>;
+ fn yeet() {}
}
impl AddRewrite for Assists {
+ fn yeet() {}
fn add_rewrite<T: AstNode>(
&mut self,
label: &str,
@@ -146,7 +148,19 @@ fn add_sort_field_list_assist(acc: &mut Assists, field_list: Option<ast::FieldLi
}
}
-fn add_sort_methods_assist(acc: &mut Assists, item_list: ast::AssocItemList) -> Option<()> {
+fn add_sort_methods_assist(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_>,
+ item_list: ast::AssocItemList,
+) -> Option<()> {
+ let selection = ctx.selection_trimmed();
+
+ // ignore assist if the selection intersects with an associated item.
+ if item_list.assoc_items().any(|item| item.syntax().text_range().intersect(selection).is_some())
+ {
+ return None;
+ }
+
let methods = get_methods(&item_list);
let sorted = sort_by_name(&methods);
@@ -217,6 +231,51 @@ mod tests {
use super::*;
#[test]
+ fn not_applicable_if_selection_in_fn_body() {
+ check_assist_not_applicable(
+ sort_items,
+ r#"
+struct S;
+impl S {
+ fn func2() {
+ $0 bar $0
+ }
+ fn func() {}
+}
+ "#,
+ )
+ }
+
+ #[test]
+ fn not_applicable_if_selection_at_associated_const() {
+ check_assist_not_applicable(
+ sort_items,
+ r#"
+struct S;
+impl S {
+ fn func2() {}
+ fn func() {}
+ const C: () = $0()$0;
+}
+ "#,
+ )
+ }
+
+ #[test]
+ fn not_applicable_if_selection_overlaps_nodes() {
+ check_assist_not_applicable(
+ sort_items,
+ r#"
+struct S;
+impl $0S {
+ fn$0 func2() {}
+ fn func() {}
+}
+ "#,
+ )
+ }
+
+ #[test]
fn not_applicable_if_no_selection() {
cov_mark::check!(not_applicable_if_no_selection);
@@ -232,6 +291,21 @@ t$0rait Bar {
}
#[test]
+ fn not_applicable_if_selection_in_trait_fn_body() {
+ check_assist_not_applicable(
+ sort_items,
+ r#"
+trait Bar {
+ fn b() {
+ $0 hello $0
+ }
+ fn a();
+}
+ "#,
+ )
+ }
+
+ #[test]
fn not_applicable_if_trait_empty() {
cov_mark::check!(not_applicable_if_sorted_or_empty_or_single);
@@ -459,6 +533,31 @@ struct Bar {
}
#[test]
+ fn sort_struct_inside_a_function() {
+ check_assist(
+ sort_items,
+ r#"
+fn hello() {
+ $0struct Bar$0 {
+ b: u8,
+ a: u32,
+ c: u64,
+ }
+}
+ "#,
+ r#"
+fn hello() {
+ struct Bar {
+ a: u32,
+ b: u8,
+ c: u64,
+ }
+}
+ "#,
+ )
+ }
+
+ #[test]
fn sort_generic_struct_with_lifetime() {
check_assist(
sort_items,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
index 33b19a354..939055f14 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
@@ -51,15 +51,11 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
let replaced = match list.syntax().last_child() {
Some(last) => {
let stmts: Vec<ast::Stmt> = list.statements().collect();
- let initializer = ast::Expr::cast(last.clone())?;
+ let initializer = ast::Expr::cast(last)?;
let let_stmt = make::let_stmt(pattern, ty, Some(initializer));
if stmts.len() > 0 {
let block = make::block_expr(stmts, None);
- format!(
- "{}\n {}",
- update_expr_string(block.to_string()),
- let_stmt.to_string()
- )
+ format!("{}\n {}", update_expr_string(block.to_string()), let_stmt)
} else {
let_stmt.to_string()
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs
index 9ef4ae047..26f3c1926 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_result_return_type.rs
@@ -5,7 +5,7 @@ use ide_db::{
use itertools::Itertools;
use syntax::{
ast::{self, Expr},
- match_ast, AstNode, TextRange, TextSize,
+ match_ast, AstNode, NodeOrToken, SyntaxKind, TextRange,
};
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -38,14 +38,15 @@ pub(crate) fn unwrap_result_return_type(acc: &mut Assists, ctx: &AssistContext<'
};
let type_ref = &ret_type.ty()?;
- let ty = ctx.sema.resolve_type(type_ref)?.as_adt();
+ let Some(hir::Adt::Enum(ret_enum)) = ctx.sema.resolve_type(type_ref)?.as_adt() else { return None; };
let result_enum =
FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()).core_result_Result()?;
-
- if !matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == result_enum) {
+ if ret_enum != result_enum {
return None;
}
+ let Some(ok_type) = unwrap_result_type(type_ref) else { return None; };
+
acc.add(
AssistId("unwrap_result_return_type", AssistKind::RefactorRewrite),
"Unwrap Result return type",
@@ -64,26 +65,19 @@ pub(crate) fn unwrap_result_return_type(acc: &mut Assists, ctx: &AssistContext<'
});
for_each_tail_expr(&body, tail_cb);
- let mut is_unit_type = false;
- if let Some((_, inner_type)) = type_ref.to_string().split_once('<') {
- let inner_type = match inner_type.split_once(',') {
- Some((success_inner_type, _)) => success_inner_type,
- None => inner_type,
- };
- let new_ret_type = inner_type.strip_suffix('>').unwrap_or(inner_type);
- if new_ret_type == "()" {
- is_unit_type = true;
- let text_range = TextRange::new(
- ret_type.syntax().text_range().start(),
- ret_type.syntax().text_range().end() + TextSize::from(1u32),
- );
- builder.delete(text_range)
- } else {
- builder.replace(
- type_ref.syntax().text_range(),
- inner_type.strip_suffix('>').unwrap_or(inner_type),
- )
+ let is_unit_type = is_unit_type(&ok_type);
+ if is_unit_type {
+ let mut text_range = ret_type.syntax().text_range();
+
+ if let Some(NodeOrToken::Token(token)) = ret_type.syntax().next_sibling_or_token() {
+ if token.kind() == SyntaxKind::WHITESPACE {
+ text_range = TextRange::new(text_range.start(), token.text_range().end());
+ }
}
+
+ builder.delete(text_range);
+ } else {
+ builder.replace(type_ref.syntax().text_range(), ok_type.syntax().text());
}
for ret_expr_arg in exprs_to_unwrap {
@@ -134,6 +128,22 @@ fn tail_cb_impl(acc: &mut Vec<ast::Expr>, e: &ast::Expr) {
}
}
+// Tries to extract `T` from `Result<T, E>`.
+fn unwrap_result_type(ty: &ast::Type) -> Option<ast::Type> {
+ let ast::Type::PathType(path_ty) = ty else { return None; };
+ let path = path_ty.path()?;
+ let segment = path.first_segment()?;
+ let generic_arg_list = segment.generic_arg_list()?;
+ let generic_args: Vec<_> = generic_arg_list.generic_args().collect();
+ let ast::GenericArg::TypeArg(ok_type) = generic_args.first()? else { return None; };
+ ok_type.ty()
+}
+
+fn is_unit_type(ty: &ast::Type) -> bool {
+ let ast::Type::TupleType(tuple) = ty else { return false };
+ tuple.fields().next().is_none()
+}
+
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
@@ -175,6 +185,21 @@ fn foo() {
}
"#,
);
+
+ // Unformatted return type
+ check_assist(
+ unwrap_result_return_type,
+ r#"
+//- minicore: result
+fn foo() -> Result<(), Box<dyn Error$0>>{
+ Ok(())
+}
+"#,
+ r#"
+fn foo() {
+}
+"#,
+ );
}
#[test]
@@ -1017,4 +1042,52 @@ fn foo(the_field: u32) -> u32 {
"#,
);
}
+
+ #[test]
+ fn unwrap_result_return_type_nested_type() {
+ check_assist(
+ unwrap_result_return_type,
+ r#"
+//- minicore: result, option
+fn foo() -> Result<Option<i32$0>, ()> {
+ Ok(Some(42))
+}
+"#,
+ r#"
+fn foo() -> Option<i32> {
+ Some(42)
+}
+"#,
+ );
+
+ check_assist(
+ unwrap_result_return_type,
+ r#"
+//- minicore: result, option
+fn foo() -> Result<Option<Result<i32$0, ()>>, ()> {
+ Ok(None)
+}
+"#,
+ r#"
+fn foo() -> Option<Result<i32, ()>> {
+ None
+}
+"#,
+ );
+
+ check_assist(
+ unwrap_result_return_type,
+ r#"
+//- minicore: result, option, iterators
+fn foo() -> Result<impl Iterator<Item = i32>$0, ()> {
+ Ok(Some(42).into_iter())
+}
+"#,
+ r#"
+fn foo() -> impl Iterator<Item = i32> {
+ Some(42).into_iter()
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index 8b07e29a5..111753bf3 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -122,6 +122,7 @@ mod handlers {
mod convert_iter_for_each_to_for;
mod convert_let_else_to_match;
mod convert_match_to_let_else;
+ mod convert_nested_function_to_closure;
mod convert_tuple_struct_to_named_struct;
mod convert_named_struct_to_tuple_struct;
mod convert_to_guarded_return;
@@ -160,6 +161,7 @@ mod handlers {
mod generate_delegate_methods;
mod add_return_type;
mod inline_call;
+ mod inline_const_as_literal;
mod inline_local_variable;
mod inline_macro;
mod inline_type_alias;
@@ -192,6 +194,7 @@ mod handlers {
mod replace_arith_op;
mod introduce_named_generic;
mod replace_let_with_if_let;
+ mod replace_named_generic_with_impl;
mod replace_qualified_name_with_use;
mod replace_string_with_char;
mod replace_turbofish_with_explicit_type;
@@ -228,8 +231,9 @@ mod handlers {
convert_iter_for_each_to_for::convert_iter_for_each_to_for,
convert_iter_for_each_to_for::convert_for_loop_with_for_each,
convert_let_else_to_match::convert_let_else_to_match,
- convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
convert_match_to_let_else::convert_match_to_let_else,
+ convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
+ convert_nested_function_to_closure::convert_nested_function_to_closure,
convert_to_guarded_return::convert_to_guarded_return,
convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro,
@@ -262,6 +266,7 @@ mod handlers {
generate_new::generate_new,
inline_call::inline_call,
inline_call::inline_into_callers,
+ inline_const_as_literal::inline_const_as_literal,
inline_local_variable::inline_local_variable,
inline_type_alias::inline_type_alias,
inline_type_alias::inline_type_alias_uses,
@@ -297,6 +302,7 @@ mod handlers {
replace_let_with_if_let::replace_let_with_if_let,
replace_method_eager_lazy::replace_with_eager_method,
replace_method_eager_lazy::replace_with_lazy_method,
+ replace_named_generic_with_impl::replace_named_generic_with_impl,
replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type,
replace_qualified_name_with_use::replace_qualified_name_with_use,
replace_arith_op::replace_arith_with_wrapping,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
index 94be99fd7..344f2bfcc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
@@ -3,7 +3,7 @@ mod generated;
mod sourcegen;
use expect_test::expect;
-use hir::{db::DefDatabase, Semantics};
+use hir::Semantics;
use ide_db::{
base_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt},
imports::insert_use::{ImportGranularity, InsertUseConfig},
@@ -161,7 +161,7 @@ fn check_with_config(
assist_label: Option<&str>,
) {
let (mut db, file_with_caret_id, range_or_offset) = RootDatabase::with_range_or_offset(before);
- db.set_enable_proc_attr_macros(true);
+ db.enable_proc_attr_macros();
let text_without_caret = db.file_text(file_with_caret_id).to_string();
let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
@@ -273,8 +273,9 @@ fn assist_order_field_struct() {
assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
- assert_eq!(assists.next().expect("expected assist").label, "Convert to tuple struct");
assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
+ assert_eq!(assists.next().expect("expected assist").label, "Generate `new`");
+ assert_eq!(assists.next().map(|it| it.label.to_string()), None);
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index e5a8d675a..c097e0739 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -439,7 +439,7 @@ fn doctest_convert_match_to_let_else() {
r#####"
//- minicore: option
fn foo(opt: Option<()>) {
- let val = $0match opt {
+ let val$0 = match opt {
Some(it) => it,
None => return,
};
@@ -495,6 +495,31 @@ impl Point {
}
#[test]
+fn doctest_convert_nested_function_to_closure() {
+ check_doc_test(
+ "convert_nested_function_to_closure",
+ r#####"
+fn main() {
+ fn fo$0o(label: &str, number: u64) {
+ println!("{}: {}", label, number);
+ }
+
+ foo("Bar", 100);
+}
+"#####,
+ r#####"
+fn main() {
+ let foo = |label: &str, number: u64| {
+ println!("{}: {}", label, number);
+ };
+
+ foo("Bar", 100);
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_convert_to_guarded_return() {
check_doc_test(
"convert_to_guarded_return",
@@ -1455,6 +1480,27 @@ fn foo(name: Option<&str>) {
}
#[test]
+fn doctest_inline_const_as_literal() {
+ check_doc_test(
+ "inline_const_as_literal",
+ r#####"
+const STRING: &str = "Hello, World!";
+
+fn something() -> &'static str {
+ STRING$0
+}
+"#####,
+ r#####"
+const STRING: &str = "Hello, World!";
+
+fn something() -> &'static str {
+ "Hello, World!"
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_inline_into_callers() {
check_doc_test(
"inline_into_callers",
@@ -1596,7 +1642,7 @@ fn doctest_introduce_named_generic() {
fn foo(bar: $0impl Bar) {}
"#####,
r#####"
-fn foo<B: Bar>(bar: B) {}
+fn foo<$0B: Bar>(bar: B) {}
"#####,
)
}
@@ -2116,7 +2162,7 @@ trait Foo {
}
struct Bar;
-$0impl Foo for Bar {
+$0impl Foo for Bar$0 {
const B: u8 = 17;
fn c() {}
type A = String;
@@ -2314,6 +2360,19 @@ fn handle(action: Action) {
}
#[test]
+fn doctest_replace_named_generic_with_impl() {
+ check_doc_test(
+ "replace_named_generic_with_impl",
+ r#####"
+fn new<P$0: AsRef<Path>>(location: P) -> Self {}
+"#####,
+ r#####"
+fn new(location: impl AsRef<Path>) -> Self {}
+"#####,
+ )
+}
+
+#[test]
fn doctest_replace_qualified_name_with_use() {
check_doc_test(
"replace_qualified_name_with_use",
@@ -2352,7 +2411,7 @@ fn doctest_replace_try_expr_with_match() {
check_doc_test(
"replace_try_expr_with_match",
r#####"
-//- minicore:option
+//- minicore: try, option
fn handle() {
let pat = Some(true)$0?;
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs
index b4f50c7fb..3da90e905 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/sourcegen.rs
@@ -90,8 +90,6 @@ impl Assist {
let comment_blocks = sourcegen::CommentBlock::extract("Assist", &text);
for block in comment_blocks {
- // FIXME: doesn't support blank lines yet, need to tweak
- // `extract_comment_blocks` for that.
let id = block.id;
assert!(
id.chars().all(|it| it.is_ascii_lowercase() || it == '_'),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index f323ebcf7..03d855350 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -3,14 +3,17 @@
use std::ops;
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
-use hir::{db::HirDatabase, HirDisplay, Semantics};
-use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
+use hir::{db::HirDatabase, HirDisplay, InFile, Semantics};
+use ide_db::{
+ famous_defs::FamousDefs, path_transform::PathTransform,
+ syntax_helpers::insert_whitespace_into_node::insert_ws_into, RootDatabase, SnippetCap,
+};
use stdx::format_to;
use syntax::{
ast::{
self,
- edit::{self, AstNodeEdit},
- edit_in_place::{AttrsOwnerEdit, Removable},
+ edit::{AstNodeEdit, IndentLevel},
+ edit_in_place::{AttrsOwnerEdit, Indent, Removable},
make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
},
ted, AstNode, AstToken, Direction, SourceFile,
@@ -91,30 +94,21 @@ pub fn filter_assoc_items(
sema: &Semantics<'_, RootDatabase>,
items: &[hir::AssocItem],
default_methods: DefaultMethods,
-) -> Vec<ast::AssocItem> {
- fn has_def_name(item: &ast::AssocItem) -> bool {
- match item {
- ast::AssocItem::Fn(def) => def.name(),
- ast::AssocItem::TypeAlias(def) => def.name(),
- ast::AssocItem::Const(def) => def.name(),
- ast::AssocItem::MacroCall(_) => None,
- }
- .is_some()
- }
-
- items
+) -> Vec<InFile<ast::AssocItem>> {
+ return items
.iter()
// Note: This throws away items with no source.
- .filter_map(|&i| {
- let item = match i {
- hir::AssocItem::Function(i) => ast::AssocItem::Fn(sema.source(i)?.value),
- hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(sema.source(i)?.value),
- hir::AssocItem::Const(i) => ast::AssocItem::Const(sema.source(i)?.value),
+ .copied()
+ .filter_map(|assoc_item| {
+ let item = match assoc_item {
+ hir::AssocItem::Function(it) => sema.source(it)?.map(ast::AssocItem::Fn),
+ hir::AssocItem::TypeAlias(it) => sema.source(it)?.map(ast::AssocItem::TypeAlias),
+ hir::AssocItem::Const(it) => sema.source(it)?.map(ast::AssocItem::Const),
};
Some(item)
})
.filter(has_def_name)
- .filter(|it| match it {
+ .filter(|it| match &it.value {
ast::AssocItem::Fn(def) => matches!(
(default_methods, def.body()),
(DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
@@ -125,36 +119,67 @@ pub fn filter_assoc_items(
),
_ => default_methods == DefaultMethods::No,
})
- .collect::<Vec<_>>()
+ .collect();
+
+ fn has_def_name(item: &InFile<ast::AssocItem>) -> bool {
+ match &item.value {
+ ast::AssocItem::Fn(def) => def.name(),
+ ast::AssocItem::TypeAlias(def) => def.name(),
+ ast::AssocItem::Const(def) => def.name(),
+ ast::AssocItem::MacroCall(_) => None,
+ }
+ .is_some()
+ }
}
+/// Given `original_items` retrieved from the trait definition (usually by
+/// [`filter_assoc_items()`]), clones each item for update and applies path transformation to it,
+/// then inserts into `impl_`. Returns the modified `impl_` and the first associated item that got
+/// inserted.
pub fn add_trait_assoc_items_to_impl(
sema: &Semantics<'_, RootDatabase>,
- items: Vec<ast::AssocItem>,
+ original_items: &[InFile<ast::AssocItem>],
trait_: hir::Trait,
- impl_: ast::Impl,
+ impl_: &ast::Impl,
target_scope: hir::SemanticsScope<'_>,
-) -> (ast::Impl, ast::AssocItem) {
- let source_scope = sema.scope_for_def(trait_);
-
- let transform = PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
+) -> ast::AssocItem {
+ let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1;
+ let items = original_items.into_iter().map(|InFile { file_id, value: original_item }| {
+ let cloned_item = {
+ if file_id.is_macro() {
+ if let Some(formatted) =
+ ast::AssocItem::cast(insert_ws_into(original_item.syntax().clone()))
+ {
+ return formatted;
+ } else {
+ stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`");
+ }
+ }
+ original_item.clone_for_update()
+ };
- let items = items.into_iter().map(|assoc_item| {
- transform.apply(assoc_item.syntax());
- assoc_item.remove_attrs_and_docs();
- assoc_item
+ if let Some(source_scope) = sema.scope(original_item.syntax()) {
+ // FIXME: Paths in nested macros are not handled well. See
+ // `add_missing_impl_members::paths_in_nested_macro_should_get_transformed` test.
+ let transform =
+ PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
+ transform.apply(cloned_item.syntax());
+ }
+ cloned_item.remove_attrs_and_docs();
+ cloned_item.reindent_to(new_indent_level);
+ cloned_item
});
- let res = impl_.clone_for_update();
-
- let assoc_item_list = res.get_or_create_assoc_item_list();
+ let assoc_item_list = impl_.get_or_create_assoc_item_list();
let mut first_item = None;
for item in items {
first_item.get_or_insert_with(|| item.clone());
match &item {
ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
- let body = make::block_expr(None, Some(make::ext::expr_todo()))
- .indent(edit::IndentLevel(1));
+ let body = AstNodeEdit::indent(
+ &make::block_expr(None, Some(make::ext::expr_todo())),
+ new_indent_level,
+ );
ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax())
}
ast::AssocItem::TypeAlias(type_alias) => {
@@ -168,7 +193,7 @@ pub fn add_trait_assoc_items_to_impl(
assoc_item_list.add_item(item)
}
- (res, first_item.unwrap())
+ first_item.unwrap()
}
#[derive(Clone, Copy, Debug)]
@@ -338,7 +363,12 @@ fn calc_depth(pat: &ast::Pat, depth: usize) -> usize {
/// `find_struct_impl` looks for impl of a struct, but this also has additional feature
/// where it takes a list of function names and check if they exist inside impl_, if
-/// even one match is found, it returns None
+/// even one match is found, it returns None.
+///
+/// That means this function can have 3 potential return values:
+/// - `None`: an impl exists, but one of the function names within the impl matches one of the provided names.
+/// - `Some(None)`: no impl exists.
+/// - `Some(Some(_))`: an impl exists, with no matching function names.
pub(crate) fn find_struct_impl(
ctx: &AssistContext<'_>,
adt: &ast::Adt,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
index c521a10fc..f74ebfae0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
@@ -234,7 +234,7 @@ fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<Str
fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> {
let name = if let Some(adt) = ty.as_adt() {
- let name = adt.name(db).to_string();
+ let name = adt.name(db).display(db).to_string();
if WRAPPER_TYPES.contains(&name.as_str()) {
let inner_ty = ty.type_arguments().next()?;
@@ -258,7 +258,7 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase) -> Option<String> {
}
fn trait_name(trait_: &hir::Trait, db: &RootDatabase) -> Option<String> {
- let name = trait_.name(db).to_string();
+ let name = trait_.name(db).display(db).to_string();
if USELESS_TRAITS.contains(&name.as_str()) {
return None;
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
index c3136f6df..480cb77b4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
@@ -23,8 +23,8 @@ pub(crate) mod env_vars;
use std::iter;
-use hir::{known, ScopeDef, Variant};
-use ide_db::{imports::import_assets::LocatedImport, SymbolKind};
+use hir::{known, HasAttrs, ScopeDef, Variant};
+use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SymbolKind};
use syntax::ast;
use crate::{
@@ -62,8 +62,8 @@ impl From<Completions> for Vec<CompletionItem> {
impl Builder {
/// Convenience method, which allows to add a freshly created completion into accumulator
/// without binding it to the variable.
- pub(crate) fn add_to(self, acc: &mut Completions) {
- acc.add(self.build())
+ pub(crate) fn add_to(self, acc: &mut Completions, db: &RootDatabase) {
+ acc.add(self.build(db))
}
}
@@ -78,17 +78,9 @@ impl Completions {
}
}
- pub(crate) fn add_all<I>(&mut self, items: I)
- where
- I: IntoIterator,
- I::Item: Into<CompletionItem>,
- {
- items.into_iter().for_each(|item| self.add(item.into()))
- }
-
pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_>, keyword: &'static str) {
let item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), keyword);
- item.add_to(self);
+ item.add_to(self, ctx.db);
}
pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_>) {
@@ -142,7 +134,7 @@ impl Completions {
item.insert_text(if snippet.contains('$') { kw } else { snippet });
}
};
- item.add_to(self);
+ item.add_to(self, ctx.db);
}
pub(crate) fn add_keyword_snippet(
@@ -157,7 +149,7 @@ impl Completions {
Some(cap) => item.insert_snippet(cap, snippet),
None => item.insert_text(if snippet.contains('$') { kw } else { snippet }),
};
- item.add_to(self);
+ item.add_to(self, ctx.db);
}
pub(crate) fn add_crate_roots(
@@ -165,9 +157,9 @@ impl Completions {
ctx: &CompletionContext<'_>,
path_ctx: &PathCompletionCtx,
) {
- ctx.process_all_names(&mut |name, res| match res {
- ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => {
- self.add_module(ctx, path_ctx, m, name);
+ ctx.process_all_names(&mut |name, res, doc_aliases| match res {
+ ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root() => {
+ self.add_module(ctx, path_ctx, m, name, doc_aliases);
}
_ => (),
});
@@ -179,7 +171,11 @@ impl Completions {
path_ctx: &PathCompletionCtx,
local_name: hir::Name,
resolution: hir::ScopeDef,
+ doc_aliases: Vec<syntax::SmolStr>,
) {
+ if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
+ return;
+ }
let is_private_editable = match ctx.def_is_visible(&resolution) {
Visible::Yes => false,
Visible::Editable => true,
@@ -187,12 +183,14 @@ impl Completions {
};
self.add(
render_path_resolution(
- RenderContext::new(ctx).private_editable(is_private_editable),
+ RenderContext::new(ctx)
+ .private_editable(is_private_editable)
+ .doc_aliases(doc_aliases),
path_ctx,
local_name,
resolution,
)
- .build(),
+ .build(ctx.db),
);
}
@@ -203,6 +201,9 @@ impl Completions {
local_name: hir::Name,
resolution: hir::ScopeDef,
) {
+ if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
+ return;
+ }
let is_private_editable = match ctx.def_is_visible(&resolution) {
Visible::Yes => false,
Visible::Editable => true,
@@ -215,7 +216,7 @@ impl Completions {
local_name,
resolution,
)
- .build(),
+ .build(ctx.db),
);
}
@@ -225,6 +226,9 @@ impl Completions {
path_ctx: &PathCompletionCtx,
e: hir::Enum,
) {
+ if !ctx.check_stability(Some(&e.attrs(ctx.db))) {
+ return;
+ }
e.variants(ctx.db)
.into_iter()
.for_each(|variant| self.add_enum_variant(ctx, path_ctx, variant, None));
@@ -236,12 +240,17 @@ impl Completions {
path_ctx: &PathCompletionCtx,
module: hir::Module,
local_name: hir::Name,
+ doc_aliases: Vec<syntax::SmolStr>,
) {
+ if !ctx.check_stability(Some(&module.attrs(ctx.db))) {
+ return;
+ }
self.add_path_resolution(
ctx,
path_ctx,
local_name,
hir::ScopeDef::ModuleDef(module.into()),
+ doc_aliases,
);
}
@@ -252,6 +261,9 @@ impl Completions {
mac: hir::Macro,
local_name: hir::Name,
) {
+ if !ctx.check_stability(Some(&mac.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&mac) {
Visible::Yes => false,
Visible::Editable => true,
@@ -264,7 +276,7 @@ impl Completions {
local_name,
mac,
)
- .build(),
+ .build(ctx.db),
);
}
@@ -275,19 +287,25 @@ impl Completions {
func: hir::Function,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&func) {
Visible::Yes => false,
Visible::Editable => true,
Visible::No => return,
};
+ let doc_aliases = ctx.doc_aliases(&func);
self.add(
render_fn(
- RenderContext::new(ctx).private_editable(is_private_editable),
+ RenderContext::new(ctx)
+ .private_editable(is_private_editable)
+ .doc_aliases(doc_aliases),
path_ctx,
local_name,
func,
)
- .build(),
+ .build(ctx.db),
);
}
@@ -299,20 +317,26 @@ impl Completions {
receiver: Option<hir::Name>,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&func) {
Visible::Yes => false,
Visible::Editable => true,
Visible::No => return,
};
+ let doc_aliases = ctx.doc_aliases(&func);
self.add(
render_method(
- RenderContext::new(ctx).private_editable(is_private_editable),
+ RenderContext::new(ctx)
+ .private_editable(is_private_editable)
+ .doc_aliases(doc_aliases),
dot_access,
receiver,
local_name,
func,
)
- .build(),
+ .build(ctx.db),
);
}
@@ -323,26 +347,34 @@ impl Completions {
func: hir::Function,
import: LocatedImport,
) {
+ if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&func) {
Visible::Yes => false,
Visible::Editable => true,
Visible::No => return,
};
+ let doc_aliases = ctx.doc_aliases(&func);
self.add(
render_method(
RenderContext::new(ctx)
.private_editable(is_private_editable)
+ .doc_aliases(doc_aliases)
.import_to_add(Some(import)),
dot_access,
None,
None,
func,
)
- .build(),
+ .build(ctx.db),
);
}
pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
+ if !ctx.check_stability(Some(&konst.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&konst) {
Visible::Yes => false,
Visible::Editable => true,
@@ -359,6 +391,9 @@ impl Completions {
ctx: &CompletionContext<'_>,
type_alias: hir::TypeAlias,
) {
+ if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&type_alias) {
Visible::Yes => false,
Visible::Editable => true,
@@ -375,6 +410,9 @@ impl Completions {
ctx: &CompletionContext<'_>,
type_alias: hir::TypeAlias,
) {
+ if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
+ return;
+ }
self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
}
@@ -385,10 +423,13 @@ impl Completions {
variant: hir::Variant,
path: hir::ModPath,
) {
+ if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+ return;
+ }
if let Some(builder) =
render_variant_lit(RenderContext::new(ctx), path_ctx, None, variant, Some(path))
{
- self.add(builder.build());
+ self.add(builder.build(ctx.db));
}
}
@@ -399,6 +440,9 @@ impl Completions {
variant: hir::Variant,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+ return;
+ }
if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx {
cov_mark::hit!(enum_variant_pattern_path);
self.add_variant_pat(ctx, pat_ctx, Some(path_ctx), variant, local_name);
@@ -408,7 +452,7 @@ impl Completions {
if let Some(builder) =
render_variant_lit(RenderContext::new(ctx), path_ctx, local_name, variant, None)
{
- self.add(builder.build());
+ self.add(builder.build(ctx.db));
}
}
@@ -420,13 +464,17 @@ impl Completions {
field: hir::Field,
ty: &hir::Type,
) {
+ if !ctx.check_stability(Some(&field.attrs(ctx.db))) {
+ return;
+ }
let is_private_editable = match ctx.is_visible(&field) {
Visible::Yes => false,
Visible::Editable => true,
Visible::No => return,
};
+ let doc_aliases = ctx.doc_aliases(&field);
let item = render_field(
- RenderContext::new(ctx).private_editable(is_private_editable),
+ RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases),
dot_access,
receiver,
field,
@@ -443,10 +491,13 @@ impl Completions {
path: Option<hir::ModPath>,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
+ return;
+ }
if let Some(builder) =
render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name)
{
- self.add(builder.build());
+ self.add(builder.build(ctx.db));
}
}
@@ -457,6 +508,9 @@ impl Completions {
path: Option<hir::ModPath>,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&un.attrs(ctx.db))) {
+ return;
+ }
let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
self.add_opt(item);
}
@@ -468,17 +522,20 @@ impl Completions {
field: usize,
ty: &hir::Type,
) {
+ // Only used for (unnamed) tuples, whose all fields *are* stable. No need to check
+ // stability here.
let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
self.add(item);
}
pub(crate) fn add_lifetime(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) {
CompletionItem::new(SymbolKind::LifetimeParam, ctx.source_range(), name.to_smol_str())
- .add_to(self)
+ .add_to(self, ctx.db)
}
pub(crate) fn add_label(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) {
- CompletionItem::new(SymbolKind::Label, ctx.source_range(), name.to_smol_str()).add_to(self)
+ CompletionItem::new(SymbolKind::Label, ctx.source_range(), name.to_smol_str())
+ .add_to(self, ctx.db)
}
pub(crate) fn add_variant_pat(
@@ -489,6 +546,9 @@ impl Completions {
variant: hir::Variant,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+ return;
+ }
self.add_opt(render_variant_pat(
RenderContext::new(ctx),
pattern_ctx,
@@ -506,6 +566,9 @@ impl Completions {
variant: hir::Variant,
path: hir::ModPath,
) {
+ if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
+ return;
+ }
let path = Some(&path);
self.add_opt(render_variant_pat(
RenderContext::new(ctx),
@@ -524,6 +587,9 @@ impl Completions {
strukt: hir::Struct,
local_name: Option<hir::Name>,
) {
+ if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
+ return;
+ }
self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
index bb950c76f..466f0b1fb 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs
@@ -93,7 +93,7 @@ pub(crate) fn complete_attribute_path(
acc.add_macro(ctx, path_ctx, m, name)
}
hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
- acc.add_module(ctx, path_ctx, m, name)
+ acc.add_module(ctx, path_ctx, m, name, vec![])
}
_ => (),
}
@@ -104,12 +104,12 @@ pub(crate) fn complete_attribute_path(
Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
// only show modules in a fresh UseTree
Qualified::No => {
- ctx.process_all_names(&mut |name, def| match def {
+ ctx.process_all_names(&mut |name, def, doc_aliases| match def {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_attr(ctx.db) => {
acc.add_macro(ctx, path_ctx, m, name)
}
hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
- acc.add_module(ctx, path_ctx, m, name)
+ acc.add_module(ctx, path_ctx, m, name, doc_aliases)
}
_ => (),
});
@@ -139,7 +139,7 @@ pub(crate) fn complete_attribute_path(
}
if is_inner || !attr_completion.prefer_inner {
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
};
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
index 7ef4ff30b..19bfd294b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
@@ -12,7 +12,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
let add_completion = |item: &str| {
let mut completion = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), item);
completion.insert_text(format!(r#""{item}""#));
- acc.add(completion.build());
+ acc.add(completion.build(ctx.db));
};
let previous = iter::successors(ctx.original_token.prev_token(), |t| {
@@ -33,11 +33,11 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
item.insert_text(insert_text);
- acc.add(item.build());
+ acc.add(item.build(ctx.db));
}),
None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| {
let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
- acc.add(item.build());
+ acc.add(item.build(ctx.db));
}),
};
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
index 793c22630..9447bc7db 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
@@ -34,7 +34,7 @@ pub(crate) fn complete_derive_path(
acc.add_macro(ctx, path_ctx, mac, name)
}
ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
- acc.add_module(ctx, path_ctx, m, name)
+ acc.add_module(ctx, path_ctx, m, name, vec![])
}
_ => (),
}
@@ -43,7 +43,7 @@ pub(crate) fn complete_derive_path(
Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
// only show modules in a fresh UseTree
Qualified::No => {
- ctx.process_all_names(&mut |name, def| {
+ ctx.process_all_names(&mut |name, def, doc_aliases| {
let mac = match def {
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac))
if !existing_derives.contains(&mac) && mac.is_derive(ctx.db) =>
@@ -51,7 +51,7 @@ pub(crate) fn complete_derive_path(
mac
}
ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
- return acc.add_module(ctx, path_ctx, m, name);
+ return acc.add_module(ctx, path_ctx, m, name, doc_aliases);
}
_ => return,
};
@@ -90,7 +90,7 @@ pub(crate) fn complete_derive_path(
item.documentation(docs);
}
item.lookup_by(lookup);
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
None => acc.add_macro(ctx, path_ctx, mac, name),
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
index 818c3cfd5..6bc6f34ed 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
@@ -56,6 +56,6 @@ pub(super) fn complete_lint(
};
let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label);
item.documentation(hir::Documentation::new(description.to_owned()));
- item.add_to(acc)
+ item.add_to(acc, ctx.db)
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs
index a29417133..14f464b77 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/repr.rs
@@ -37,7 +37,7 @@ pub(super) fn complete_repr(
if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) {
item.insert_snippet(cap, snippet);
}
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
index 77246379e..c5bbb7f8d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs
@@ -23,7 +23,7 @@ pub(crate) fn complete_dot(
let mut item =
CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await");
item.detail("expr.await");
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
if let DotAccessKind::Method { .. } = dot_access.kind {
@@ -105,13 +105,20 @@ fn complete_fields(
mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type),
mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type),
) {
+ let mut seen_names = FxHashSet::default();
for receiver in receiver.autoderef(ctx.db) {
for (field, ty) in receiver.fields(ctx.db) {
- named_field(acc, field, ty);
+ if seen_names.insert(field.name(ctx.db)) {
+ named_field(acc, field, ty);
+ }
}
for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
- // Tuple fields are always public (tuple struct fields are handled above).
- tuple_index(acc, i, ty);
+ // Tuples are always the last type in a deref chain, so just check if the name is
+ // already seen without inserting into the hashset.
+ if !seen_names.contains(&hir::Name::new_tuple_field(i)) {
+ // Tuple fields are always public (tuple struct fields are handled above).
+ tuple_index(acc, i, ty);
+ }
}
}
}
@@ -173,6 +180,43 @@ fn foo(s: S) { s.$0 }
}
#[test]
+ fn no_unstable_method_on_stable() {
+ check(
+ r#"
+//- /main.rs crate:main deps:std
+fn foo(s: std::S) { s.$0 }
+//- /std.rs crate:std
+pub struct S;
+impl S {
+ #[unstable]
+ pub fn bar(&self) {}
+}
+"#,
+ expect![""],
+ );
+ }
+
+ #[test]
+ fn unstable_method_on_nightly() {
+ check(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+fn foo(s: std::S) { s.$0 }
+//- /std.rs crate:std
+pub struct S;
+impl S {
+ #[unstable]
+ pub fn bar(&self) {}
+}
+"#,
+ expect![[r#"
+ me bar() fn(&self)
+ "#]],
+ );
+ }
+
+ #[test]
fn test_struct_field_completion_self() {
check(
r#"
@@ -635,6 +679,74 @@ impl T {
}
#[test]
+ fn test_field_no_same_name() {
+ check(
+ r#"
+//- minicore: deref
+struct A { field: u8 }
+struct B { field: u16, another: u32 }
+impl core::ops::Deref for A {
+ type Target = B;
+ fn deref(&self) -> &Self::Target { loop {} }
+}
+fn test(a: A) {
+ a.$0
+}
+"#,
+ expect![[r#"
+ fd another u32
+ fd field u8
+ me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_field_no_same_index() {
+ check(
+ r#"
+//- minicore: deref
+struct A(u8);
+struct B(u16, u32);
+impl core::ops::Deref for A {
+ type Target = B;
+ fn deref(&self) -> &Self::Target { loop {} }
+}
+fn test(a: A) {
+ a.$0
+}
+"#,
+ expect![[r#"
+ fd 0 u8
+ fd 1 u32
+ me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_struct_deref_to_tuple_no_same_index() {
+ check(
+ r#"
+//- minicore: deref
+struct A(u8);
+impl core::ops::Deref for A {
+ type Target = (u16, u32);
+ fn deref(&self) -> &Self::Target { loop {} }
+}
+fn test(a: A) {
+ a.$0
+}
+"#,
+ expect![[r#"
+ fd 0 u8
+ fd 1 u32
+ me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+ "#]],
+ );
+ }
+
+ #[test]
fn test_completion_works_in_consts() {
check(
r#"
@@ -942,4 +1054,45 @@ fn test(thing: impl Encrypt) {
"#]],
)
}
+
+ #[test]
+ fn only_consider_same_type_once() {
+ check(
+ r#"
+//- minicore: deref
+struct A(u8);
+struct B(u16);
+impl core::ops::Deref for A {
+ type Target = B;
+ fn deref(&self) -> &Self::Target { loop {} }
+}
+impl core::ops::Deref for B {
+ type Target = A;
+ fn deref(&self) -> &Self::Target { loop {} }
+}
+fn test(a: A) {
+ a.$0
+}
+"#,
+ expect![[r#"
+ fd 0 u8
+ me deref() (use core::ops::Deref) fn(&self) -> &<Self as Deref>::Target
+ "#]],
+ );
+ }
+
+ #[test]
+ fn no_inference_var_in_completion() {
+ check(
+ r#"
+struct S<T>(T);
+fn test(s: S<Unknown>) {
+ s.$0
+}
+"#,
+ expect![[r#"
+ fd 0 {unknown}
+ "#]],
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
index 1002be211..419b86456 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
@@ -37,10 +37,10 @@ pub(crate) fn complete_cargo_env_vars(
guard_env_macro(expanded, &ctx.sema)?;
let range = expanded.text_range_between_quotes()?;
- CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| {
+ CARGO_DEFINED_VARS.into_iter().for_each(|&(var, detail)| {
let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
- item.detail(*detail);
- item.add_to(acc);
+ item.detail(detail);
+ item.add_to(acc, ctx.db);
});
Some(())
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
index cfe4787f7..9daa6984c 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
@@ -88,7 +88,13 @@ pub(crate) fn complete_expr_path(
let module_scope = module.scope(ctx.db, Some(ctx.module));
for (name, def) in module_scope {
if scope_def_applicable(def) {
- acc.add_path_resolution(ctx, path_ctx, name, def);
+ acc.add_path_resolution(
+ ctx,
+ path_ctx,
+ name,
+ def,
+ ctx.doc_aliases_in_scope(def),
+ );
}
}
}
@@ -212,7 +218,7 @@ pub(crate) fn complete_expr_path(
}
}
}
- ctx.process_all_names(&mut |name, def| match def {
+ ctx.process_all_names(&mut |name, def, doc_aliases| match def {
ScopeDef::ModuleDef(hir::ModuleDef::Trait(t)) => {
let assocs = t.items_with_supertraits(ctx.db);
match &*assocs {
@@ -220,12 +226,14 @@ pub(crate) fn complete_expr_path(
// there is no associated item path that can be constructed with them
[] => (),
// FIXME: Render the assoc item with the trait qualified
- &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def),
+ &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases),
// FIXME: Append `::` to the thing here, since a trait on its own won't work
- [..] => acc.add_path_resolution(ctx, path_ctx, name, def),
+ [..] => acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases),
}
}
- _ if scope_def_applicable(def) => acc.add_path_resolution(ctx, path_ctx, name, def),
+ _ if scope_def_applicable(def) => {
+ acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases)
+ }
_ => (),
});
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
index 4e89ef696..c717a9cb5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_abi.rs
@@ -42,7 +42,7 @@ const SUPPORTED_CALLING_CONVENTIONS: &[&str] = &[
pub(crate) fn complete_extern_abi(
acc: &mut Completions,
- _ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_>,
expanded: &ast::String,
) -> Option<()> {
if !expanded.syntax().parent().map_or(false, |it| ast::Abi::can_cast(it.kind())) {
@@ -51,7 +51,7 @@ pub(crate) fn complete_extern_abi(
let abi_str = expanded;
let source_range = abi_str.text_range_between_quotes()?;
for &abi in SUPPORTED_CALLING_CONVENTIONS {
- CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc);
+ CompletionItem::new(CompletionItemKind::Keyword, source_range, abi).add_to(acc, ctx.db);
}
Some(())
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
index 0979f6a6d..39c1b7f7b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
@@ -257,30 +257,24 @@ fn import_on_the_fly(
};
let user_input_lowercased = potential_import_name.to_lowercase();
- acc.add_all(
- import_assets
- .search_for_imports(
- &ctx.sema,
- ctx.config.insert_use.prefix_kind,
- ctx.config.prefer_no_std,
- )
- .into_iter()
- .filter(ns_filter)
- .filter(|import| {
- !ctx.is_item_hidden(&import.item_to_import)
- && !ctx.is_item_hidden(&import.original_item)
- })
- .sorted_by_key(|located_import| {
- compute_fuzzy_completion_order_key(
- &located_import.import_path,
- &user_input_lowercased,
- )
- })
- .filter_map(|import| {
- render_resolution_with_import(RenderContext::new(ctx), path_ctx, import)
- })
- .map(|builder| builder.build()),
- );
+ import_assets
+ .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std)
+ .into_iter()
+ .filter(ns_filter)
+ .filter(|import| {
+ let original_item = &import.original_item;
+ !ctx.is_item_hidden(&import.item_to_import)
+ && !ctx.is_item_hidden(original_item)
+ && ctx.check_stability(original_item.attrs(ctx.db).as_deref())
+ })
+ .sorted_by_key(|located_import| {
+ compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
+ })
+ .filter_map(|import| {
+ render_resolution_with_import(RenderContext::new(ctx), path_ctx, import)
+ })
+ .map(|builder| builder.build(ctx.db))
+ .for_each(|item| acc.add(item));
Some(())
}
@@ -305,30 +299,24 @@ fn import_on_the_fly_pat_(
};
let user_input_lowercased = potential_import_name.to_lowercase();
- acc.add_all(
- import_assets
- .search_for_imports(
- &ctx.sema,
- ctx.config.insert_use.prefix_kind,
- ctx.config.prefer_no_std,
- )
- .into_iter()
- .filter(ns_filter)
- .filter(|import| {
- !ctx.is_item_hidden(&import.item_to_import)
- && !ctx.is_item_hidden(&import.original_item)
- })
- .sorted_by_key(|located_import| {
- compute_fuzzy_completion_order_key(
- &located_import.import_path,
- &user_input_lowercased,
- )
- })
- .filter_map(|import| {
- render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import)
- })
- .map(|builder| builder.build()),
- );
+ import_assets
+ .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std)
+ .into_iter()
+ .filter(ns_filter)
+ .filter(|import| {
+ let original_item = &import.original_item;
+ !ctx.is_item_hidden(&import.item_to_import)
+ && !ctx.is_item_hidden(original_item)
+ && ctx.check_stability(original_item.attrs(ctx.db).as_deref())
+ })
+ .sorted_by_key(|located_import| {
+ compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
+ })
+ .filter_map(|import| {
+ render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import)
+ })
+ .map(|builder| builder.build(ctx.db))
+ .for_each(|item| acc.add(item));
Some(())
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs
index d8b8a190e..8b38d4f01 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/fn_param.rs
@@ -40,7 +40,7 @@ pub(crate) fn complete_fn_param(
};
// Completion lookup is omitted intentionally here.
// See the full discussion: https://github.com/rust-lang/rust-analyzer/issues/12073
- item.add_to(acc)
+ item.add_to(acc, ctx.db)
};
match kind {
@@ -50,7 +50,7 @@ pub(crate) fn complete_fn_param(
ParamKind::Closure(closure) => {
let stmt_list = closure.syntax().ancestors().find_map(ast::StmtList::cast)?;
params_from_stmt_list_scope(ctx, stmt_list, |name, ty| {
- add_new_item_to_acc(&format!("{name}: {ty}"));
+ add_new_item_to_acc(&format!("{}: {ty}", name.display(ctx.db)));
});
}
}
@@ -100,7 +100,9 @@ fn fill_fn_params(
if let Some(stmt_list) = function.syntax().parent().and_then(ast::StmtList::cast) {
params_from_stmt_list_scope(ctx, stmt_list, |name, ty| {
- file_params.entry(format!("{name}: {ty}")).or_insert(name.to_string());
+ file_params
+ .entry(format!("{}: {ty}", name.display(ctx.db)))
+ .or_insert(name.display(ctx.db).to_string());
});
}
remove_duplicated(&mut file_params, param_list.params());
@@ -127,7 +129,7 @@ fn params_from_stmt_list_scope(
let module = scope.module().into();
scope.process_all_names(&mut |name, def| {
if let hir::ScopeDef::Local(local) = def {
- if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module) {
+ if let Ok(ty) = local.ty(ctx.db).display_source_code(ctx.db, module, true) {
cb(name, ty);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
index 5c46c5806..8e904fd60 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
@@ -32,7 +32,7 @@ pub(crate) fn format_string(
let source_range = TextRange::new(brace_offset, cursor);
ctx.locals.iter().for_each(|(name, _)| {
CompletionItem::new(CompletionItemKind::Binding, source_range, name.to_smol_str())
- .add_to(acc);
+ .add_to(acc, ctx.db);
})
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs
index 60d05ae46..5ea6a49b1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list.rs
@@ -45,7 +45,7 @@ pub(crate) fn complete_item_list(
acc.add_macro(ctx, path_ctx, m, name)
}
hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
- acc.add_module(ctx, path_ctx, m, name)
+ acc.add_module(ctx, path_ctx, m, name, vec![])
}
_ => (),
}
@@ -55,12 +55,12 @@ pub(crate) fn complete_item_list(
}
Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
Qualified::No if ctx.qualifier_ctx.none() => {
- ctx.process_all_names(&mut |name, def| match def {
+ ctx.process_all_names(&mut |name, def, doc_aliases| match def {
hir::ScopeDef::ModuleDef(hir::ModuleDef::Macro(m)) if m.is_fn_like(ctx.db) => {
acc.add_macro(ctx, path_ctx, m, name)
}
hir::ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) => {
- acc.add_module(ctx, path_ctx, m, name)
+ acc.add_module(ctx, path_ctx, m, name, doc_aliases)
}
_ => (),
});
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 889d90095..269e40e6e 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -150,21 +150,24 @@ fn complete_trait_impl(
impl_def: &ast::Impl,
) {
if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
- get_missing_assoc_items(&ctx.sema, impl_def).into_iter().for_each(|item| {
- use self::ImplCompletionKind::*;
- match (item, kind) {
- (hir::AssocItem::Function(func), All | Fn) => {
- add_function_impl(acc, ctx, replacement_range, func, hir_impl)
+ get_missing_assoc_items(&ctx.sema, impl_def)
+ .into_iter()
+ .filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db))))
+ .for_each(|item| {
+ use self::ImplCompletionKind::*;
+ match (item, kind) {
+ (hir::AssocItem::Function(func), All | Fn) => {
+ add_function_impl(acc, ctx, replacement_range, func, hir_impl)
+ }
+ (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
+ add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
+ }
+ (hir::AssocItem::Const(const_), All | Const) => {
+ add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
+ }
+ _ => {}
}
- (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => {
- add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl)
- }
- (hir::AssocItem::Const(const_), All | Const) => {
- add_const_impl(acc, ctx, replacement_range, const_, hir_impl)
- }
- _ => {}
- }
- });
+ });
}
}
@@ -179,7 +182,7 @@ fn add_function_impl(
let label = format!(
"fn {}({})",
- fn_name,
+ fn_name.display(ctx.db),
if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." }
);
@@ -190,7 +193,7 @@ fn add_function_impl(
};
let mut item = CompletionItem::new(completion_kind, replacement_range, label);
- item.lookup_by(format!("fn {fn_name}"))
+ item.lookup_by(format!("fn {}", fn_name.display(ctx.db)))
.set_documentation(func.docs(ctx.db))
.set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() });
@@ -213,7 +216,7 @@ fn add_function_impl(
item.text_edit(TextEdit::replace(replacement_range, header));
}
};
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
}
@@ -224,9 +227,8 @@ fn get_transformed_assoc_item(
assoc_item: ast::AssocItem,
impl_def: hir::Impl,
) -> Option<ast::AssocItem> {
- let assoc_item = assoc_item.clone_for_update();
let trait_ = impl_def.trait_(ctx.db)?;
- let source_scope = &ctx.sema.scope_for_def(trait_);
+ let source_scope = &ctx.sema.scope(assoc_item.syntax())?;
let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?;
let transform = PathTransform::trait_impl(
target_scope,
@@ -235,6 +237,9 @@ fn get_transformed_assoc_item(
ctx.sema.source(impl_def)?.value,
);
+ let assoc_item = assoc_item.clone_for_update();
+ // FIXME: Paths in nested macros are not handled well. See
+ // `macro_generated_assoc_item2` test.
transform.apply(assoc_item.syntax());
assoc_item.remove_attrs_and_docs();
Some(assoc_item)
@@ -297,7 +302,7 @@ fn add_type_alias_impl(
item.text_edit(TextEdit::replace(replacement_range, decl));
}
};
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
}
@@ -337,7 +342,7 @@ fn add_const_impl(
),
None => item.text_edit(TextEdit::replace(replacement_range, replacement)),
};
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
}
@@ -831,6 +836,33 @@ impl Test for () {
}
#[test]
+ fn fn_with_lifetimes() {
+ check_edit(
+ "fn foo",
+ r#"
+trait Test<'a, 'b, T> {
+ fn foo(&self, a: &'a T, b: &'b T) -> &'a T;
+}
+
+impl<'x, 'y, A> Test<'x, 'y, A> for () {
+ t$0
+}
+"#,
+ r#"
+trait Test<'a, 'b, T> {
+ fn foo(&self, a: &'a T, b: &'b T) -> &'a T;
+}
+
+impl<'x, 'y, A> Test<'x, 'y, A> for () {
+ fn foo(&self, a: &'x A, b: &'y A) -> &'x A {
+ $0
+}
+}
+"#,
+ );
+ }
+
+ #[test]
fn complete_without_name() {
let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {
check_edit(
@@ -1191,6 +1223,81 @@ impl Foo for Test {
}
#[test]
+ fn macro_generated_assoc_item() {
+ check_edit(
+ "fn method",
+ r#"
+macro_rules! ty { () => { i32 } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ () => {
+ fn method(&mut self, params: <ty!() as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait { define_method!(); }
+impl AnotherTrait for () {
+ $0
+}
+"#,
+ r#"
+macro_rules! ty { () => { i32 } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ () => {
+ fn method(&mut self, params: <ty!() as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait { define_method!(); }
+impl AnotherTrait for () {
+ fn method(&mut self,params: <ty!()as SomeTrait>::Output) {
+ $0
+}
+}
+"#,
+ );
+ }
+
+ // FIXME: `T` in `ty!(T)` should be replaced by `PathTransform`.
+ #[test]
+ fn macro_generated_assoc_item2() {
+ check_edit(
+ "fn method",
+ r#"
+macro_rules! ty { ($me:ty) => { $me } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ ($t:ty) => {
+ fn method(&mut self, params: <ty!($t) as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait<T: SomeTrait> { define_method!(T); }
+impl AnotherTrait<i32> for () {
+ $0
+}
+"#,
+ r#"
+macro_rules! ty { ($me:ty) => { $me } }
+trait SomeTrait { type Output; }
+impl SomeTrait for i32 { type Output = i64; }
+macro_rules! define_method {
+ ($t:ty) => {
+ fn method(&mut self, params: <ty!($t) as SomeTrait>::Output);
+ };
+}
+trait AnotherTrait<T: SomeTrait> { define_method!(T); }
+impl AnotherTrait<i32> for () {
+ fn method(&mut self,params: <ty!(T)as SomeTrait>::Output) {
+ $0
+}
+}
+"#,
+ );
+ }
+
+ #[test]
fn includes_gat_generics() {
check_edit(
"type Ty",
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
index 3b79def63..2c6cbf614 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/lifetime.rs
@@ -329,6 +329,7 @@ fn foo() {
fn complete_label_in_for_iterable() {
check(
r#"
+//- minicore: iterator
fn foo() {
'outer: for _ in [{ 'inner: loop { break '$0 } }] {}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
index 950731eb4..d3e75c6da 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs
@@ -52,7 +52,7 @@ pub(crate) fn complete_mod(
let existing_mod_declarations = current_module
.children(ctx.db)
- .filter_map(|module| Some(module.name(ctx.db)?.to_string()))
+ .filter_map(|module| Some(module.name(ctx.db)?.display(ctx.db).to_string()))
.filter(|module| module != ctx.original_token.text())
.collect::<FxHashSet<_>>();
@@ -99,7 +99,7 @@ pub(crate) fn complete_mod(
label.push(';');
}
let item = CompletionItem::new(SymbolKind::Module, ctx.source_range(), &label);
- item.add_to(acc)
+ item.add_to(acc, ctx.db)
});
Some(())
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
index 58d5bf114..40b2c831a 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
@@ -64,7 +64,7 @@ pub(crate) fn complete_pattern(
// FIXME: ideally, we should look at the type we are matching against and
// suggest variants + auto-imports
- ctx.process_all_names(&mut |name, res| {
+ ctx.process_all_names(&mut |name, res, _| {
let add_simple_path = match res {
hir::ScopeDef::ModuleDef(def) => match def {
hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
@@ -127,7 +127,7 @@ pub(crate) fn complete_pattern_path(
};
if add_resolution {
- acc.add_path_resolution(ctx, path_ctx, name, def);
+ acc.add_path_resolution(ctx, path_ctx, name, def, vec![]);
}
}
}
@@ -164,7 +164,7 @@ pub(crate) fn complete_pattern_path(
Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx),
Qualified::No => {
// this will only be hit if there are brackets or braces, otherwise this will be parsed as an ident pattern
- ctx.process_all_names(&mut |name, res| {
+ ctx.process_all_names(&mut |name, res, doc_aliases| {
// FIXME: we should check what kind of pattern we are in and filter accordingly
let add_completion = match res {
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db),
@@ -175,7 +175,7 @@ pub(crate) fn complete_pattern_path(
_ => false,
};
if add_completion {
- acc.add_path_resolution(ctx, path_ctx, name, res);
+ acc.add_path_resolution(ctx, path_ctx, name, res, doc_aliases);
}
});
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index c55bd9aaa..2ffe12337 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -64,7 +64,7 @@ pub(crate) fn complete_postfix(
&format!("drop($0{receiver_text})"),
);
item.set_documentation(drop_fn.docs(ctx.db));
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
}
@@ -78,14 +78,14 @@ pub(crate) fn complete_postfix(
"if let Ok {}",
&format!("if let Ok($1) = {receiver_text} {{\n $0\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
postfix_snippet(
"while",
"while let Ok {}",
&format!("while let Ok($1) = {receiver_text} {{\n $0\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
TryEnum::Option => {
postfix_snippet(
@@ -93,22 +93,22 @@ pub(crate) fn complete_postfix(
"if let Some {}",
&format!("if let Some($1) = {receiver_text} {{\n $0\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
postfix_snippet(
"while",
"while let Some {}",
&format!("while let Some($1) = {receiver_text} {{\n $0\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
}
} else if receiver_ty.is_bool() || receiver_ty.is_unknown() {
postfix_snippet("if", "if expr {}", &format!("if {receiver_text} {{\n $0\n}}"))
- .add_to(acc);
+ .add_to(acc, ctx.db);
postfix_snippet("while", "while expr {}", &format!("while {receiver_text} {{\n $0\n}}"))
- .add_to(acc);
- postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc);
+ .add_to(acc, ctx.db);
+ postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db);
} else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator() {
if receiver_ty.impls_trait(ctx.db, trait_, &[]) {
postfix_snippet(
@@ -116,12 +116,12 @@ pub(crate) fn complete_postfix(
"for ele in expr {}",
&format!("for ele in {receiver_text} {{\n $0\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
}
- postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc);
- postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc);
+ postfix_snippet("ref", "&expr", &format!("&{receiver_text}")).add_to(acc, ctx.db);
+ postfix_snippet("refm", "&mut expr", &format!("&mut {receiver_text}")).add_to(acc, ctx.db);
let mut unsafe_should_be_wrapped = true;
if dot_receiver.syntax().kind() == BLOCK_EXPR {
@@ -137,7 +137,7 @@ pub(crate) fn complete_postfix(
} else {
format!("unsafe {receiver_text}")
};
- postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc);
+ postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db);
// The rest of the postfix completions create an expression that moves an argument,
// so it's better to consider references now to avoid breaking the compilation
@@ -162,7 +162,7 @@ pub(crate) fn complete_postfix(
"match expr {}",
&format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
TryEnum::Option => {
postfix_snippet(
@@ -172,7 +172,7 @@ pub(crate) fn complete_postfix(
"match {receiver_text} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}"
),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
},
None => {
@@ -181,20 +181,23 @@ pub(crate) fn complete_postfix(
"match expr {}",
&format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"),
)
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
}
- postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})")).add_to(acc);
- postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc); // fixme
- postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc);
- postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})")).add_to(acc);
+ postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})"))
+ .add_to(acc, ctx.db);
+ postfix_snippet("dbg", "dbg!(expr)", &format!("dbg!({receiver_text})")).add_to(acc, ctx.db); // fixme
+ postfix_snippet("dbgr", "dbg!(&expr)", &format!("dbg!(&{receiver_text})")).add_to(acc, ctx.db);
+ postfix_snippet("call", "function(expr)", &format!("${{1}}({receiver_text})"))
+ .add_to(acc, ctx.db);
if let Some(parent) = dot_receiver.syntax().parent().and_then(|p| p.parent()) {
if matches!(parent.kind(), STMT_LIST | EXPR_STMT) {
- postfix_snippet("let", "let", &format!("let $0 = {receiver_text};")).add_to(acc);
+ postfix_snippet("let", "let", &format!("let $0 = {receiver_text};"))
+ .add_to(acc, ctx.db);
postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text};"))
- .add_to(acc);
+ .add_to(acc, ctx.db);
}
}
@@ -315,7 +318,7 @@ fn add_custom_postfix_completions(
for import in imports.into_iter() {
builder.add_import(import);
}
- builder.add_to(acc);
+ builder.add_to(acc, ctx.db);
},
);
None
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
index dfcc78e92..cb242e4aa 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
@@ -60,7 +60,7 @@ pub(crate) fn add_format_like_completions(
format!(r#"{}({}, {})"#, macro_name, out, exprs.join(", "))
};
- postfix_snippet(label, macro_name, &snippet).add_to(acc);
+ postfix_snippet(label, macro_name, &snippet).add_to(acc, ctx.db);
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
index 0521e735d..945c3945b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
@@ -69,7 +69,7 @@ pub(crate) fn complete_record_expr_fields(
let mut item =
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
item.insert_text(".");
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
return;
}
missing_fields
@@ -98,7 +98,7 @@ pub(crate) fn add_default_update(
postfix_match: Some(CompletionRelevancePostfixMatch::Exact),
..Default::default()
});
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
index da1f0542d..e9831a5b2 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
@@ -32,8 +32,8 @@ pub(crate) fn complete_expr_snippet(
}
if in_block_expr {
- snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
- snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
+ snippet(ctx, cap, "pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc, ctx.db);
+ snippet(ctx, cap, "ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc, ctx.db);
let item = snippet(
ctx,
cap,
@@ -45,7 +45,7 @@ macro_rules! $1 {
};
}",
);
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
@@ -88,7 +88,7 @@ mod tests {
}",
);
item.lookup_by("tmod");
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
let mut item = snippet(
ctx,
@@ -101,7 +101,7 @@ fn ${1:feature}() {
}",
);
item.lookup_by("tfn");
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
let item = snippet(
ctx,
@@ -114,7 +114,7 @@ macro_rules! $1 {
};
}",
);
- item.add_to(acc);
+ item.add_to(acc, ctx.db);
}
}
@@ -146,7 +146,7 @@ fn add_custom_completions(
builder.add_import(import);
}
builder.set_detail(snip.description.clone());
- builder.add_to(acc);
+ builder.add_to(acc, ctx.db);
},
);
None
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
index 69c05a76d..e47054756 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
@@ -85,7 +85,7 @@ pub(crate) fn complete_type_path(
let module_scope = module.scope(ctx.db, Some(ctx.module));
for (name, def) in module_scope {
if scope_def_applicable(def) {
- acc.add_path_resolution(ctx, path_ctx, name, def);
+ acc.add_path_resolution(ctx, path_ctx, name, def, vec![]);
}
}
}
@@ -141,7 +141,7 @@ pub(crate) fn complete_type_path(
match location {
TypeLocation::TypeBound => {
acc.add_nameref_keywords_with_colon(ctx);
- ctx.process_all_names(&mut |name, res| {
+ ctx.process_all_names(&mut |name, res, doc_aliases| {
let add_resolution = match res {
ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => {
mac.is_fn_like(ctx.db)
@@ -152,7 +152,7 @@ pub(crate) fn complete_type_path(
_ => false,
};
if add_resolution {
- acc.add_path_resolution(ctx, path_ctx, name, res);
+ acc.add_path_resolution(ctx, path_ctx, name, res, doc_aliases);
}
});
return;
@@ -215,9 +215,9 @@ pub(crate) fn complete_type_path(
};
acc.add_nameref_keywords_with_colon(ctx);
- ctx.process_all_names(&mut |name, def| {
+ ctx.process_all_names(&mut |name, def, doc_aliases| {
if scope_def_applicable(def) {
- acc.add_path_resolution(ctx, path_ctx, name, def);
+ acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases);
}
});
}
@@ -242,7 +242,7 @@ pub(crate) fn complete_ascribed_type(
}
}?
.adjusted();
- let ty_string = x.display_source_code(ctx.db, ctx.module.into()).ok()?;
+ let ty_string = x.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
acc.add(render_type_inference(ty_string, ctx));
None
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
index 2555c34aa..7a60030e9 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs
@@ -52,6 +52,9 @@ pub(crate) fn complete_use_path(
)
};
for (name, def) in module_scope {
+ if !ctx.check_stability(def.attrs(ctx.db).as_deref()) {
+ continue;
+ }
let is_name_already_imported = name
.as_text()
.map_or(false, |text| already_imported_names.contains(text.as_str()));
@@ -72,7 +75,7 @@ pub(crate) fn complete_use_path(
is_name_already_imported,
..Default::default()
});
- acc.add(builder.build());
+ acc.add(builder.build(ctx.db));
}
}
}
@@ -91,10 +94,10 @@ pub(crate) fn complete_use_path(
// only show modules and non-std enum in a fresh UseTree
Qualified::No => {
cov_mark::hit!(unqualified_path_selected_only);
- ctx.process_all_names(&mut |name, res| {
+ ctx.process_all_names(&mut |name, res, doc_aliases| {
match res {
ScopeDef::ModuleDef(hir::ModuleDef::Module(module)) => {
- acc.add_module(ctx, path_ctx, module, name);
+ acc.add_module(ctx, path_ctx, module, name, doc_aliases);
}
ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Enum(e))) => {
// exclude prelude enum
@@ -105,9 +108,9 @@ pub(crate) fn complete_use_path(
let item = CompletionItem::new(
CompletionItemKind::SymbolKind(SymbolKind::Enum),
ctx.source_range(),
- format!("{}::", e.name(ctx.db)),
+ format!("{}::", e.name(ctx.db).display(ctx.db)),
);
- acc.add(item.build());
+ acc.add(item.build(ctx.db));
}
}
_ => {}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs
index 5e6cf4bf9..e0a959ad0 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/vis.rs
@@ -23,7 +23,7 @@ pub(crate) fn complete_vis_path(
if let Some(next) = next_towards_current {
if let Some(name) = next.name(ctx.db) {
cov_mark::hit!(visibility_qualified);
- acc.add_module(ctx, path_ctx, next, name);
+ acc.add_module(ctx, path_ctx, next, name, vec![]);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index 8cbf89e9c..7b145f3c1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -17,7 +17,7 @@ use ide_db::{
};
use syntax::{
ast::{self, AttrKind, NameOrNameRef},
- AstNode,
+ AstNode, SmolStr,
SyntaxKind::{self, *},
SyntaxToken, TextRange, TextSize, T,
};
@@ -367,6 +367,8 @@ pub(crate) struct CompletionContext<'a> {
pub(super) krate: hir::Crate,
/// The module of the `scope`.
pub(super) module: hir::Module,
+ /// Whether nightly toolchain is used. Cached since this is looked up a lot.
+ is_nightly: bool,
/// The expected name of what we are completing.
/// This is usually the parameter name of the function argument we are completing.
@@ -386,7 +388,7 @@ pub(crate) struct CompletionContext<'a> {
pub(super) depth_from_crate_root: usize,
}
-impl<'a> CompletionContext<'a> {
+impl CompletionContext<'_> {
/// The range of the identifier that is being completed.
pub(crate) fn source_range(&self) -> TextRange {
let kind = self.original_token.kind();
@@ -441,6 +443,14 @@ impl<'a> CompletionContext<'a> {
self.is_visible_impl(&vis, &attrs, item.krate(self.db))
}
+ pub(crate) fn doc_aliases<I>(&self, item: &I) -> Vec<SmolStr>
+ where
+ I: hir::HasAttrs + Copy,
+ {
+ let attrs = item.attrs(self.db);
+ attrs.doc_aliases().collect()
+ }
+
/// Check if an item is `#[doc(hidden)]`.
pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
let attrs = item.attrs(self.db);
@@ -451,6 +461,12 @@ impl<'a> CompletionContext<'a> {
}
}
+ /// Checks whether this item should be listed in regards to stability. Returns `true` if we should.
+ pub(crate) fn check_stability(&self, attrs: Option<&hir::Attrs>) -> bool {
+ let Some(attrs) = attrs else { return true; };
+ !attrs.is_unstable() || self.is_nightly
+ }
+
/// Whether the given trait is an operator trait or not.
pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool {
match trait_.attrs(self.db).lang() {
@@ -491,21 +507,22 @@ impl<'a> CompletionContext<'a> {
);
}
- /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items.
- pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
+ /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and
+ /// passes all doc-aliases along, to funnel it into [`Completions::add_path_resolution`].
+ pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec<SmolStr>)) {
let _p = profile::span("CompletionContext::process_all_names");
self.scope.process_all_names(&mut |name, def| {
if self.is_scope_def_hidden(def) {
return;
}
-
- f(name, def);
+ let doc_aliases = self.doc_aliases_in_scope(def);
+ f(name, def, doc_aliases);
});
}
pub(crate) fn process_all_names_raw(&self, f: &mut dyn FnMut(Name, ScopeDef)) {
let _p = profile::span("CompletionContext::process_all_names_raw");
- self.scope.process_all_names(&mut |name, def| f(name, def));
+ self.scope.process_all_names(f);
}
fn is_scope_def_hidden(&self, scope_def: ScopeDef) -> bool {
@@ -545,6 +562,14 @@ impl<'a> CompletionContext<'a> {
// `doc(hidden)` items are only completed within the defining crate.
self.krate != defining_crate && attrs.has_doc_hidden()
}
+
+ pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec<SmolStr> {
+ if let Some(attrs) = scope_def.attrs(self.db) {
+ attrs.doc_aliases().collect()
+ } else {
+ vec![]
+ }
+ }
}
// CompletionContext construction
@@ -615,6 +640,11 @@ impl<'a> CompletionContext<'a> {
let krate = scope.krate();
let module = scope.module();
+ let toolchain = db.crate_graph()[krate.into()].channel;
+ // `toolchain == None` means we're in some detached files. Since we have no information on
+ // the toolchain being used, let's just allow unstable items to be listed.
+ let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
+
let mut locals = FxHashMap::default();
scope.process_all_names(&mut |name, scope| {
if let ScopeDef::Local(local) = scope {
@@ -634,6 +664,7 @@ impl<'a> CompletionContext<'a> {
token,
krate,
module,
+ is_nightly,
expected_name,
expected_type,
qualifier_ctx,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index a94c40458..cc5221cfc 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -609,14 +609,14 @@ fn classify_name_ref(
_ => false,
};
- let reciever_is_part_of_indivisible_expression = match &receiver {
+ let receiver_is_part_of_indivisible_expression = match &receiver {
Some(ast::Expr::IfExpr(_)) => {
let next_token_kind = next_non_trivia_token(name_ref.syntax().clone()).map(|t| t.kind());
next_token_kind == Some(SyntaxKind::ELSE_KW)
},
_ => false
};
- if reciever_is_part_of_indivisible_expression {
+ if receiver_is_part_of_indivisible_expression {
return None;
}
@@ -1190,7 +1190,7 @@ fn pattern_context_for(
})
}).and_then(|variants| {
Some(variants.iter().filter_map(|variant| {
- let variant_name = variant.name(sema.db).to_string();
+ let variant_name = variant.name(sema.db).display(sema.db).to_string();
let variant_already_present = match_arm_list.arms().any(|arm| {
arm.pat().and_then(|pat| {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
index bb9fa7cca..e850f7bfd 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
@@ -3,7 +3,8 @@
use std::fmt;
use hir::{Documentation, Mutability};
-use ide_db::{imports::import_assets::LocatedImport, SnippetCap, SymbolKind};
+use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind};
+use itertools::Itertools;
use smallvec::SmallVec;
use stdx::{impl_from, never};
use syntax::{SmolStr, TextRange, TextSize};
@@ -45,7 +46,7 @@ pub struct CompletionItem {
///
/// That is, in `foo.bar$0` lookup of `abracadabra` will be accepted (it
/// contains `bar` sub sequence), and `quux` will rejected.
- pub lookup: Option<SmolStr>,
+ pub lookup: SmolStr,
/// Additional info to show in the UI pop up.
pub detail: Option<String>,
@@ -75,7 +76,8 @@ pub struct CompletionItem {
pub ref_match: Option<(Mutability, TextSize)>,
/// The import data to add to completion's edits.
- pub import_to_add: SmallVec<[LocatedImport; 1]>,
+ /// (ImportPath, LastSegment)
+ pub import_to_add: SmallVec<[(String, String); 1]>,
}
// We use custom debug for CompletionItem to make snapshot tests more readable.
@@ -353,12 +355,13 @@ impl CompletionItem {
relevance: CompletionRelevance::default(),
ref_match: None,
imports_to_add: Default::default(),
+ doc_aliases: vec![],
}
}
/// What string is used for filtering.
pub fn lookup(&self) -> &str {
- self.lookup.as_deref().unwrap_or(&self.label)
+ self.lookup.as_str()
}
pub fn ref_match(&self) -> Option<(String, text_edit::Indel, CompletionRelevance)> {
@@ -385,6 +388,7 @@ pub(crate) struct Builder {
source_range: TextRange,
imports_to_add: SmallVec<[LocatedImport; 1]>,
trait_name: Option<SmolStr>,
+ doc_aliases: Vec<SmolStr>,
label: SmolStr,
insert_text: Option<String>,
is_snippet: bool,
@@ -406,21 +410,31 @@ impl Builder {
local_name: hir::Name,
resolution: hir::ScopeDef,
) -> Self {
- render_path_resolution(RenderContext::new(ctx), path_ctx, local_name, resolution)
+ let doc_aliases = ctx.doc_aliases_in_scope(resolution);
+ render_path_resolution(
+ RenderContext::new(ctx).doc_aliases(doc_aliases),
+ path_ctx,
+ local_name,
+ resolution,
+ )
}
- pub(crate) fn build(self) -> CompletionItem {
+ pub(crate) fn build(self, db: &RootDatabase) -> CompletionItem {
let _p = profile::span("item::Builder::build");
let mut label = self.label;
- let mut lookup = self.lookup;
+ let mut lookup = self.lookup.unwrap_or_else(|| label.clone());
let insert_text = self.insert_text.unwrap_or_else(|| label.to_string());
+ if !self.doc_aliases.is_empty() {
+ let doc_aliases = self.doc_aliases.into_iter().join(", ");
+ label = SmolStr::from(format!("{label} (alias {doc_aliases})"));
+ lookup = SmolStr::from(format!("{lookup} {doc_aliases}"));
+ }
if let [import_edit] = &*self.imports_to_add {
// snippets can have multiple imports, but normal completions only have up to one
if let Some(original_path) = import_edit.original_path.as_ref() {
- lookup = lookup.or_else(|| Some(label.clone()));
- label = SmolStr::from(format!("{label} (use {original_path})"));
+ label = SmolStr::from(format!("{label} (use {})", original_path.display(db)));
}
} else if let Some(trait_name) = self.trait_name {
label = SmolStr::from(format!("{label} (as {trait_name})"));
@@ -431,6 +445,17 @@ impl Builder {
None => TextEdit::replace(self.source_range, insert_text),
};
+ let import_to_add = self
+ .imports_to_add
+ .into_iter()
+ .filter_map(|import| {
+ Some((
+ import.import_path.display(db).to_string(),
+ import.import_path.segments().last()?.display(db).to_string(),
+ ))
+ })
+ .collect();
+
CompletionItem {
source_range: self.source_range,
label,
@@ -444,7 +469,7 @@ impl Builder {
trigger_call_info: self.trigger_call_info,
relevance: self.relevance,
ref_match: self.ref_match,
- import_to_add: self.imports_to_add,
+ import_to_add,
}
}
pub(crate) fn lookup_by(&mut self, lookup: impl Into<SmolStr>) -> &mut Builder {
@@ -459,6 +484,10 @@ impl Builder {
self.trait_name = Some(trait_name);
self
}
+ pub(crate) fn doc_aliases(&mut self, doc_aliases: Vec<SmolStr>) -> &mut Builder {
+ self.doc_aliases = doc_aliases;
+ self
+ }
pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder {
self.insert_text = Some(insert_text.into());
self
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
index 6fe781114..106d4e1e5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
@@ -97,7 +97,7 @@ pub use crate::{
/// Main entry point for completion. We run completion as a two-phase process.
///
-/// First, we look at the position and collect a so-called `CompletionContext.
+/// First, we look at the position and collect a so-called `CompletionContext`.
/// This is a somewhat messy process, because, during completion, syntax tree is
/// incomplete and can look really weird.
///
@@ -133,7 +133,7 @@ pub use crate::{
///
/// Another case where this would be instrumental is macro expansion. We want to
/// insert a fake ident and re-expand code. There's `expand_speculative` as a
-/// work-around for this.
+/// workaround for this.
///
/// A different use-case is completion of injection (examples and links in doc
/// comments). When computing completion for a path in a doc-comment, you want
@@ -243,7 +243,7 @@ pub fn resolve_completion_edits(
config.prefer_no_std,
)
})
- .find(|mod_path| mod_path.to_string() == full_import_path);
+ .find(|mod_path| mod_path.display(db).to_string() == full_import_path);
if let Some(import_path) = import {
insert_use::insert_use(&new_ast, mod_path_to_ast(&import_path), &config.insert_use);
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
index c1f51aabb..1953eb479 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -32,11 +32,17 @@ pub(crate) struct RenderContext<'a> {
completion: &'a CompletionContext<'a>,
is_private_editable: bool,
import_to_add: Option<LocatedImport>,
+ doc_aliases: Vec<SmolStr>,
}
impl<'a> RenderContext<'a> {
pub(crate) fn new(completion: &'a CompletionContext<'a>) -> RenderContext<'a> {
- RenderContext { completion, is_private_editable: false, import_to_add: None }
+ RenderContext {
+ completion,
+ is_private_editable: false,
+ import_to_add: None,
+ doc_aliases: vec![],
+ }
}
pub(crate) fn private_editable(mut self, private_editable: bool) -> Self {
@@ -49,6 +55,11 @@ impl<'a> RenderContext<'a> {
self
}
+ pub(crate) fn doc_aliases(mut self, doc_aliases: Vec<SmolStr>) -> Self {
+ self.doc_aliases = doc_aliases;
+ self
+ }
+
fn snippet_cap(&self) -> Option<SnippetCap> {
self.completion.config.snippet_cap
}
@@ -115,24 +126,25 @@ pub(crate) fn render_field(
field: hir::Field,
ty: &hir::Type,
) -> CompletionItem {
+ let db = ctx.db();
let is_deprecated = ctx.is_deprecated(field);
- let name = field.name(ctx.db());
+ let name = field.name(db);
let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str());
let mut item = CompletionItem::new(
SymbolKind::Field,
ctx.source_range(),
- field_with_receiver(receiver.as_ref(), &name),
+ field_with_receiver(db, receiver.as_ref(), &name),
);
item.set_relevance(CompletionRelevance {
type_match: compute_type_match(ctx.completion, ty),
exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()),
..CompletionRelevance::default()
});
- item.detail(ty.display(ctx.db()).to_string())
- .set_documentation(field.docs(ctx.db()))
+ item.detail(ty.display(db).to_string())
+ .set_documentation(field.docs(db))
.set_deprecated(is_deprecated)
.lookup_by(name);
- item.insert_text(field_with_receiver(receiver.as_ref(), &escaped_name));
+ item.insert_text(field_with_receiver(db, receiver.as_ref(), &escaped_name));
if let Some(receiver) = &dot_access.receiver {
if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) {
if let Some(ref_match) = compute_ref_match(ctx.completion, ty) {
@@ -140,11 +152,19 @@ pub(crate) fn render_field(
}
}
}
- item.build()
+ item.doc_aliases(ctx.doc_aliases);
+ item.build(db)
}
-fn field_with_receiver(receiver: Option<&hir::Name>, field_name: &str) -> SmolStr {
- receiver.map_or_else(|| field_name.into(), |receiver| format!("{receiver}.{field_name}").into())
+fn field_with_receiver(
+ db: &RootDatabase,
+ receiver: Option<&hir::Name>,
+ field_name: &str,
+) -> SmolStr {
+ receiver.map_or_else(
+ || field_name.into(),
+ |receiver| format!("{}.{field_name}", receiver.display(db)).into(),
+ )
}
pub(crate) fn render_tuple_field(
@@ -156,10 +176,10 @@ pub(crate) fn render_tuple_field(
let mut item = CompletionItem::new(
SymbolKind::Field,
ctx.source_range(),
- field_with_receiver(receiver.as_ref(), &field.to_string()),
+ field_with_receiver(ctx.db(), receiver.as_ref(), &field.to_string()),
);
item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string());
- item.build()
+ item.build(ctx.db())
}
pub(crate) fn render_type_inference(
@@ -169,7 +189,7 @@ pub(crate) fn render_type_inference(
let mut builder =
CompletionItem::new(CompletionItemKind::InferredType, ctx.source_range(), ty_string);
builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() });
- builder.build()
+ builder.build(ctx.db)
}
pub(crate) fn render_path_resolution(
@@ -197,7 +217,9 @@ pub(crate) fn render_resolution_with_import(
) -> Option<Builder> {
let resolution = ScopeDef::from(import_edit.original_item);
let local_name = scope_def_to_name(resolution, &ctx, &import_edit)?;
-
+ //this now just renders the alias text, but we need to find the aliases earlier and call this with the alias instead
+ let doc_aliases = ctx.completion.doc_aliases_in_scope(resolution);
+ let ctx = ctx.doc_aliases(doc_aliases);
Some(render_resolution_path(ctx, path_ctx, local_name, Some(import_edit), resolution))
}
@@ -305,7 +327,7 @@ fn render_resolution_path(
item.lookup_by(name.clone())
.label(SmolStr::from_iter([&name, "<…>"]))
.trigger_call_info()
- .insert_snippet(cap, format!("{local_name}<$0>"));
+ .insert_snippet(cap, format!("{}<$0>", local_name.display(db)));
}
}
}
@@ -348,6 +370,8 @@ fn render_resolution_simple_(
if let Some(import_to_add) = ctx.import_to_add {
item.add_import(import_to_add);
}
+
+ item.doc_aliases(ctx.doc_aliases);
item
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
index 70b19988c..3c73983c3 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs
@@ -29,5 +29,5 @@ fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem>
}
item.insert_text(escaped_name);
- Some(item.build())
+ Some(item.build(ctx.db()))
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
index 197592e78..8afce8db5 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs
@@ -52,8 +52,13 @@ fn render(
let (call, escaped_call) = match &func_kind {
FuncKind::Method(_, Some(receiver)) => (
- format!("{}.{}", receiver.unescaped(), name.unescaped()).into(),
- format!("{receiver}.{name}").into(),
+ format!(
+ "{}.{}",
+ receiver.unescaped().display(ctx.db()),
+ name.unescaped().display(ctx.db())
+ )
+ .into(),
+ format!("{}.{}", receiver.display(ctx.db()), name.display(ctx.db())).into(),
),
_ => (name.unescaped().to_smol_str(), name.to_smol_str()),
};
@@ -147,6 +152,8 @@ fn render(
}
}
}
+
+ item.doc_aliases(ctx.doc_aliases);
item
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
index ed78fcd8e..728d236df 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
@@ -71,8 +71,10 @@ fn render(
}
None => (name.clone().into(), name.into(), false),
};
- let (qualified_name, escaped_qualified_name) =
- (qualified_name.unescaped().to_string(), qualified_name.to_string());
+ let (qualified_name, escaped_qualified_name) = (
+ qualified_name.unescaped().display(ctx.db()).to_string(),
+ qualified_name.display(ctx.db()).to_string(),
+ );
let snippet_cap = ctx.snippet_cap();
let mut rendered = match kind {
@@ -98,7 +100,7 @@ fn render(
}
let label = format_literal_label(&qualified_name, kind, snippet_cap);
let lookup = if qualified {
- format_literal_lookup(&short_qualified_name.to_string(), kind)
+ format_literal_lookup(&short_qualified_name.display(ctx.db()).to_string(), kind)
} else {
format_literal_lookup(&qualified_name, kind)
};
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
index 44e886076..ce7af1d34 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
@@ -74,7 +74,7 @@ fn render(
item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(&name));
}
_ => {
- cov_mark::hit!(dont_insert_macro_call_parens_unncessary);
+ cov_mark::hit!(dont_insert_macro_call_parens_unnecessary);
item.insert_text(escaped_name);
}
};
@@ -140,8 +140,8 @@ mod tests {
use crate::tests::check_edit;
#[test]
- fn dont_insert_macro_call_parens_unncessary() {
- cov_mark::check!(dont_insert_macro_call_parens_unncessary);
+ fn dont_insert_macro_call_parens_unnecessary() {
+ cov_mark::check!(dont_insert_macro_call_parens_unnecessary);
check_edit(
"frobnicate",
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
index 9225c91be..d06abc5e9 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
@@ -57,7 +57,10 @@ pub(crate) fn render_variant_pat(
let enum_ty = variant.parent_enum(ctx.db()).ty(ctx.db());
let (name, escaped_name) = match path {
- Some(path) => (path.unescaped().to_string().into(), path.to_string().into()),
+ Some(path) => (
+ path.unescaped().display(ctx.db()).to_string().into(),
+ path.display(ctx.db()).to_string().into(),
+ ),
None => {
let name = local_name.unwrap_or_else(|| variant.name(ctx.db()));
(name.unescaped().to_smol_str(), name.to_smol_str())
@@ -121,7 +124,7 @@ fn build_completion(
Some(snippet_cap) => item.insert_snippet(snippet_cap, pat),
None => item.insert_text(pat),
};
- item.build()
+ item.build(ctx.db())
}
fn render_pat(
@@ -172,7 +175,7 @@ fn render_record_as_pat(
format!(
"{name} {{ {}{} }}",
fields.enumerate().format_with(", ", |(idx, field), f| {
- f(&format_args!("{}${}", field.name(db), idx + 1))
+ f(&format_args!("{}${}", field.name(db).display(db.upcast()), idx + 1))
}),
if fields_omitted { ", .." } else { "" },
name = name
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
index fbe120d2a..343ba7e28 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs
@@ -53,5 +53,5 @@ fn render(
}
item.insert_text(escaped_name);
- Some(item.build())
+ Some(item.build(ctx.db()))
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
index 6e0c53ec9..93e943dbe 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs
@@ -21,8 +21,10 @@ pub(crate) fn render_union_literal(
let name = local_name.unwrap_or_else(|| un.name(ctx.db()));
let (qualified_name, escaped_qualified_name) = match path {
- Some(p) => (p.unescaped().to_string(), p.to_string()),
- None => (name.unescaped().to_string(), name.to_string()),
+ Some(p) => (p.unescaped().display(ctx.db()).to_string(), p.display(ctx.db()).to_string()),
+ None => {
+ (name.unescaped().display(ctx.db()).to_string(), name.display(ctx.db()).to_string())
+ }
};
let label = format_literal_label(&name.to_smol_str(), StructKind::Record, ctx.snippet_cap());
let lookup = format_literal_lookup(&name.to_smol_str(), StructKind::Record);
@@ -51,9 +53,9 @@ pub(crate) fn render_union_literal(
format!(
"{} {{ {} }}",
escaped_qualified_name,
- fields
- .iter()
- .format_with(", ", |field, f| { f(&format_args!("{}: ()", field.name(ctx.db()))) })
+ fields.iter().format_with(", ", |field, f| {
+ f(&format_args!("{}: ()", field.name(ctx.db()).display(ctx.db())))
+ })
)
};
@@ -61,7 +63,11 @@ pub(crate) fn render_union_literal(
"{} {{ {}{} }}",
qualified_name,
fields.iter().format_with(", ", |field, f| {
- f(&format_args!("{}: {}", field.name(ctx.db()), field.ty(ctx.db()).display(ctx.db())))
+ f(&format_args!(
+ "{}: {}",
+ field.name(ctx.db()).display(ctx.db()),
+ field.ty(ctx.db()).display(ctx.db())
+ ))
}),
if fields_omitted { ", .." } else { "" }
);
@@ -76,5 +82,5 @@ pub(crate) fn render_union_literal(
None => item.insert_text(literal),
};
- Some(item.build())
+ Some(item.build(ctx.db()))
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
index 55c55725b..a9a01a3a3 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/variant.rs
@@ -27,14 +27,14 @@ pub(crate) fn render_record_lit(
}
let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| {
if snippet_cap.is_some() {
- f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1))
+ f(&format_args!("{}: ${{{}:()}}", field.name(db).display(db.upcast()), idx + 1))
} else {
- f(&format_args!("{}: ()", field.name(db)))
+ f(&format_args!("{}: ()", field.name(db).display(db.upcast())))
}
});
let types = fields.iter().format_with(", ", |field, f| {
- f(&format_args!("{}: {}", field.name(db), field.ty(db).display(db)))
+ f(&format_args!("{}: {}", field.name(db).display(db.upcast()), field.ty(db).display(db)))
});
RenderedLiteral {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
index 1fe48b9e9..2464e8d5f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
@@ -23,7 +23,8 @@ mod type_pos;
mod use_tree;
mod visibility;
-use hir::{db::DefDatabase, PrefixKind};
+use expect_test::Expect;
+use hir::PrefixKind;
use ide_db::{
base_db::{fixture::ChangeFixture, FileLoader, FilePosition},
imports::insert_use::{ImportGranularity, InsertUseConfig},
@@ -104,7 +105,7 @@ fn completion_list_with_config(
include_keywords: bool,
trigger_character: Option<char>,
) -> String {
- // filter out all but one builtintype completion for smaller test outputs
+ // filter out all but one built-in type completion for smaller test outputs
let items = get_all_items(config, ra_fixture, trigger_character);
let items = items
.into_iter()
@@ -120,7 +121,7 @@ fn completion_list_with_config(
pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
let change_fixture = ChangeFixture::parse(ra_fixture);
let mut database = RootDatabase::default();
- database.set_enable_proc_attr_macros(true);
+ database.enable_proc_attr_macros();
database.apply_change(change_fixture.change);
let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
let offset = range_or_offset.expect_offset();
@@ -197,11 +198,11 @@ pub(crate) fn check_edit_with_config(
&db,
&config,
position,
- completion.import_to_add.iter().filter_map(|import_edit| {
- let import_path = &import_edit.import_path;
- let import_name = import_path.segments().last()?;
- Some((import_path.to_string(), import_name.to_string()))
- }),
+ completion
+ .import_to_add
+ .iter()
+ .cloned()
+ .filter_map(|(import_path, import_name)| Some((import_path, import_name))),
)
.into_iter()
.flatten()
@@ -215,6 +216,11 @@ pub(crate) fn check_edit_with_config(
assert_eq_text!(&ra_fixture_after, &actual)
}
+fn check_empty(ra_fixture: &str, expect: Expect) {
+ let actual = completion_list(ra_fixture);
+ expect.assert_eq(&actual);
+}
+
pub(crate) fn get_all_items(
config: CompletionConfig,
code: &str,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index c1c6a689e..be5b7f8a3 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -1,18 +1,13 @@
//! Completion tests for expressions.
use expect_test::{expect, Expect};
-use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
expect.assert_eq(&actual)
}
-fn check_empty(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual);
-}
-
#[test]
fn complete_literal_struct_with_a_private_field() {
// `FooDesc.bar` is private, the completion should not be triggered.
@@ -672,7 +667,7 @@ fn main() {
}
#[test]
-fn varaiant_with_struct() {
+fn variant_with_struct() {
check_empty(
r#"
pub struct YoloVariant {
@@ -997,3 +992,105 @@ fn foo() { if foo {} el$0 { let x = 92; } }
"#]],
);
}
+
+#[test]
+fn expr_no_unstable_item_on_stable() {
+ check_empty(
+ r#"
+//- /main.rs crate:main deps:std
+use std::*;
+fn main() {
+ $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct UnstableThisShouldNotBeListed;
+"#,
+ expect![[r#"
+ fn main() fn()
+ md std
+ bt u32
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}
+
+#[test]
+fn expr_unstable_item_on_nightly() {
+ check_empty(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+fn main() {
+ $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct UnstableButWeAreOnNightlyAnyway;
+"#,
+ expect![[r#"
+ fn main() fn()
+ md std
+ st UnstableButWeAreOnNightlyAnyway
+ bt u32
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
index 0b485eb77..8c038c0fb 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
@@ -1108,6 +1108,41 @@ fn function() {
}
#[test]
+fn flyimport_pattern_no_unstable_item_on_stable() {
+ check(
+ r#"
+//- /main.rs crate:main deps:std
+fn function() {
+ let foo$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct FooStruct {}
+"#,
+ expect![""],
+ );
+}
+
+#[test]
+fn flyimport_pattern_unstable_item_on_nightly() {
+ check(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+fn function() {
+ let foo$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct FooStruct {}
+"#,
+ expect![[r#"
+ st FooStruct (use std::FooStruct)
+ "#]],
+ );
+}
+
+#[test]
fn flyimport_item_name() {
check(
r#"
@@ -1230,3 +1265,24 @@ macro_rules! define_struct {
"#]],
);
}
+
+#[test]
+fn macro_use_prelude_is_in_scope() {
+ check(
+ r#"
+//- /main.rs crate:main deps:dep
+#[macro_use]
+extern crate dep;
+
+fn main() {
+ print$0
+}
+//- /lib.rs crate:dep
+#[macro_export]
+macro_rules! println {
+ () => {}
+}
+"#,
+ expect![""],
+ )
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
index 9fc731bb1..2b5b4dd77 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
@@ -1,7 +1,7 @@
//! Completion tests for item list position.
use expect_test::{expect, Expect};
-use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}{ra_fixture}"));
@@ -298,6 +298,58 @@ impl Test for () {
}
#[test]
+fn in_trait_impl_no_unstable_item_on_stable() {
+ check_empty(
+ r#"
+trait Test {
+ #[unstable]
+ type Type;
+ #[unstable]
+ const CONST: ();
+ #[unstable]
+ fn function();
+}
+
+impl Test for () {
+ $0
+}
+"#,
+ expect![[r#"
+ kw crate::
+ kw self::
+ "#]],
+ );
+}
+
+#[test]
+fn in_trait_impl_unstable_item_on_nightly() {
+ check_empty(
+ r#"
+//- toolchain:nightly
+trait Test {
+ #[unstable]
+ type Type;
+ #[unstable]
+ const CONST: ();
+ #[unstable]
+ fn function();
+}
+
+impl Test for () {
+ $0
+}
+"#,
+ expect![[r#"
+ ct const CONST: () =
+ fn fn function()
+ ta type Type =
+ kw crate::
+ kw self::
+ "#]],
+ );
+}
+
+#[test]
fn after_unit_struct() {
check(
r#"struct S; f$0"#,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
index c0e485c36..8af6cce98 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
@@ -1,12 +1,7 @@
//! Completion tests for pattern position.
use expect_test::{expect, Expect};
-use crate::tests::{check_edit, completion_list, BASE_ITEMS_FIXTURE};
-
-fn check_empty(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture);
- expect.assert_eq(&actual)
-}
+use crate::tests::{check_edit, check_empty, completion_list, BASE_ITEMS_FIXTURE};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
@@ -742,3 +737,56 @@ fn f(x: EnumAlias<u8>) {
"#]],
);
}
+
+#[test]
+fn pat_no_unstable_item_on_stable() {
+ check_empty(
+ r#"
+//- /main.rs crate:main deps:std
+use std::*;
+fn foo() {
+ let a$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+#[unstable]
+pub enum Enum {
+ Variant
+}
+"#,
+ expect![[r#"
+ md std
+ kw mut
+ kw ref
+ "#]],
+ );
+}
+
+#[test]
+fn pat_unstable_item_on_nightly() {
+ check_empty(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+fn foo() {
+ let a$0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+#[unstable]
+pub enum Enum {
+ Variant
+}
+"#,
+ expect![[r#"
+ en Enum
+ md std
+ st S
+ kw mut
+ kw ref
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
index 2656a4d54..789ad6634 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/predicate.rs
@@ -1,7 +1,7 @@
//! Completion tests for predicates and bounds.
use expect_test::{expect, Expect};
-use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
@@ -129,3 +129,43 @@ impl Record {
"#]],
);
}
+
+#[test]
+fn pred_no_unstable_item_on_stable() {
+ check_empty(
+ r#"
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo<T> where T: $0 {}
+//- /std.rs crate:std
+#[unstable]
+pub trait Trait {}
+"#,
+ expect![[r#"
+ md std
+ kw crate::
+ kw self::
+ "#]],
+ );
+}
+
+#[test]
+fn pred_unstable_item_on_nightly() {
+ check_empty(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo<T> where T: $0 {}
+//- /std.rs crate:std
+#[unstable]
+pub trait Trait {}
+"#,
+ expect![[r#"
+ md std
+ tt Trait
+ kw crate::
+ kw self::
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs
index 92ea4d15b..2d6234e31 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/proc_macros.rs
@@ -81,7 +81,7 @@ impl Foo {
}
#[proc_macros::input_replace(
- fn suprise() {
+ fn surprise() {
Foo.$0
}
)]
@@ -114,7 +114,7 @@ impl Foo {
}
#[proc_macros::input_replace(
- fn suprise() {
+ fn surprise() {
Foo.f$0
}
)]
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs
index f8a6f6cd3..382472083 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs
@@ -989,3 +989,294 @@ fn foo { crate::::$0 }
expect![""],
)
}
+
+#[test]
+fn completes_struct_via_doc_alias_in_fn_body() {
+ check(
+ r#"
+#[doc(alias = "Bar")]
+struct Foo;
+
+fn here_we_go() {
+ $0
+}
+"#,
+ expect![[r#"
+ fn here_we_go() fn()
+ st Foo (alias Bar)
+ bt u32
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}
+
+#[test]
+fn completes_struct_via_multiple_doc_aliases_in_fn_body() {
+ check(
+ r#"
+#[doc(alias("Bar", "Qux"))]
+#[doc(alias = "Baz")]
+struct Foo;
+
+fn here_we_go() {
+ B$0
+}
+"#,
+ expect![[r#"
+ fn here_we_go() fn()
+ st Foo (alias Bar, Qux, Baz)
+ bt u32
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}
+
+#[test]
+fn completes_field_name_via_doc_alias_in_fn_body() {
+ check(
+ r#"
+struct Foo {
+ #[doc(alias = "qux")]
+ bar: u8
+};
+
+fn here_we_go() {
+ let foo = Foo { q$0 }
+}
+"#,
+ expect![[r#"
+ fd bar (alias qux) u8
+ "#]],
+ );
+}
+
+#[test]
+fn completes_struct_fn_name_via_doc_alias_in_fn_body() {
+ check(
+ r#"
+struct Foo;
+impl Foo {
+ #[doc(alias = "qux")]
+ fn bar() -> u8 { 1 }
+}
+
+fn here_we_go() {
+ Foo::q$0
+}
+"#,
+ expect![[r#"
+ fn bar() (alias qux) fn() -> u8
+ "#]],
+ );
+}
+
+#[test]
+fn completes_method_name_via_doc_alias_in_fn_body() {
+ check(
+ r#"
+struct Foo {
+ bar: u8
+}
+impl Foo {
+ #[doc(alias = "qux")]
+ fn baz(&self) -> u8 {
+ self.bar
+ }
+}
+
+fn here_we_go() {
+ let foo = Foo { field: 42 };
+ foo.q$0
+}
+"#,
+ expect![[r#"
+ fd bar u8
+ me baz() (alias qux) fn(&self) -> u8
+ sn box Box::new(expr)
+ sn call function(expr)
+ sn dbg dbg!(expr)
+ sn dbgr dbg!(&expr)
+ sn let let
+ sn letm let mut
+ sn match match expr {}
+ sn ref &expr
+ sn refm &mut expr
+ sn unsafe unsafe {}
+ "#]],
+ );
+}
+
+#[test]
+fn completes_fn_name_via_doc_alias_in_fn_body() {
+ check(
+ r#"
+#[doc(alias = "qux")]
+fn foo() {}
+fn bar() { qu$0 }
+"#,
+ expect![[r#"
+ fn bar() fn()
+ fn foo() (alias qux) fn()
+ bt u32
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw let
+ kw loop
+ kw match
+ kw mod
+ kw return
+ kw self::
+ kw static
+ kw struct
+ kw trait
+ kw true
+ kw type
+ kw union
+ kw unsafe
+ kw use
+ kw while
+ kw while let
+ sn macro_rules
+ sn pd
+ sn ppd
+ "#]],
+ );
+}
+
+#[test]
+fn completes_struct_name_via_doc_alias_in_another_mod() {
+ check(
+ r#"
+mod foo {
+ #[doc(alias = "Qux")]
+ pub struct Bar(u8);
+}
+
+fn here_we_go() {
+ use foo;
+ let foo = foo::Q$0
+}
+"#,
+ expect![[r#"
+ st Bar (alias Qux)
+ "#]],
+ );
+}
+
+#[test]
+fn completes_use_via_doc_alias_in_another_mod() {
+ check(
+ r#"
+mod foo {
+ #[doc(alias = "Qux")]
+ pub struct Bar(u8);
+}
+
+fn here_we_go() {
+ use foo::Q$0;
+}
+"#,
+ expect![[r#"
+ st Bar (alias Qux)
+ "#]],
+ );
+}
+
+#[test]
+fn completes_flyimport_with_doc_alias_in_another_mod() {
+ check(
+ r#"
+mod foo {
+ #[doc(alias = "Qux")]
+ pub struct Bar();
+}
+
+fn here_we_go() {
+ let foo = Bar$0
+}
+"#,
+ expect![[r#"
+ fn here_we_go() fn()
+ md foo
+ st Bar (alias Qux) (use foo::Bar)
+ bt u32
+ kw crate::
+ kw false
+ kw for
+ kw if
+ kw if let
+ kw loop
+ kw match
+ kw return
+ kw self::
+ kw true
+ kw unsafe
+ kw while
+ kw while let
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
index c3f4fb4d1..8cb1ff4a1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
@@ -1,7 +1,7 @@
//! Completion tests for type position.
use expect_test::{expect, Expect};
-use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};
+use crate::tests::{check_empty, completion_list, BASE_ITEMS_FIXTURE};
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(&format!("{BASE_ITEMS_FIXTURE}\n{ra_fixture}"));
@@ -669,3 +669,53 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
"#]],
);
}
+
+#[test]
+fn type_pos_no_unstable_type_on_stable() {
+ check_empty(
+ r#"
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo {
+ f: $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+"#,
+ expect![[r#"
+ md std
+ sp Self
+ st Foo
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ )
+}
+
+#[test]
+fn type_pos_unstable_type_on_nightly() {
+ check_empty(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+struct Foo {
+ f: $0
+}
+//- /std.rs crate:std
+#[unstable]
+pub struct S;
+"#,
+ expect![[r#"
+ md std
+ sp Self
+ st Foo
+ st S
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ )
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs
index 037d7dce5..4c74dba52 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs
@@ -382,3 +382,50 @@ use self::foo::impl$0
"#]],
);
}
+
+#[test]
+fn use_tree_no_unstable_items_on_stable() {
+ check(
+ r#"
+//- /lib.rs crate:main deps:std
+use std::$0
+//- /std.rs crate:std
+#[unstable]
+pub mod simd {}
+#[unstable]
+pub struct S;
+#[unstable]
+pub fn foo() {}
+#[unstable]
+#[macro_export]
+marco_rules! m { () => {} }
+"#,
+ expect![""],
+ );
+}
+
+#[test]
+fn use_tree_unstable_items_on_nightly() {
+ check(
+ r#"
+//- toolchain:nightly
+//- /lib.rs crate:main deps:std
+use std::$0
+//- /std.rs crate:std
+#[unstable]
+pub mod simd {}
+#[unstable]
+pub struct S;
+#[unstable]
+pub fn foo() {}
+#[unstable]
+#[macro_export]
+marco_rules! m { () => {} }
+"#,
+ expect![[r#"
+ fn foo fn()
+ md simd
+ st S
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
index 57daaf623..4e75dc4db 100644
--- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
@@ -23,6 +23,8 @@ itertools = "0.10.5"
arrayvec = "0.7.2"
indexmap = "1.9.1"
memchr = "2.5.0"
+triomphe.workspace = true
+nohash-hasher.workspace = true
# local deps
base-db.workspace = true
@@ -36,6 +38,8 @@ text-edit.workspace = true
# something from some `hir-xxx` subpackage, reexport the API via `hir`.
hir.workspace = true
+line-index.workspace = true
+
[dev-dependencies]
expect-test = "1.4.0"
oorandom = "11.1.3"
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
index ea1d9cc49..0dd544d0a 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
@@ -1,13 +1,15 @@
//! Applies changes to the IDE state transactionally.
-use std::sync::Arc;
-
use base_db::{
- salsa::{Database, Durability},
+ salsa::{
+ debug::{DebugQueryTable, TableEntry},
+ Database, Durability, Query, QueryTable,
+ },
Change, SourceRootId,
};
use profile::{memory_usage, Bytes};
use rustc_hash::FxHashSet;
+use triomphe::Arc;
use crate::{symbol_index::SymbolsDatabase, RootDatabase};
@@ -48,22 +50,44 @@ impl RootDatabase {
// | VS Code | **rust-analyzer: Memory Usage (Clears Database)**
// |===
// image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[]
- pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
- let mut acc: Vec<(String, Bytes)> = vec![];
+ pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes, usize)> {
+ let mut acc: Vec<(String, Bytes, usize)> = vec![];
+
+ fn collect_query_count<'q, Q>(table: &QueryTable<'q, Q>) -> usize
+ where
+ QueryTable<'q, Q>: DebugQueryTable,
+ Q: Query,
+ <Q as Query>::Storage: 'q,
+ {
+ struct EntryCounter(usize);
+ impl<K, V> FromIterator<TableEntry<K, V>> for EntryCounter {
+ fn from_iter<T>(iter: T) -> EntryCounter
+ where
+ T: IntoIterator<Item = TableEntry<K, V>>,
+ {
+ EntryCounter(iter.into_iter().count())
+ }
+ }
+ table.entries::<EntryCounter>().0
+ }
+
macro_rules! purge_each_query {
($($q:path)*) => {$(
let before = memory_usage().allocated;
- $q.in_db(self).purge();
+ let table = $q.in_db(self);
+ let count = collect_query_count(&table);
+ table.purge();
let after = memory_usage().allocated;
let q: $q = Default::default();
let name = format!("{:?}", q);
- acc.push((name, before - after));
+ acc.push((name, before - after, count));
)*}
}
purge_each_query![
// SourceDatabase
base_db::ParseQuery
base_db::CrateGraphQuery
+ base_db::ProcMacrosQuery
// SourceDatabaseExt
base_db::FileTextQuery
@@ -79,7 +103,6 @@ impl RootDatabase {
hir::db::MacroDefQuery
hir::db::MacroExpandQuery
hir::db::ExpandProcMacroQuery
- hir::db::MacroExpandErrorQuery
hir::db::HygieneFrameQuery
// DefDatabase
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/assists.rs b/src/tools/rust-analyzer/crates/ide-db/src/assists.rs
index 8c6c1c44a..7a7328f31 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/assists.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/assists.rs
@@ -98,7 +98,7 @@ impl FromStr for AssistKind {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AssistId(pub &'static str, pub AssistKind);
-/// A way to control how many asssist to resolve during the assist resolution.
+/// A way to control how many assist to resolve during the assist resolution.
/// When an assist is resolved, its edits are calculated that might be costly to always do by default.
#[derive(Debug)]
pub enum AssistResolveStrategy {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index 4071c490b..760834bfa 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -8,8 +8,8 @@
use arrayvec::ArrayVec;
use hir::{
Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, Field,
- Function, GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef,
- Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant,
+ Function, GenericParam, HasVisibility, Impl, Label, Local, Macro, Module, ModuleDef, Name,
+ PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant,
Visibility,
};
use stdx::impl_from;
@@ -622,22 +622,3 @@ impl From<ModuleDef> for Definition {
}
}
}
-
-impl From<Definition> for Option<ItemInNs> {
- fn from(def: Definition) -> Self {
- let item = match def {
- Definition::Module(it) => ModuleDef::Module(it),
- Definition::Function(it) => ModuleDef::Function(it),
- Definition::Adt(it) => ModuleDef::Adt(it),
- Definition::Variant(it) => ModuleDef::Variant(it),
- Definition::Const(it) => ModuleDef::Const(it),
- Definition::Static(it) => ModuleDef::Static(it),
- Definition::Trait(it) => ModuleDef::Trait(it),
- Definition::TraitAlias(it) => ModuleDef::TraitAlias(it),
- Definition::TypeAlias(it) => ModuleDef::TypeAlias(it),
- Definition::BuiltinType(it) => ModuleDef::BuiltinType(it),
- _ => return None,
- };
- Some(ItemInNs::from(item))
- }
-}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
index f0c369096..e488300b4 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
@@ -4230,7 +4230,7 @@ pub union GenericUnion<T: Copy> { // Unions with non-`Copy` fields are unstable.
pub const THIS_IS_OKAY: GenericUnion<()> = GenericUnion { field: () };
```
-Like transarent `struct`s, a transparent `union` of type `U` has the same
+Like transparent `struct`s, a transparent `union` of type `U` has the same
layout, size, and ABI as its single non-ZST field. If it is generic over a type
`T`, and all its fields are ZSTs except for exactly one field of type `T`, then
it has the same layout and ABI as `T` (even if `T` is a ZST when monomorphized).
@@ -6548,7 +6548,7 @@ subtracting elements in an Add impl."##,
},
Lint {
label: "clippy::suspicious_assignment_formatting",
- description: r##"Checks for use of the non-existent `=*`, `=!` and `=-`
+ description: r##"Checks for use of the nonexistent `=*`, `=!` and `=-`
operators."##,
},
Lint {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
index 8e3b1eef1..eba9d8afc 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
@@ -77,7 +77,7 @@ pub fn visit_file_defs(
}
module.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into()));
- let is_root = module.is_crate_root(db);
+ let is_root = module.is_crate_root();
module
.legacy_macros(db)
.into_iter()
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
index b26b0a908..901d592c6 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
@@ -362,12 +362,12 @@ fn import_for_item(
let original_item_candidate = item_for_path_search(db, original_item)?;
let import_path_candidate = mod_path(original_item_candidate)?;
- let import_path_string = import_path_candidate.to_string();
+ let import_path_string = import_path_candidate.display(db).to_string();
let expected_import_end = if item_as_assoc(db, original_item).is_some() {
unresolved_qualifier.to_string()
} else {
- format!("{unresolved_qualifier}::{}", item_name(db, original_item)?)
+ format!("{unresolved_qualifier}::{}", item_name(db, original_item)?.display(db))
};
if !import_path_string.contains(unresolved_first_segment)
|| !import_path_string.ends_with(&expected_import_end)
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
index 07a57c883..46f1353e2 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
@@ -5,17 +5,11 @@
use either::Either;
use hir::{
import_map::{self, ImportKind},
- symbols::FileSymbol,
AsAssocItem, Crate, ItemInNs, Semantics,
};
use limit::Limit;
-use syntax::{ast, AstNode, SyntaxKind::NAME};
-use crate::{
- defs::{Definition, NameClass},
- imports::import_assets::NameToImport,
- symbol_index, RootDatabase,
-};
+use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase};
/// A value to use, when uncertain which limit to pick.
pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(40);
@@ -115,12 +109,12 @@ fn find_items<'a>(
});
// Query the local crate using the symbol index.
- let local_results = symbol_index::crate_symbols(db, krate, local_query)
+ let local_results = local_query
+ .search(&symbol_index::crate_symbols(db, krate))
.into_iter()
- .filter_map(move |local_candidate| get_name_definition(sema, &local_candidate))
- .filter_map(|name_definition_to_import| match name_definition_to_import {
- Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)),
- def => <Option<_>>::from(def),
+ .filter_map(|local_candidate| match local_candidate.def {
+ hir::ModuleDef::Macro(macro_def) => Some(ItemInNs::Macros(macro_def)),
+ def => Some(ItemInNs::from(def)),
});
external_importables.chain(local_results).filter(move |&item| match assoc_item_search {
@@ -130,22 +124,6 @@ fn find_items<'a>(
})
}
-fn get_name_definition(
- sema: &Semantics<'_, RootDatabase>,
- import_candidate: &FileSymbol,
-) -> Option<Definition> {
- let _p = profile::span("get_name_definition");
-
- let candidate_node = import_candidate.loc.syntax(sema)?;
- let candidate_name_node = if candidate_node.kind() != NAME {
- candidate_node.children().find(|it| it.kind() == NAME)?
- } else {
- candidate_node
- };
- let name = ast::Name::cast(candidate_name_node)?;
- NameClass::classify(sema, &name)?.defined()
-}
-
fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)).is_some()
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
index b1df11bf9..ff1a20f03 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
@@ -13,7 +13,6 @@ pub mod famous_defs;
pub mod helpers;
pub mod items_locator;
pub mod label;
-pub mod line_index;
pub mod path_transform;
pub mod rename;
pub mod rust_doc;
@@ -43,21 +42,20 @@ pub mod syntax_helpers {
pub use parser::LexedStr;
}
-use std::{fmt, mem::ManuallyDrop, sync::Arc};
+use std::{fmt, mem::ManuallyDrop};
use base_db::{
salsa::{self, Durability},
AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast,
};
-use hir::{
- db::{DefDatabase, ExpandDatabase, HirDatabase},
- symbols::FileSymbolKind,
-};
-use stdx::hash::NoHashHashSet;
+use hir::db::{DefDatabase, ExpandDatabase, HirDatabase};
+use triomphe::Arc;
use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
+pub use ::line_index;
+
/// `base_db` is normally also needed in places where `ide_db` is used, so this re-export is for convenience.
pub use base_db;
@@ -114,13 +112,13 @@ impl Upcast<dyn HirDatabase> for RootDatabase {
}
impl FileLoader for RootDatabase {
- fn file_text(&self, file_id: FileId) -> Arc<String> {
+ fn file_text(&self, file_id: FileId) -> Arc<str> {
FileLoaderDelegate(self).file_text(file_id)
}
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId> {
FileLoaderDelegate(self).resolve_path(path)
}
- fn relevant_crates(&self, file_id: FileId) -> Arc<NoHashHashSet<CrateId>> {
+ fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
FileLoaderDelegate(self).relevant_crates(file_id)
}
}
@@ -137,18 +135,186 @@ impl RootDatabase {
pub fn new(lru_capacity: Option<usize>) -> RootDatabase {
let mut db = RootDatabase { storage: ManuallyDrop::new(salsa::Storage::default()) };
db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
+ db.set_proc_macros_with_durability(Default::default(), Durability::HIGH);
db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
db.set_library_roots_with_durability(Default::default(), Durability::HIGH);
- db.set_enable_proc_attr_macros(false);
- db.update_lru_capacity(lru_capacity);
+ db.set_expand_proc_attr_macros_with_durability(false, Durability::HIGH);
+ db.update_parse_query_lru_capacity(lru_capacity);
db
}
- pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
- let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_LRU_CAP);
+ pub fn enable_proc_attr_macros(&mut self) {
+ self.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
+ }
+
+ pub fn update_parse_query_lru_capacity(&mut self, lru_capacity: Option<usize>) {
+ let lru_capacity = lru_capacity.unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP);
base_db::ParseQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
- hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
- hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(lru_capacity);
+ // macro expansions are usually rather small, so we can afford to keep more of them alive
+ hir::db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity);
+ hir::db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(4 * lru_capacity);
+ }
+
+ pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) {
+ use hir::db as hir_db;
+
+ base_db::ParseQuery.in_db_mut(self).set_lru_capacity(
+ lru_capacities
+ .get(stringify!(ParseQuery))
+ .copied()
+ .unwrap_or(base_db::DEFAULT_PARSE_LRU_CAP),
+ );
+ hir_db::ParseMacroExpansionQuery.in_db_mut(self).set_lru_capacity(
+ lru_capacities
+ .get(stringify!(ParseMacroExpansionQuery))
+ .copied()
+ .unwrap_or(4 * base_db::DEFAULT_PARSE_LRU_CAP),
+ );
+ hir_db::MacroExpandQuery.in_db_mut(self).set_lru_capacity(
+ lru_capacities
+ .get(stringify!(MacroExpandQuery))
+ .copied()
+ .unwrap_or(4 * base_db::DEFAULT_PARSE_LRU_CAP),
+ );
+
+ macro_rules! update_lru_capacity_per_query {
+ ($( $module:ident :: $query:ident )*) => {$(
+ if let Some(&cap) = lru_capacities.get(stringify!($query)) {
+ $module::$query.in_db_mut(self).set_lru_capacity(cap);
+ }
+ )*}
+ }
+ update_lru_capacity_per_query![
+ // SourceDatabase
+ // base_db::ParseQuery
+ // base_db::CrateGraphQuery
+ // base_db::ProcMacrosQuery
+
+ // SourceDatabaseExt
+ // base_db::FileTextQuery
+ // base_db::FileSourceRootQuery
+ // base_db::SourceRootQuery
+ base_db::SourceRootCratesQuery
+
+ // ExpandDatabase
+ hir_db::AstIdMapQuery
+ // hir_db::ParseMacroExpansionQuery
+ // hir_db::InternMacroCallQuery
+ hir_db::MacroArgTextQuery
+ hir_db::MacroDefQuery
+ // hir_db::MacroExpandQuery
+ hir_db::ExpandProcMacroQuery
+ hir_db::HygieneFrameQuery
+ hir_db::ParseMacroExpansionErrorQuery
+
+ // DefDatabase
+ hir_db::FileItemTreeQuery
+ hir_db::CrateDefMapQueryQuery
+ hir_db::BlockDefMapQuery
+ hir_db::StructDataQuery
+ hir_db::StructDataWithDiagnosticsQuery
+ hir_db::UnionDataQuery
+ hir_db::UnionDataWithDiagnosticsQuery
+ hir_db::EnumDataQuery
+ hir_db::EnumDataWithDiagnosticsQuery
+ hir_db::ImplDataQuery
+ hir_db::ImplDataWithDiagnosticsQuery
+ hir_db::TraitDataQuery
+ hir_db::TraitDataWithDiagnosticsQuery
+ hir_db::TraitAliasDataQuery
+ hir_db::TypeAliasDataQuery
+ hir_db::FunctionDataQuery
+ hir_db::ConstDataQuery
+ hir_db::StaticDataQuery
+ hir_db::Macro2DataQuery
+ hir_db::MacroRulesDataQuery
+ hir_db::ProcMacroDataQuery
+ hir_db::BodyWithSourceMapQuery
+ hir_db::BodyQuery
+ hir_db::ExprScopesQuery
+ hir_db::GenericParamsQuery
+ hir_db::VariantsAttrsQuery
+ hir_db::FieldsAttrsQuery
+ hir_db::VariantsAttrsSourceMapQuery
+ hir_db::FieldsAttrsSourceMapQuery
+ hir_db::AttrsQuery
+ hir_db::CrateLangItemsQuery
+ hir_db::LangItemQuery
+ hir_db::ImportMapQuery
+ hir_db::FieldVisibilitiesQuery
+ hir_db::FunctionVisibilityQuery
+ hir_db::ConstVisibilityQuery
+ hir_db::CrateSupportsNoStdQuery
+
+ // HirDatabase
+ hir_db::InferQueryQuery
+ hir_db::MirBodyQuery
+ hir_db::BorrowckQuery
+ hir_db::TyQuery
+ hir_db::ValueTyQuery
+ hir_db::ImplSelfTyQuery
+ hir_db::ConstParamTyQuery
+ hir_db::ConstEvalQuery
+ hir_db::ConstEvalDiscriminantQuery
+ hir_db::ImplTraitQuery
+ hir_db::FieldTypesQuery
+ hir_db::LayoutOfAdtQuery
+ hir_db::TargetDataLayoutQuery
+ hir_db::CallableItemSignatureQuery
+ hir_db::ReturnTypeImplTraitsQuery
+ hir_db::GenericPredicatesForParamQuery
+ hir_db::GenericPredicatesQuery
+ hir_db::TraitEnvironmentQuery
+ hir_db::GenericDefaultsQuery
+ hir_db::InherentImplsInCrateQuery
+ hir_db::InherentImplsInBlockQuery
+ hir_db::IncoherentInherentImplCratesQuery
+ hir_db::TraitImplsInCrateQuery
+ hir_db::TraitImplsInBlockQuery
+ hir_db::TraitImplsInDepsQuery
+ // hir_db::InternCallableDefQuery
+ // hir_db::InternLifetimeParamIdQuery
+ // hir_db::InternImplTraitIdQuery
+ // hir_db::InternTypeOrConstParamIdQuery
+ // hir_db::InternClosureQuery
+ // hir_db::InternGeneratorQuery
+ hir_db::AssociatedTyDataQuery
+ hir_db::TraitDatumQuery
+ hir_db::StructDatumQuery
+ hir_db::ImplDatumQuery
+ hir_db::FnDefDatumQuery
+ hir_db::FnDefVarianceQuery
+ hir_db::AdtVarianceQuery
+ hir_db::AssociatedTyValueQuery
+ hir_db::TraitSolveQueryQuery
+ hir_db::ProgramClausesForChalkEnvQuery
+
+ // SymbolsDatabase
+ symbol_index::ModuleSymbolsQuery
+ symbol_index::LibrarySymbolsQuery
+ // symbol_index::LocalRootsQuery
+ // symbol_index::LibraryRootsQuery
+
+ // LineIndexDatabase
+ crate::LineIndexQuery
+
+ // InternDatabase
+ // hir_db::InternFunctionQuery
+ // hir_db::InternStructQuery
+ // hir_db::InternUnionQuery
+ // hir_db::InternEnumQuery
+ // hir_db::InternConstQuery
+ // hir_db::InternStaticQuery
+ // hir_db::InternTraitQuery
+ // hir_db::InternTraitAliasQuery
+ // hir_db::InternTypeAliasQuery
+ // hir_db::InternImplQuery
+ // hir_db::InternExternBlockQuery
+ // hir_db::InternBlockQuery
+ // hir_db::InternMacro2Query
+ // hir_db::InternProcMacroQuery
+ // hir_db::InternMacroRulesQuery
+ ];
}
}
@@ -211,20 +377,22 @@ impl From<hir::MacroKind> for SymbolKind {
}
}
-impl From<FileSymbolKind> for SymbolKind {
- fn from(it: FileSymbolKind) -> Self {
+impl From<hir::ModuleDefId> for SymbolKind {
+ fn from(it: hir::ModuleDefId) -> Self {
match it {
- FileSymbolKind::Const => SymbolKind::Const,
- FileSymbolKind::Enum => SymbolKind::Enum,
- FileSymbolKind::Function => SymbolKind::Function,
- FileSymbolKind::Macro => SymbolKind::Macro,
- FileSymbolKind::Module => SymbolKind::Module,
- FileSymbolKind::Static => SymbolKind::Static,
- FileSymbolKind::Struct => SymbolKind::Struct,
- FileSymbolKind::Trait => SymbolKind::Trait,
- FileSymbolKind::TraitAlias => SymbolKind::TraitAlias,
- FileSymbolKind::TypeAlias => SymbolKind::TypeAlias,
- FileSymbolKind::Union => SymbolKind::Union,
+ hir::ModuleDefId::ConstId(..) => SymbolKind::Const,
+ hir::ModuleDefId::EnumVariantId(..) => SymbolKind::Variant,
+ hir::ModuleDefId::FunctionId(..) => SymbolKind::Function,
+ hir::ModuleDefId::MacroId(..) => SymbolKind::Macro,
+ hir::ModuleDefId::ModuleId(..) => SymbolKind::Module,
+ hir::ModuleDefId::StaticId(..) => SymbolKind::Static,
+ hir::ModuleDefId::AdtId(hir::AdtId::StructId(..)) => SymbolKind::Struct,
+ hir::ModuleDefId::AdtId(hir::AdtId::EnumId(..)) => SymbolKind::Enum,
+ hir::ModuleDefId::AdtId(hir::AdtId::UnionId(..)) => SymbolKind::Union,
+ hir::ModuleDefId::TraitId(..) => SymbolKind::Trait,
+ hir::ModuleDefId::TraitAliasId(..) => SymbolKind::TraitAlias,
+ hir::ModuleDefId::TypeAliasId(..) => SymbolKind::TypeAlias,
+ hir::ModuleDefId::BuiltinType(..) => SymbolKind::TypeAlias,
}
}
}
@@ -247,4 +415,5 @@ impl SnippetCap {
#[cfg(test)]
mod tests {
mod sourcegen_lints;
+ mod line_index;
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs
deleted file mode 100644
index 16814a1e6..000000000
--- a/src/tools/rust-analyzer/crates/ide-db/src/line_index.rs
+++ /dev/null
@@ -1,314 +0,0 @@
-//! `LineIndex` maps flat `TextSize` offsets into `(Line, Column)`
-//! representation.
-use std::{iter, mem};
-
-use stdx::hash::NoHashHashMap;
-use syntax::{TextRange, TextSize};
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct LineIndex {
- /// Offset the beginning of each line, zero-based.
- pub(crate) newlines: Vec<TextSize>,
- /// List of non-ASCII characters on each line.
- pub(crate) line_wide_chars: NoHashHashMap<u32, Vec<WideChar>>,
-}
-
-/// Line/Column information in native, utf8 format.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct LineCol {
- /// Zero-based
- pub line: u32,
- /// Zero-based utf8 offset
- pub col: u32,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum WideEncoding {
- Utf16,
- Utf32,
-}
-
-/// Line/Column information in legacy encodings.
-///
-/// Deliberately not a generic type and different from `LineCol`.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct WideLineCol {
- /// Zero-based
- pub line: u32,
- /// Zero-based
- pub col: u32,
-}
-
-#[derive(Clone, Debug, Hash, PartialEq, Eq)]
-pub(crate) struct WideChar {
- /// Start offset of a character inside a line, zero-based
- pub(crate) start: TextSize,
- /// End offset of a character inside a line, zero-based
- pub(crate) end: TextSize,
-}
-
-impl WideChar {
- /// Returns the length in 8-bit UTF-8 code units.
- fn len(&self) -> TextSize {
- self.end - self.start
- }
-
- /// Returns the length in UTF-16 or UTF-32 code units.
- fn wide_len(&self, enc: WideEncoding) -> usize {
- match enc {
- WideEncoding::Utf16 => {
- if self.len() == TextSize::from(4) {
- 2
- } else {
- 1
- }
- }
-
- WideEncoding::Utf32 => 1,
- }
- }
-}
-
-impl LineIndex {
- pub fn new(text: &str) -> LineIndex {
- let mut line_wide_chars = NoHashHashMap::default();
- let mut wide_chars = Vec::new();
-
- let mut newlines = Vec::with_capacity(16);
- newlines.push(TextSize::from(0));
-
- let mut curr_row = 0.into();
- let mut curr_col = 0.into();
- let mut line = 0;
- for c in text.chars() {
- let c_len = TextSize::of(c);
- curr_row += c_len;
- if c == '\n' {
- newlines.push(curr_row);
-
- // Save any utf-16 characters seen in the previous line
- if !wide_chars.is_empty() {
- line_wide_chars.insert(line, mem::take(&mut wide_chars));
- }
-
- // Prepare for processing the next line
- curr_col = 0.into();
- line += 1;
- continue;
- }
-
- if !c.is_ascii() {
- wide_chars.push(WideChar { start: curr_col, end: curr_col + c_len });
- }
-
- curr_col += c_len;
- }
-
- // Save any utf-16 characters seen in the last line
- if !wide_chars.is_empty() {
- line_wide_chars.insert(line, wide_chars);
- }
-
- LineIndex { newlines, line_wide_chars }
- }
-
- pub fn line_col(&self, offset: TextSize) -> LineCol {
- let line = self.newlines.partition_point(|&it| it <= offset) - 1;
- let line_start_offset = self.newlines[line];
- let col = offset - line_start_offset;
- LineCol { line: line as u32, col: col.into() }
- }
-
- pub fn offset(&self, line_col: LineCol) -> Option<TextSize> {
- self.newlines
- .get(line_col.line as usize)
- .map(|offset| offset + TextSize::from(line_col.col))
- }
-
- pub fn to_wide(&self, enc: WideEncoding, line_col: LineCol) -> WideLineCol {
- let col = self.utf8_to_wide_col(enc, line_col.line, line_col.col.into());
- WideLineCol { line: line_col.line, col: col as u32 }
- }
-
- pub fn to_utf8(&self, enc: WideEncoding, line_col: WideLineCol) -> LineCol {
- let col = self.wide_to_utf8_col(enc, line_col.line, line_col.col);
- LineCol { line: line_col.line, col: col.into() }
- }
-
- pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
- let lo = self.newlines.partition_point(|&it| it < range.start());
- let hi = self.newlines.partition_point(|&it| it <= range.end());
- let all = iter::once(range.start())
- .chain(self.newlines[lo..hi].iter().copied())
- .chain(iter::once(range.end()));
-
- all.clone()
- .zip(all.skip(1))
- .map(|(lo, hi)| TextRange::new(lo, hi))
- .filter(|it| !it.is_empty())
- }
-
- fn utf8_to_wide_col(&self, enc: WideEncoding, line: u32, col: TextSize) -> usize {
- let mut res: usize = col.into();
- if let Some(wide_chars) = self.line_wide_chars.get(&line) {
- for c in wide_chars {
- if c.end <= col {
- res -= usize::from(c.len()) - c.wide_len(enc);
- } else {
- // From here on, all utf16 characters come *after* the character we are mapping,
- // so we don't need to take them into account
- break;
- }
- }
- }
- res
- }
-
- fn wide_to_utf8_col(&self, enc: WideEncoding, line: u32, mut col: u32) -> TextSize {
- if let Some(wide_chars) = self.line_wide_chars.get(&line) {
- for c in wide_chars {
- if col > u32::from(c.start) {
- col += u32::from(c.len()) - c.wide_len(enc) as u32;
- } else {
- // From here on, all utf16 characters come *after* the character we are mapping,
- // so we don't need to take them into account
- break;
- }
- }
- }
-
- col.into()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use test_utils::skip_slow_tests;
-
- use super::WideEncoding::{Utf16, Utf32};
- use super::*;
-
- #[test]
- fn test_line_index() {
- let text = "hello\nworld";
- let table = [
- (00, 0, 0),
- (01, 0, 1),
- (05, 0, 5),
- (06, 1, 0),
- (07, 1, 1),
- (08, 1, 2),
- (10, 1, 4),
- (11, 1, 5),
- (12, 1, 6),
- ];
-
- let index = LineIndex::new(text);
- for (offset, line, col) in table {
- assert_eq!(index.line_col(offset.into()), LineCol { line, col });
- }
-
- let text = "\nhello\nworld";
- let table = [(0, 0, 0), (1, 1, 0), (2, 1, 1), (6, 1, 5), (7, 2, 0)];
- let index = LineIndex::new(text);
- for (offset, line, col) in table {
- assert_eq!(index.line_col(offset.into()), LineCol { line, col });
- }
- }
-
- #[test]
- fn test_char_len() {
- assert_eq!('メ'.len_utf8(), 3);
- assert_eq!('メ'.len_utf16(), 1);
- }
-
- #[test]
- fn test_empty_index() {
- let col_index = LineIndex::new(
- "
-const C: char = 'x';
-",
- );
- assert_eq!(col_index.line_wide_chars.len(), 0);
- }
-
- #[test]
- fn test_every_chars() {
- if skip_slow_tests() {
- return;
- }
-
- let text: String = {
- let mut chars: Vec<char> = ((0 as char)..char::MAX).collect(); // Neat!
- chars.extend("\n".repeat(chars.len() / 16).chars());
- let mut rng = oorandom::Rand32::new(stdx::rand::seed());
- stdx::rand::shuffle(&mut chars, |i| rng.rand_range(0..i as u32) as usize);
- chars.into_iter().collect()
- };
- assert!(text.contains('💩')); // Sanity check.
-
- let line_index = LineIndex::new(&text);
-
- let mut lin_col = LineCol { line: 0, col: 0 };
- let mut col_utf16 = 0;
- let mut col_utf32 = 0;
- for (offset, c) in text.char_indices() {
- let got_offset = line_index.offset(lin_col).unwrap();
- assert_eq!(usize::from(got_offset), offset);
-
- let got_lin_col = line_index.line_col(got_offset);
- assert_eq!(got_lin_col, lin_col);
-
- for enc in [Utf16, Utf32] {
- let wide_lin_col = line_index.to_wide(enc, lin_col);
- let got_lin_col = line_index.to_utf8(enc, wide_lin_col);
- assert_eq!(got_lin_col, lin_col);
-
- let want_col = match enc {
- Utf16 => col_utf16,
- Utf32 => col_utf32,
- };
- assert_eq!(wide_lin_col.col, want_col)
- }
-
- if c == '\n' {
- lin_col.line += 1;
- lin_col.col = 0;
- col_utf16 = 0;
- col_utf32 = 0;
- } else {
- lin_col.col += c.len_utf8() as u32;
- col_utf16 += c.len_utf16() as u32;
- col_utf32 += 1;
- }
- }
- }
-
- #[test]
- fn test_splitlines() {
- fn r(lo: u32, hi: u32) -> TextRange {
- TextRange::new(lo.into(), hi.into())
- }
-
- let text = "a\nbb\nccc\n";
- let line_index = LineIndex::new(text);
-
- let actual = line_index.lines(r(0, 9)).collect::<Vec<_>>();
- let expected = vec![r(0, 2), r(2, 5), r(5, 9)];
- assert_eq!(actual, expected);
-
- let text = "";
- let line_index = LineIndex::new(text);
-
- let actual = line_index.lines(r(0, 0)).collect::<Vec<_>>();
- let expected = vec![];
- assert_eq!(actual, expected);
-
- let text = "\n";
- let line_index = LineIndex::new(text);
-
- let actual = line_index.lines(r(0, 1)).collect::<Vec<_>>();
- let expected = vec![r(0, 1)];
- assert_eq!(actual, expected)
- }
-}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
index 6402a84a6..73e6a920e 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
@@ -9,6 +9,19 @@ use syntax::{
ted, SyntaxNode,
};
+#[derive(Default)]
+struct AstSubsts {
+ types_and_consts: Vec<TypeOrConst>,
+ lifetimes: Vec<ast::LifetimeArg>,
+}
+
+enum TypeOrConst {
+ Either(ast::TypeArg), // indistinguishable type or const param
+ Const(ast::ConstArg),
+}
+
+type LifetimeName = String;
+
/// `PathTransform` substitutes path in SyntaxNodes in bulk.
///
/// This is mostly useful for IDE code generation. If you paste some existing
@@ -34,7 +47,7 @@ use syntax::{
/// ```
pub struct PathTransform<'a> {
generic_def: Option<hir::GenericDef>,
- substs: Vec<ast::Type>,
+ substs: AstSubsts,
target_scope: &'a SemanticsScope<'a>,
source_scope: &'a SemanticsScope<'a>,
}
@@ -72,7 +85,12 @@ impl<'a> PathTransform<'a> {
target_scope: &'a SemanticsScope<'a>,
source_scope: &'a SemanticsScope<'a>,
) -> PathTransform<'a> {
- PathTransform { source_scope, target_scope, generic_def: None, substs: Vec::new() }
+ PathTransform {
+ source_scope,
+ target_scope,
+ generic_def: None,
+ substs: AstSubsts::default(),
+ }
}
pub fn apply(&self, syntax: &SyntaxNode) {
@@ -91,12 +109,14 @@ impl<'a> PathTransform<'a> {
let target_module = self.target_scope.module();
let source_module = self.source_scope.module();
let skip = match self.generic_def {
- // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
+ // this is a trait impl, so we need to skip the first type parameter (i.e. Self) -- this is a bit hacky
Some(hir::GenericDef::Trait(_)) => 1,
_ => 0,
};
- let substs_by_param: FxHashMap<_, _> = self
- .generic_def
+ let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default();
+ let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default();
+ let mut default_types: Vec<hir::TypeParam> = Default::default();
+ self.generic_def
.into_iter()
.flat_map(|it| it.type_params(db))
.skip(skip)
@@ -106,51 +126,105 @@ impl<'a> PathTransform<'a> {
// can still hit those trailing values and check if they actually have
// a default type. If they do, go for that type from `hir` to `ast` so
// the resulting change can be applied correctly.
- .zip(self.substs.iter().map(Some).chain(std::iter::repeat(None)))
- .filter_map(|(k, v)| match k.split(db) {
- Either::Left(_) => None,
- Either::Right(t) => match v {
- Some(v) => Some((k, v.clone())),
- None => {
- let default = t.default(db)?;
- Some((
- k,
- ast::make::ty(
- &default.display_source_code(db, source_module.into()).ok()?,
- ),
- ))
+ .zip(self.substs.types_and_consts.iter().map(Some).chain(std::iter::repeat(None)))
+ .for_each(|(k, v)| match (k.split(db), v) {
+ (Either::Right(k), Some(TypeOrConst::Either(v))) => {
+ if let Some(ty) = v.ty() {
+ type_substs.insert(k, ty.clone());
+ }
+ }
+ (Either::Right(k), None) => {
+ if let Some(default) = k.default(db) {
+ if let Some(default) =
+ &default.display_source_code(db, source_module.into(), false).ok()
+ {
+ type_substs.insert(k, ast::make::ty(default).clone_for_update());
+ default_types.push(k);
+ }
+ }
+ }
+ (Either::Left(k), Some(TypeOrConst::Either(v))) => {
+ if let Some(ty) = v.ty() {
+ const_substs.insert(k, ty.syntax().clone());
}
- },
- })
+ }
+ (Either::Left(k), Some(TypeOrConst::Const(v))) => {
+ if let Some(expr) = v.expr() {
+ // FIXME: expressions in curly brackets can cause ambiguity after insertion
+ // (e.g. `N * 2` -> `{1 + 1} * 2`; it's unclear whether `{1 + 1}`
+ // is a standalone statement or a part of another expresson)
+ // and sometimes require slight modifications; see
+ // https://doc.rust-lang.org/reference/statements.html#expression-statements
+ const_substs.insert(k, expr.syntax().clone());
+ }
+ }
+ (Either::Left(_), None) => (), // FIXME: get default const value
+ _ => (), // ignore mismatching params
+ });
+ let lifetime_substs: FxHashMap<_, _> = self
+ .generic_def
+ .into_iter()
+ .flat_map(|it| it.lifetime_params(db))
+ .zip(self.substs.lifetimes.clone())
+ .filter_map(|(k, v)| Some((k.name(db).display(db.upcast()).to_string(), v.lifetime()?)))
.collect();
- Ctx { substs: substs_by_param, target_module, source_scope: self.source_scope }
+ let ctx = Ctx {
+ type_substs,
+ const_substs,
+ lifetime_substs,
+ target_module,
+ source_scope: self.source_scope,
+ };
+ ctx.transform_default_type_substs(default_types);
+ ctx
}
}
struct Ctx<'a> {
- substs: FxHashMap<hir::TypeOrConstParam, ast::Type>,
+ type_substs: FxHashMap<hir::TypeParam, ast::Type>,
+ const_substs: FxHashMap<hir::ConstParam, SyntaxNode>,
+ lifetime_substs: FxHashMap<LifetimeName, ast::Lifetime>,
target_module: hir::Module,
source_scope: &'a SemanticsScope<'a>,
}
+fn postorder(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
+ item.preorder().filter_map(|event| match event {
+ syntax::WalkEvent::Enter(_) => None,
+ syntax::WalkEvent::Leave(node) => Some(node),
+ })
+}
+
impl<'a> Ctx<'a> {
fn apply(&self, item: &SyntaxNode) {
// `transform_path` may update a node's parent and that would break the
// tree traversal. Thus all paths in the tree are collected into a vec
// so that such operation is safe.
- let paths = item
- .preorder()
- .filter_map(|event| match event {
- syntax::WalkEvent::Enter(_) => None,
- syntax::WalkEvent::Leave(node) => Some(node),
- })
- .filter_map(ast::Path::cast)
- .collect::<Vec<_>>();
-
+ let paths = postorder(item).filter_map(ast::Path::cast).collect::<Vec<_>>();
for path in paths {
self.transform_path(path);
}
+
+ postorder(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| {
+ if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) {
+ ted::replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax());
+ }
+ });
}
+
+ fn transform_default_type_substs(&self, default_types: Vec<hir::TypeParam>) {
+ for k in default_types {
+ let v = self.type_substs.get(&k).unwrap();
+ // `transform_path` may update a node's parent and that would break the
+ // tree traversal. Thus all paths in the tree are collected into a vec
+ // so that such operation is safe.
+ let paths = postorder(&v.syntax()).filter_map(ast::Path::cast).collect::<Vec<_>>();
+ for path in paths {
+ self.transform_path(path);
+ }
+ }
+ }
+
fn transform_path(&self, path: ast::Path) -> Option<()> {
if path.qualifier().is_some() {
return None;
@@ -167,7 +241,7 @@ impl<'a> Ctx<'a> {
match resolution {
hir::PathResolution::TypeParam(tp) => {
- if let Some(subst) = self.substs.get(&tp.merge()) {
+ if let Some(subst) = self.type_substs.get(&tp) {
let parent = path.syntax().parent()?;
if let Some(parent) = ast::Path::cast(parent.clone()) {
// Path inside path means that there is an associated
@@ -234,8 +308,12 @@ impl<'a> Ctx<'a> {
}
ted::replace(path.syntax(), res.syntax())
}
+ hir::PathResolution::ConstParam(cp) => {
+ if let Some(subst) = self.const_substs.get(&cp) {
+ ted::replace(path.syntax(), subst.clone_subtree().clone_for_update());
+ }
+ }
hir::PathResolution::Local(_)
- | hir::PathResolution::ConstParam(_)
| hir::PathResolution::SelfType(_)
| hir::PathResolution::Def(_)
| hir::PathResolution::BuiltinAttr(_)
@@ -248,7 +326,7 @@ impl<'a> Ctx<'a> {
// FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
// trait ref, and then go from the types in the substs back to the syntax).
-fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
+fn get_syntactic_substs(impl_def: ast::Impl) -> Option<AstSubsts> {
let target_trait = impl_def.trait_()?;
let path_type = match target_trait {
ast::Type::PathType(path) => path,
@@ -259,13 +337,22 @@ fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
get_type_args_from_arg_list(generic_arg_list)
}
-fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<Vec<ast::Type>> {
- let mut result = Vec::new();
- for generic_arg in generic_arg_list.generic_args() {
- if let ast::GenericArg::TypeArg(type_arg) = generic_arg {
- result.push(type_arg.ty()?)
+fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<AstSubsts> {
+ let mut result = AstSubsts::default();
+ generic_arg_list.generic_args().for_each(|generic_arg| match generic_arg {
+ // Const params are marked as consts on definition only,
+ // being passed to the trait they are indistguishable from type params;
+ // anyway, we don't really need to distinguish them here.
+ ast::GenericArg::TypeArg(type_arg) => {
+ result.types_and_consts.push(TypeOrConst::Either(type_arg))
}
- }
+ // Some const values are recognized correctly.
+ ast::GenericArg::ConstArg(const_arg) => {
+ result.types_and_consts.push(TypeOrConst::Const(const_arg));
+ }
+ ast::GenericArg::LifetimeArg(l_arg) => result.lifetimes.push(l_arg),
+ _ => (),
+ });
Some(result)
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
index f710211c8..52a23b4b8 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
@@ -178,7 +178,7 @@ fn rename_mod(
let mut source_change = SourceChange::default();
- if module.is_crate_root(sema.db) {
+ if module.is_crate_root() {
return Ok(source_change);
}
@@ -202,12 +202,13 @@ fn rename_mod(
// - Module has submodules defined in separate files
let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) {
// Go up one level since the anchor is inside the dir we're trying to rename
- (true, _, Some(mod_name)) => {
- Some((format!("../{}", mod_name.unescaped()), format!("../{new_name}")))
- }
+ (true, _, Some(mod_name)) => Some((
+ format!("../{}", mod_name.unescaped().display(sema.db)),
+ format!("../{new_name}"),
+ )),
// The anchor is on the same level as target dir
(false, true, Some(mod_name)) => {
- Some((mod_name.unescaped().to_string(), new_name.to_string()))
+ Some((mod_name.unescaped().display(sema.db).to_string(), new_name.to_owned()))
}
_ => None,
};
@@ -232,7 +233,7 @@ fn rename_mod(
{
source_change.insert_source_edit(
file_id,
- TextEdit::replace(file_range.range, new_name.to_string()),
+ TextEdit::replace(file_range.range, new_name.to_owned()),
)
};
}
@@ -442,7 +443,7 @@ fn source_edit_from_name_ref(
let s = field_name.syntax().text_range().start();
let e = pat.syntax().text_range().start();
edit.delete(TextRange::new(s, e));
- edit.replace(name.syntax().text_range(), new_name.to_string());
+ edit.replace(name.syntax().text_range(), new_name.to_owned());
return true;
}
}
@@ -462,7 +463,19 @@ fn source_edit_from_def(
if let Definition::Local(local) = def {
let mut file_id = None;
for source in local.sources(sema.db) {
- let source = source.source;
+ let source = match source.source.clone().original_ast_node(sema.db) {
+ Some(source) => source,
+ None => match source.source.syntax().original_file_range_opt(sema.db) {
+ Some(FileRange { file_id: file_id2, range }) => {
+ file_id = Some(file_id2);
+ edit.replace(range, new_name.to_owned());
+ continue;
+ }
+ None => {
+ bail!("Can't rename local that is defined in a macro declaration")
+ }
+ },
+ };
file_id = source.file_id.file_id();
if let Either::Left(pat) = source.value {
let name_range = pat.name().unwrap().syntax().text_range();
@@ -485,7 +498,7 @@ fn source_edit_from_def(
// Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 }
// Foo { field: ref mut local } -> Foo { field: ref mut new_name }
// ^^^^^ replace this with `new_name`
- edit.replace(name_range, new_name.to_string());
+ edit.replace(name_range, new_name.to_owned());
}
} else {
// Foo { ref mut field } -> Foo { field: ref mut new_name }
@@ -495,10 +508,10 @@ fn source_edit_from_def(
pat.syntax().text_range().start(),
format!("{}: ", pat_field.field_name().unwrap()),
);
- edit.replace(name_range, new_name.to_string());
+ edit.replace(name_range, new_name.to_owned());
}
} else {
- edit.replace(name_range, new_name.to_string());
+ edit.replace(name_range, new_name.to_owned());
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 12f5e4e2a..e8ff107bd 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -4,17 +4,18 @@
//! get a super-set of matches. Then, we we confirm each match using precise
//! name resolution.
-use std::{mem, sync::Arc};
+use std::mem;
use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
use hir::{
AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility,
};
use memchr::memmem::Finder;
+use nohash_hasher::IntMap;
use once_cell::unsync::Lazy;
use parser::SyntaxKind;
-use stdx::hash::NoHashHashMap;
use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
+use triomphe::Arc;
use crate::{
defs::{Definition, NameClass, NameRefClass},
@@ -24,7 +25,7 @@ use crate::{
#[derive(Debug, Default, Clone)]
pub struct UsageSearchResult {
- pub references: NoHashHashMap<FileId, Vec<FileReference>>,
+ pub references: IntMap<FileId, Vec<FileReference>>,
}
impl UsageSearchResult {
@@ -49,7 +50,7 @@ impl UsageSearchResult {
impl IntoIterator for UsageSearchResult {
type Item = (FileId, Vec<FileReference>);
- type IntoIter = <NoHashHashMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter;
+ type IntoIter = <IntMap<FileId, Vec<FileReference>> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.references.into_iter()
@@ -83,17 +84,17 @@ pub enum ReferenceCategory {
/// e.g. for things like local variables.
#[derive(Clone, Debug)]
pub struct SearchScope {
- entries: NoHashHashMap<FileId, Option<TextRange>>,
+ entries: IntMap<FileId, Option<TextRange>>,
}
impl SearchScope {
- fn new(entries: NoHashHashMap<FileId, Option<TextRange>>) -> SearchScope {
+ fn new(entries: IntMap<FileId, Option<TextRange>>) -> SearchScope {
SearchScope { entries }
}
/// Build a search scope spanning the entire crate graph of files.
fn crate_graph(db: &RootDatabase) -> SearchScope {
- let mut entries = NoHashHashMap::default();
+ let mut entries = IntMap::default();
let graph = db.crate_graph();
for krate in graph.iter() {
@@ -107,7 +108,7 @@ impl SearchScope {
/// Build a search scope spanning all the reverse dependencies of the given crate.
fn reverse_dependencies(db: &RootDatabase, of: hir::Crate) -> SearchScope {
- let mut entries = NoHashHashMap::default();
+ let mut entries = IntMap::default();
for rev_dep in of.transitive_reverse_dependencies(db) {
let root_file = rev_dep.root_file(db);
let source_root_id = db.file_source_root(root_file);
@@ -127,7 +128,7 @@ impl SearchScope {
/// Build a search scope spanning the given module and all its submodules.
fn module_and_children(db: &RootDatabase, module: hir::Module) -> SearchScope {
- let mut entries = NoHashHashMap::default();
+ let mut entries = IntMap::default();
let (file_id, range) = {
let InFile { file_id, value } = module.definition_source(db);
@@ -160,7 +161,7 @@ impl SearchScope {
/// Build an empty search scope.
pub fn empty() -> SearchScope {
- SearchScope::new(NoHashHashMap::default())
+ SearchScope::new(IntMap::default())
}
/// Build a empty search scope spanning the given file.
@@ -224,7 +225,7 @@ impl Definition {
// def is crate root
// FIXME: We don't do searches for crates currently, as a crate does not actually have a single name
if let &Definition::Module(module) = self {
- if module.is_crate_root(db) {
+ if module.is_crate_root() {
return SearchScope::reverse_dependencies(db, module.krate());
}
}
@@ -242,6 +243,8 @@ impl Definition {
DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
+ // FIXME: implement
+ DefWithBody::InTypeConst(_) => return SearchScope::empty(),
};
return match def {
Some(def) => SearchScope::file_range(def.as_ref().original_file_range_full(db)),
@@ -391,7 +394,7 @@ impl<'a> FindUsages<'a> {
let name = match self.def {
// special case crate modules as these do not have a proper name
- Definition::Module(module) if module.is_crate_root(self.sema.db) => {
+ Definition::Module(module) if module.is_crate_root() => {
// FIXME: This assumes the crate name is always equal to its display name when it really isn't
module
.krate()
@@ -438,11 +441,11 @@ impl<'a> FindUsages<'a> {
fn scope_files<'a>(
sema: &'a Semantics<'_, RootDatabase>,
scope: &'a SearchScope,
- ) -> impl Iterator<Item = (Arc<String>, FileId, TextRange)> + 'a {
+ ) -> impl Iterator<Item = (Arc<str>, FileId, TextRange)> + 'a {
scope.entries.iter().map(|(&file_id, &search_range)| {
let text = sema.db.file_text(file_id);
let search_range =
- search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
+ search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text)));
(text, file_id, search_range)
})
@@ -499,7 +502,7 @@ impl<'a> FindUsages<'a> {
let scope =
search_scope.intersection(&SearchScope::module_and_children(self.sema.db, module));
- let is_crate_root = module.is_crate_root(self.sema.db).then(|| Finder::new("crate"));
+ let is_crate_root = module.is_crate_root().then(|| Finder::new("crate"));
let finder = &Finder::new("super");
for (text, file_id, search_range) in scope_files(sema, &scope) {
@@ -553,7 +556,7 @@ impl<'a> FindUsages<'a> {
let text = sema.db.file_text(file_id);
let search_range =
- search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
+ search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text)));
let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
let finder = &Finder::new("self");
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
index 936354f29..061fb0f05 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
@@ -5,16 +5,16 @@
use std::{collections::hash_map::Entry, iter, mem};
+use crate::SnippetCap;
use base_db::{AnchoredPathBuf, FileId};
-use stdx::{hash::NoHashHashMap, never};
-use syntax::{algo, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize};
+use nohash_hasher::IntMap;
+use stdx::never;
+use syntax::{algo, ast, ted, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize};
use text_edit::{TextEdit, TextEditBuilder};
-use crate::SnippetCap;
-
#[derive(Default, Debug, Clone)]
pub struct SourceChange {
- pub source_file_edits: NoHashHashMap<FileId, TextEdit>,
+ pub source_file_edits: IntMap<FileId, TextEdit>,
pub file_system_edits: Vec<FileSystemEdit>,
pub is_snippet: bool,
}
@@ -23,7 +23,7 @@ impl SourceChange {
/// Creates a new SourceChange with the given label
/// from the edits.
pub fn from_edits(
- source_file_edits: NoHashHashMap<FileId, TextEdit>,
+ source_file_edits: IntMap<FileId, TextEdit>,
file_system_edits: Vec<FileSystemEdit>,
) -> Self {
SourceChange { source_file_edits, file_system_edits, is_snippet: false }
@@ -77,8 +77,8 @@ impl Extend<FileSystemEdit> for SourceChange {
}
}
-impl From<NoHashHashMap<FileId, TextEdit>> for SourceChange {
- fn from(source_file_edits: NoHashHashMap<FileId, TextEdit>) -> SourceChange {
+impl From<IntMap<FileId, TextEdit>> for SourceChange {
+ fn from(source_file_edits: IntMap<FileId, TextEdit>) -> SourceChange {
SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false }
}
}
@@ -99,6 +99,8 @@ pub struct SourceChangeBuilder {
/// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin.
pub mutated_tree: Option<TreeMutator>,
+ /// Keeps track of where to place snippets
+ pub snippet_builder: Option<SnippetBuilder>,
}
pub struct TreeMutator {
@@ -106,6 +108,12 @@ pub struct TreeMutator {
mutable_clone: SyntaxNode,
}
+#[derive(Default)]
+pub struct SnippetBuilder {
+ /// Where to place snippets at
+ places: Vec<PlaceSnippet>,
+}
+
impl TreeMutator {
pub fn new(immutable: &SyntaxNode) -> TreeMutator {
let immutable = immutable.ancestors().last().unwrap();
@@ -131,6 +139,7 @@ impl SourceChangeBuilder {
source_change: SourceChange::default(),
trigger_signature_help: false,
mutated_tree: None,
+ snippet_builder: None,
}
}
@@ -140,6 +149,17 @@ impl SourceChangeBuilder {
}
fn commit(&mut self) {
+ // Render snippets first so that they get bundled into the tree diff
+ if let Some(mut snippets) = self.snippet_builder.take() {
+ // Last snippet always has stop index 0
+ let last_stop = snippets.places.pop().unwrap();
+ last_stop.place(0);
+
+ for (index, stop) in snippets.places.into_iter().enumerate() {
+ stop.place(index + 1)
+ }
+ }
+
if let Some(tm) = self.mutated_tree.take() {
algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit)
}
@@ -161,7 +181,7 @@ impl SourceChangeBuilder {
/// mutability, and different nodes in the same tree see the same mutations.
///
/// The typical pattern for an assist is to find specific nodes in the read
- /// phase, and then get their mutable couterparts using `make_mut` in the
+ /// phase, and then get their mutable counterparts using `make_mut` in the
/// mutable state.
pub fn make_syntax_mut(&mut self, node: SyntaxNode) -> SyntaxNode {
self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node)
@@ -214,6 +234,30 @@ impl SourceChangeBuilder {
self.trigger_signature_help = true;
}
+ /// Adds a tabstop snippet to place the cursor before `node`
+ pub fn add_tabstop_before(&mut self, _cap: SnippetCap, node: impl AstNode) {
+ assert!(node.syntax().parent().is_some());
+ self.add_snippet(PlaceSnippet::Before(node.syntax().clone()));
+ }
+
+ /// Adds a tabstop snippet to place the cursor after `node`
+ pub fn add_tabstop_after(&mut self, _cap: SnippetCap, node: impl AstNode) {
+ assert!(node.syntax().parent().is_some());
+ self.add_snippet(PlaceSnippet::After(node.syntax().clone()));
+ }
+
+ /// Adds a snippet to move the cursor selected over `node`
+ pub fn add_placeholder_snippet(&mut self, _cap: SnippetCap, node: impl AstNode) {
+ assert!(node.syntax().parent().is_some());
+ self.add_snippet(PlaceSnippet::Over(node.syntax().clone()))
+ }
+
+ fn add_snippet(&mut self, snippet: PlaceSnippet) {
+ let snippet_builder = self.snippet_builder.get_or_insert(SnippetBuilder { places: vec![] });
+ snippet_builder.places.push(snippet);
+ self.source_change.is_snippet = true;
+ }
+
pub fn finish(mut self) -> SourceChange {
self.commit();
mem::take(&mut self.source_change)
@@ -236,3 +280,66 @@ impl From<FileSystemEdit> for SourceChange {
}
}
}
+
+enum PlaceSnippet {
+ /// Place a tabstop before a node
+ Before(SyntaxNode),
+ /// Place a tabstop before a node
+ After(SyntaxNode),
+ /// Place a placeholder snippet in place of the node
+ Over(SyntaxNode),
+}
+
+impl PlaceSnippet {
+ /// Places the snippet before or over a node with the given tab stop index
+ fn place(self, order: usize) {
+ // ensure the target node is still attached
+ match &self {
+ PlaceSnippet::Before(node) | PlaceSnippet::After(node) | PlaceSnippet::Over(node) => {
+ // node should still be in the tree, but if it isn't
+ // then it's okay to just ignore this place
+ if stdx::never!(node.parent().is_none()) {
+ return;
+ }
+ }
+ }
+
+ match self {
+ PlaceSnippet::Before(node) => {
+ ted::insert_raw(ted::Position::before(&node), Self::make_tab_stop(order));
+ }
+ PlaceSnippet::After(node) => {
+ ted::insert_raw(ted::Position::after(&node), Self::make_tab_stop(order));
+ }
+ PlaceSnippet::Over(node) => {
+ let position = ted::Position::before(&node);
+ node.detach();
+
+ let snippet = ast::SourceFile::parse(&format!("${{{order}:_}}"))
+ .syntax_node()
+ .clone_for_update();
+
+ let placeholder =
+ snippet.descendants().find_map(ast::UnderscoreExpr::cast).unwrap();
+ ted::replace(placeholder.syntax(), node);
+
+ ted::insert_raw(position, snippet);
+ }
+ }
+ }
+
+ fn make_tab_stop(order: usize) -> SyntaxNode {
+ let stop = ast::SourceFile::parse(&format!("stop!(${order})"))
+ .syntax_node()
+ .descendants()
+ .find_map(ast::TokenTree::cast)
+ .unwrap()
+ .syntax()
+ .clone_for_update();
+
+ stop.first_token().unwrap().detach();
+ stop.last_token().unwrap().detach();
+
+ stop
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
index a91ffd1ec..b54c43b29 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
@@ -25,7 +25,6 @@ use std::{
fmt,
hash::{Hash, Hasher},
mem,
- sync::Arc,
};
use base_db::{
@@ -40,6 +39,7 @@ use hir::{
};
use rayon::prelude::*;
use rustc_hash::FxHashSet;
+use triomphe::Arc;
use crate::RootDatabase;
@@ -98,6 +98,10 @@ pub trait SymbolsDatabase: HirDatabase + SourceDatabaseExt + Upcast<dyn HirDatab
/// The symbol index for a given source root within library_roots.
fn library_symbols(&self, source_root_id: SourceRootId) -> Arc<SymbolIndex>;
+ #[salsa::transparent]
+ /// The symbol indices of modules that make up a given crate.
+ fn crate_symbols(&self, krate: Crate) -> Box<[Arc<SymbolIndex>]>;
+
/// The set of "local" (that is, from the current workspace) roots.
/// Files in local roots are assumed to change frequently.
#[salsa::input]
@@ -112,26 +116,33 @@ pub trait SymbolsDatabase: HirDatabase + SourceDatabaseExt + Upcast<dyn HirDatab
fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Arc<SymbolIndex> {
let _p = profile::span("library_symbols");
- // todo: this could be parallelized, once I figure out how to do that...
- let symbols = db
- .source_root_crates(source_root_id)
+ let mut symbol_collector = SymbolCollector::new(db.upcast());
+
+ db.source_root_crates(source_root_id)
.iter()
.flat_map(|&krate| Crate::from(krate).modules(db.upcast()))
- // we specifically avoid calling SymbolsDatabase::module_symbols here, even they do the same thing,
+ // we specifically avoid calling other SymbolsDatabase queries here, even though they do the same thing,
// as the index for a library is not going to really ever change, and we do not want to store each
- // module's index in salsa.
- .flat_map(|module| SymbolCollector::collect(db.upcast(), module))
- .collect();
+ // the module or crate indices for those in salsa unless we need to.
+ .for_each(|module| symbol_collector.collect(module));
+ let mut symbols = symbol_collector.finish();
+ symbols.shrink_to_fit();
Arc::new(SymbolIndex::new(symbols))
}
fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc<SymbolIndex> {
let _p = profile::span("module_symbols");
- let symbols = SymbolCollector::collect(db.upcast(), module);
+
+ let symbols = SymbolCollector::collect_module(db.upcast(), module);
Arc::new(SymbolIndex::new(symbols))
}
+pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc<SymbolIndex>]> {
+ let _p = profile::span("crate_symbols");
+ krate.modules(db.upcast()).into_iter().map(|module| db.module_symbols(module)).collect()
+}
+
/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
struct Snap<DB>(DB);
impl<DB: ParallelDatabase> Snap<salsa::Snapshot<DB>> {
@@ -187,36 +198,21 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
.map_with(Snap::new(db), |snap, &root| snap.library_symbols(root))
.collect()
} else {
- let mut modules = Vec::new();
+ let mut crates = Vec::new();
for &root in db.local_roots().iter() {
- let crates = db.source_root_crates(root);
- for &krate in crates.iter() {
- modules.extend(Crate::from(krate).modules(db));
- }
+ crates.extend(db.source_root_crates(root).iter().copied())
}
-
- modules
- .par_iter()
- .map_with(Snap::new(db), |snap, &module| snap.module_symbols(module))
- .collect()
+ let indices: Vec<_> = crates
+ .into_par_iter()
+ .map_with(Snap::new(db), |snap, krate| snap.crate_symbols(krate.into()))
+ .collect();
+ indices.iter().flat_map(|indices| indices.iter().cloned()).collect()
};
query.search(&indices)
}
-pub fn crate_symbols(db: &RootDatabase, krate: Crate, query: Query) -> Vec<FileSymbol> {
- let _p = profile::span("crate_symbols").detail(|| format!("{query:?}"));
-
- let modules = krate.modules(db);
- let indices: Vec<_> = modules
- .par_iter()
- .map_with(Snap::new(db), |snap, &module| snap.module_symbols(module))
- .collect();
-
- query.search(&indices)
-}
-
#[derive(Default)]
pub struct SymbolIndex {
symbols: Vec<FileSymbol>,
@@ -274,7 +270,12 @@ impl SymbolIndex {
builder.insert(key, value).unwrap();
}
- let map = fst::Map::new(builder.into_inner().unwrap()).unwrap();
+ let map = fst::Map::new({
+ let mut buf = builder.into_inner().unwrap();
+ buf.shrink_to_fit();
+ buf
+ })
+ .unwrap();
SymbolIndex { symbols, map }
}
@@ -316,7 +317,14 @@ impl Query {
let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);
for symbol in &symbol_index.symbols[start..end] {
- if self.only_types && !symbol.kind.is_type() {
+ if self.only_types
+ && !matches!(
+ symbol.def,
+ hir::ModuleDef::Adt(..)
+ | hir::ModuleDef::TypeAlias(..)
+ | hir::ModuleDef::BuiltinType(..)
+ )
+ {
continue;
}
if self.exact {
@@ -418,7 +426,7 @@ struct StructInModB;
.modules(&db)
.into_iter()
.map(|module_id| {
- let mut symbols = SymbolCollector::collect(&db, module_id);
+ let mut symbols = SymbolCollector::collect_module(&db, module_id);
symbols.sort_by_key(|it| it.name.clone());
(module_id, symbols)
})
@@ -426,4 +434,31 @@ struct StructInModB;
expect_file!["./test_data/test_symbol_index_collection.txt"].assert_debug_eq(&symbols);
}
+
+ #[test]
+ fn test_doc_alias() {
+ let (db, _) = RootDatabase::with_single_file(
+ r#"
+#[doc(alias="s1")]
+#[doc(alias="s2")]
+#[doc(alias("mul1","mul2"))]
+struct Struct;
+
+#[doc(alias="s1")]
+struct Duplicate;
+ "#,
+ );
+
+ let symbols: Vec<_> = Crate::from(db.test_crate())
+ .modules(&db)
+ .into_iter()
+ .map(|module_id| {
+ let mut symbols = SymbolCollector::collect_module(&db, module_id);
+ symbols.sort_by_key(|it| it.name.clone());
+ (module_id, symbols)
+ })
+ .collect();
+
+ expect_file!["./test_data/test_doc_alias.txt"].assert_debug_eq(&symbols);
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
index 2d6927cee..acf0a67de 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
@@ -92,7 +92,7 @@ pub fn lex_format_specifiers(
let (_, second) = cloned.next().unwrap_or_default();
match second {
'<' | '^' | '>' => {
- // alignment specifier, first char specifies fillment
+ // alignment specifier, first char specifies fill
skip_char_and_emit(&mut chars, FormatSpecifier::Fill, &mut callback);
skip_char_and_emit(&mut chars, FormatSpecifier::Align, &mut callback);
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
index fcef71fb7..fc2308181 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
@@ -1,7 +1,7 @@
//! Tools to work with expressions present in format string literals for the `format_args!` family of macros.
//! Primarily meant for assists and completions.
-/// Enum for represenging extraced format string args.
+/// Enum for representing extracted format string args.
/// Can either be extracted expressions (which includes identifiers),
/// or placeholders `{}`.
#[derive(Debug, PartialEq, Eq)]
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
index 8bc093a85..0b0fc6693 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs
@@ -60,7 +60,9 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode {
|f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) };
match tok.kind() {
- k if is_text(k) && is_next(|it| !it.is_punct() || it == UNDERSCORE, false) => {
+ k if is_text(k)
+ && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#]), false) =>
+ {
mods.push(do_ws(after, tok));
}
L_CURLY if is_next(|it| it != R_CURLY, true) => {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
index a34dc1b69..22ced69d8 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -52,7 +52,9 @@ pub fn preorder_expr(start: &ast::Expr, cb: &mut dyn FnMut(WalkEvent<ast::Expr>)
}
};
if let Some(let_stmt) = node.parent().and_then(ast::LetStmt::cast) {
- if Some(node.clone()) != let_stmt.initializer().map(|it| it.syntax().clone()) {
+ if let_stmt.initializer().map(|it| it.syntax() != &node).unwrap_or(true)
+ && let_stmt.let_else().map(|it| it.syntax() != &node).unwrap_or(true)
+ {
// skipping potential const pat expressions in let statements
preorder.skip_subtree();
continue;
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
new file mode 100644
index 000000000..7834c6603
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -0,0 +1,216 @@
+[
+ (
+ Module {
+ id: ModuleId {
+ krate: Idx::<CrateData>(0),
+ block: None,
+ local_id: Idx::<ModuleData>(0),
+ },
+ },
+ [
+ FileSymbol {
+ name: "Duplicate",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 1,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 83..119,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 109..118,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
+ name: "Struct",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 0,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 0..81,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 74..80,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
+ name: "mul1",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 0,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 0..81,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 74..80,
+ },
+ },
+ container_name: None,
+ is_alias: true,
+ },
+ FileSymbol {
+ name: "mul2",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 0,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 0..81,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 74..80,
+ },
+ },
+ container_name: None,
+ is_alias: true,
+ },
+ FileSymbol {
+ name: "s1",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 0,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 0..81,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 74..80,
+ },
+ },
+ container_name: None,
+ is_alias: true,
+ },
+ FileSymbol {
+ name: "s1",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 1,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 83..119,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 109..118,
+ },
+ },
+ container_name: None,
+ is_alias: true,
+ },
+ FileSymbol {
+ name: "s2",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 0,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: STRUCT,
+ range: 0..81,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 74..80,
+ },
+ },
+ container_name: None,
+ is_alias: true,
+ },
+ ],
+ ),
+]
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 8c11408de..1a00e2938 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -2,9 +2,7 @@
(
Module {
id: ModuleId {
- krate: CrateId(
- 0,
- ),
+ krate: Idx::<CrateData>(0),
block: None,
local_id: Idx::<ModuleData>(0),
},
@@ -12,9 +10,18 @@
[
FileSymbol {
name: "Alias",
+ def: TypeAlias(
+ TypeAlias {
+ id: TypeAliasId(
+ 0,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: TYPE_ALIAS,
@@ -25,14 +32,23 @@
range: 402..407,
},
},
- kind: TypeAlias,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "CONST",
+ def: Const(
+ Const {
+ id: ConstId(
+ 0,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: CONST,
@@ -43,14 +59,23 @@
range: 346..351,
},
},
- kind: Const,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "CONST_WITH_INNER",
+ def: Const(
+ Const {
+ id: ConstId(
+ 2,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: CONST,
@@ -61,14 +86,25 @@
range: 526..542,
},
},
- kind: Const,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "Enum",
+ def: Adt(
+ Enum(
+ Enum {
+ id: EnumId(
+ 0,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: ENUM,
@@ -79,14 +115,25 @@
range: 190..194,
},
},
- kind: Enum,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "Macro",
+ def: Macro(
+ Macro {
+ id: Macro2Id(
+ Macro2Id(
+ 0,
+ ),
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: MACRO_DEF,
@@ -97,14 +144,23 @@
range: 159..164,
},
},
- kind: Macro,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "STATIC",
+ def: Static(
+ Static {
+ id: StaticId(
+ 0,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: STATIC,
@@ -115,14 +171,25 @@
range: 369..375,
},
},
- kind: Static,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "Struct",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 1,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -133,14 +200,27 @@
range: 177..183,
},
},
- kind: Struct,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "StructFromMacro",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 0,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 2147483648,
+ hir_file_id: MacroFile(
+ MacroFile {
+ macro_call_id: MacroCallId(
+ 0,
+ ),
+ },
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -151,14 +231,25 @@
range: 6..21,
},
},
- kind: Struct,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "StructInFn",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 4,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -169,16 +260,27 @@
range: 325..335,
},
},
- kind: Struct,
container_name: Some(
"main",
),
+ is_alias: false,
},
FileSymbol {
name: "StructInNamedConst",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 5,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -189,16 +291,27 @@
range: 562..580,
},
},
- kind: Struct,
container_name: Some(
"CONST_WITH_INNER",
),
+ is_alias: false,
},
FileSymbol {
name: "StructInUnnamedConst",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 6,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -209,14 +322,23 @@
range: 486..506,
},
},
- kind: Struct,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "Trait",
+ def: Trait(
+ Trait {
+ id: TraitId(
+ 0,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: TRAIT,
@@ -227,14 +349,25 @@
range: 267..272,
},
},
- kind: Trait,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "Union",
+ def: Adt(
+ Union(
+ Union {
+ id: UnionId(
+ 0,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: UNION,
@@ -245,14 +378,25 @@
range: 214..219,
},
},
- kind: Union,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "a_mod",
+ def: Module(
+ Module {
+ id: ModuleId {
+ krate: Idx::<CrateData>(0),
+ block: None,
+ local_id: Idx::<ModuleData>(1),
+ },
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: MODULE,
@@ -263,14 +407,25 @@
range: 423..428,
},
},
- kind: Module,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "b_mod",
+ def: Module(
+ Module {
+ id: ModuleId {
+ krate: Idx::<CrateData>(0),
+ block: None,
+ local_id: Idx::<ModuleData>(2),
+ },
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: MODULE,
@@ -281,14 +436,25 @@
range: 598..603,
},
},
- kind: Module,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "define_struct",
+ def: Macro(
+ Macro {
+ id: MacroRulesId(
+ MacroRulesId(
+ 1,
+ ),
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: MACRO_RULES,
@@ -299,14 +465,23 @@
range: 64..77,
},
},
- kind: Macro,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "impl_fn",
+ def: Function(
+ Function {
+ id: FunctionId(
+ 2,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: FN,
@@ -317,14 +492,25 @@
range: 245..252,
},
},
- kind: Function,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "macro_rules_macro",
+ def: Macro(
+ Macro {
+ id: MacroRulesId(
+ MacroRulesId(
+ 0,
+ ),
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: MACRO_RULES,
@@ -335,14 +521,23 @@
range: 14..31,
},
},
- kind: Macro,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "main",
+ def: Function(
+ Function {
+ id: FunctionId(
+ 0,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: FN,
@@ -353,14 +548,23 @@
range: 305..309,
},
},
- kind: Function,
container_name: None,
+ is_alias: false,
},
FileSymbol {
name: "trait_fn",
+ def: Function(
+ Function {
+ id: FunctionId(
+ 1,
+ ),
+ },
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: FN,
@@ -371,19 +575,17 @@
range: 282..290,
},
},
- kind: Function,
container_name: Some(
"Trait",
),
+ is_alias: false,
},
],
),
(
Module {
id: ModuleId {
- krate: CrateId(
- 0,
- ),
+ krate: Idx::<CrateData>(0),
block: None,
local_id: Idx::<ModuleData>(1),
},
@@ -391,9 +593,20 @@
[
FileSymbol {
name: "StructInModA",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 2,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 0,
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -404,17 +617,15 @@
range: 442..454,
},
},
- kind: Struct,
container_name: None,
+ is_alias: false,
},
],
),
(
Module {
id: ModuleId {
- krate: CrateId(
- 0,
- ),
+ krate: Idx::<CrateData>(0),
block: None,
local_id: Idx::<ModuleData>(2),
},
@@ -422,9 +633,20 @@
[
FileSymbol {
name: "StructInModB",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 3,
+ ),
+ },
+ ),
+ ),
loc: DeclarationLocation {
- hir_file_id: HirFileId(
- 1,
+ hir_file_id: FileId(
+ FileId(
+ 1,
+ ),
),
ptr: SyntaxNodePtr {
kind: STRUCT,
@@ -435,8 +657,8 @@
range: 7..19,
},
},
- kind: Struct,
container_name: None,
+ is_alias: false,
},
],
),
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs
new file mode 100644
index 000000000..6b49bb263
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-db/src/tests/line_index.rs
@@ -0,0 +1,49 @@
+use line_index::{LineCol, LineIndex, WideEncoding};
+use test_utils::skip_slow_tests;
+
+#[test]
+fn test_every_chars() {
+ if skip_slow_tests() {
+ return;
+ }
+
+ let text: String = {
+ let mut chars: Vec<char> = ((0 as char)..char::MAX).collect(); // Neat!
+ chars.extend("\n".repeat(chars.len() / 16).chars());
+ let mut rng = oorandom::Rand32::new(stdx::rand::seed());
+ stdx::rand::shuffle(&mut chars, |i| rng.rand_range(0..i as u32) as usize);
+ chars.into_iter().collect()
+ };
+ assert!(text.contains('💩')); // Sanity check.
+
+ let line_index = LineIndex::new(&text);
+
+ let mut lin_col = LineCol { line: 0, col: 0 };
+ let mut col_utf16 = 0;
+ let mut col_utf32 = 0;
+ for (offset, c) in text.char_indices() {
+ let got_offset = line_index.offset(lin_col).unwrap();
+ assert_eq!(usize::from(got_offset), offset);
+
+ let got_lin_col = line_index.line_col(got_offset);
+ assert_eq!(got_lin_col, lin_col);
+
+ for (enc, col) in [(WideEncoding::Utf16, col_utf16), (WideEncoding::Utf32, col_utf32)] {
+ let wide_lin_col = line_index.to_wide(enc, lin_col).unwrap();
+ let got_lin_col = line_index.to_utf8(enc, wide_lin_col).unwrap();
+ assert_eq!(got_lin_col, lin_col);
+ assert_eq!(wide_lin_col.col, col)
+ }
+
+ if c == '\n' {
+ lin_col.line += 1;
+ lin_col.col = 0;
+ col_utf16 = 0;
+ col_utf32 = 0;
+ } else {
+ lin_col.col += c.len_utf8() as u32;
+ col_utf16 += c.len_utf16() as u32;
+ col_utf32 += 1;
+ }
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
index 6a7ea7c19..9abbc3441 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/traits.rs
@@ -38,15 +38,15 @@ pub fn get_missing_assoc_items(
for item in imp.items(sema.db) {
match item {
hir::AssocItem::Function(it) => {
- impl_fns_consts.insert(it.name(sema.db).to_string());
+ impl_fns_consts.insert(it.name(sema.db).display(sema.db).to_string());
}
hir::AssocItem::Const(it) => {
if let Some(name) = it.name(sema.db) {
- impl_fns_consts.insert(name.to_string());
+ impl_fns_consts.insert(name.display(sema.db).to_string());
}
}
hir::AssocItem::TypeAlias(it) => {
- impl_type.insert(it.name(sema.db).to_string());
+ impl_type.insert(it.name(sema.db).display(sema.db).to_string());
}
}
}
@@ -57,12 +57,14 @@ pub fn get_missing_assoc_items(
.into_iter()
.filter(|i| match i {
hir::AssocItem::Function(f) => {
- !impl_fns_consts.contains(&f.name(sema.db).to_string())
+ !impl_fns_consts.contains(&f.name(sema.db).display(sema.db).to_string())
+ }
+ hir::AssocItem::TypeAlias(t) => {
+ !impl_type.contains(&t.name(sema.db).display(sema.db).to_string())
}
- hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()),
hir::AssocItem::Const(c) => c
.name(sema.db)
- .map(|n| !impl_fns_consts.contains(&n.to_string()))
+ .map(|n| !impl_fns_consts.contains(&n.display(sema.db).to_string()))
.unwrap_or_default(),
})
.collect()
@@ -137,7 +139,7 @@ mod tests {
sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
let actual = match trait_ {
- Some(trait_) => trait_.name(&db).to_string(),
+ Some(trait_) => trait_.name(&db).display(&db).to_string(),
None => String::new(),
};
expect.assert_eq(&actual);
@@ -152,7 +154,7 @@ mod tests {
let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
let actual = items
.into_iter()
- .map(|item| item.name(&db).unwrap().to_string())
+ .map(|item| item.name(&db).unwrap().display(&db).to_string())
.collect::<Vec<_>>()
.join("\n");
expect.assert_eq(&actual);
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
index 39431bed3..f96ea29ae 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
@@ -1,9 +1,9 @@
-//! Functionality for generating trivial contructors
+//! Functionality for generating trivial constructors
use hir::StructKind;
use syntax::ast;
-/// given a type return the trivial contructor (if one exists)
+/// given a type return the trivial constructor (if one exists)
pub fn use_trivial_constructor(
db: &crate::RootDatabase,
path: ast::Path,
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
index 114face2d..30576c71f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -31,12 +31,8 @@ mod tests {
fn foo() {
break;
//^^^^^ error: break outside of loop
- break 'a;
- //^^^^^^^^ error: break outside of loop
continue;
//^^^^^^^^ error: continue outside of loop
- continue 'a;
- //^^^^^^^^^^^ error: continue outside of loop
}
"#,
);
@@ -51,12 +47,8 @@ fn foo() {
async {
break;
//^^^^^ error: break outside of loop
- break 'a;
- //^^^^^^^^ error: break outside of loop
continue;
//^^^^^^^^ error: continue outside of loop
- continue 'a;
- //^^^^^^^^^^^ error: continue outside of loop
};
}
}
@@ -73,12 +65,8 @@ fn foo() {
|| {
break;
//^^^^^ error: break outside of loop
- break 'a;
- //^^^^^^^^ error: break outside of loop
continue;
//^^^^^^^^ error: continue outside of loop
- continue 'a;
- //^^^^^^^^^^^ error: continue outside of loop
};
}
}
@@ -94,9 +82,7 @@ fn foo() {
'a: loop {
{
break;
- break 'a;
continue;
- continue 'a;
}
}
}
@@ -112,9 +98,7 @@ fn foo() {
'a: loop {
try {
break;
- break 'a;
continue;
- continue 'a;
};
}
}
@@ -130,11 +114,8 @@ fn foo() {
'a: {
break;
//^^^^^ error: break outside of loop
- break 'a;
continue;
//^^^^^^^^ error: continue outside of loop
- continue 'a;
- //^^^^^^^^^^^ error: continue outside of loop
}
}
"#,
@@ -143,15 +124,35 @@ fn foo() {
#[test]
fn value_break_in_for_loop() {
+ // FIXME: the error is correct, but the message is terrible
check_diagnostics(
r#"
+//- minicore: iterator
fn test() {
for _ in [()] {
break 3;
- // ^^^^^^^ error: can't break with a value in this position
+ // ^ error: expected (), found i32
}
}
"#,
);
}
+
+ #[test]
+ fn try_block_desugaring_inside_closure() {
+ // regression test for #14701
+ check_diagnostics(
+ r#"
+//- minicore: option, try
+fn test() {
+ try {
+ || {
+ let x = Some(2);
+ Some(x?)
+ };
+ };
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
index db88bf7b9..90279e145 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
@@ -27,7 +27,7 @@ pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCas
}
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> {
- let root = ctx.sema.db.parse_or_expand(d.file)?;
+ let root = ctx.sema.db.parse_or_expand(d.file);
let name_node = d.ident.to_node(&root);
let def = NameClass::classify(&ctx.sema, &name_node)?.defined()?;
@@ -295,7 +295,7 @@ impl someStruct {
}
#[test]
- fn no_diagnostic_for_enum_varinats() {
+ fn no_diagnostic_for_enum_variants() {
check_diagnostics(
r#"
enum Option { Some, None }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
index 870c78d1f..7547779a9 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -9,6 +9,16 @@ pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) ->
Diagnostic::new("macro-error", d.message.clone(), display_range).experimental()
}
+// Diagnostic: macro-error
+//
+// This diagnostic is shown for macro expansion errors.
+pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefError) -> Diagnostic {
+ // Use more accurate position if available.
+ let display_range =
+ ctx.resolve_precise_location(&d.node.clone().map(|it| it.syntax_node_ptr()), d.name);
+ Diagnostic::new("macro-def-error", d.message.clone(), display_range).experimental()
+}
+
#[cfg(test)]
mod tests {
use crate::{
@@ -188,6 +198,7 @@ fn f() {
"#,
);
}
+
#[test]
fn dollar_crate_in_builtin_macro() {
check_diagnostics(
@@ -212,4 +223,38 @@ fn f() {
"#,
)
}
+
+ #[test]
+ fn def_diagnostic() {
+ check_diagnostics(
+ r#"
+macro_rules! foo {
+ //^^^ error: expected subtree
+ f => {};
+}
+
+fn f() {
+ foo!();
+ //^^^ error: invalid macro definition: expected subtree
+
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn expansion_syntax_diagnostic() {
+ check_diagnostics(
+ r#"
+macro_rules! foo {
+ () => { struct; };
+}
+
+fn f() {
+ foo!();
+ //^^^ error: Syntax Error in Expansion: expected a name
+}
+"#,
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 5c4327ff9..60ccc41df 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -31,7 +31,7 @@ use crate::{fix, Diagnostic, DiagnosticsContext};
pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic {
let mut message = String::from("missing structure fields:\n");
for field in &d.missed_fields {
- format_to!(message, "- {}\n", field);
+ format_to!(message, "- {}\n", field.display(ctx.sema.db));
}
let ptr = InFile::new(
@@ -56,7 +56,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
return None;
}
- let root = ctx.sema.db.parse_or_expand(d.file)?;
+ let root = ctx.sema.db.parse_or_expand(d.file);
let current_module = match &d.field_list_parent {
Either::Left(ptr) => ctx.sema.scope(ptr.to_node(&root).syntax()).map(|it| it.module()),
@@ -175,8 +175,10 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
fn make_ty(ty: &hir::Type, db: &dyn HirDatabase, module: hir::Module) -> ast::Type {
let ty_str = match ty.as_adt() {
- Some(adt) => adt.name(db).to_string(),
- None => ty.display_source_code(db, module.into()).ok().unwrap_or_else(|| "_".to_string()),
+ Some(adt) => adt.name(db).display(db.upcast()).to_string(),
+ None => {
+ ty.display_source_code(db, module.into(), false).ok().unwrap_or_else(|| "_".to_string())
+ }
};
make::ty(&ty_str)
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index ac4463331..3f13b97a4 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -271,15 +271,20 @@ enum Either2 { C, D }
fn main() {
match Either::A {
Either2::C => (),
+ //^^^^^^^^^^ error: expected Either, found Either2
Either2::D => (),
+ //^^^^^^^^^^ error: expected Either, found Either2
}
match (true, false) {
(true, false, true) => (),
+ //^^^^^^^^^^^^^^^^^^^ error: expected (bool, bool), found (bool, bool, bool)
(true) => (),
// ^^^^ error: expected (bool, bool), found bool
}
match (true, false) { (true,) => {} }
+ //^^^^^^^ error: expected (bool, bool), found (bool,)
match (0) { () => () }
+ //^^ error: expected i32, found ()
match Unresolved::Bar { Unresolved::Baz => () }
}
"#,
@@ -293,7 +298,9 @@ fn main() {
r#"
fn main() {
match false { true | () => {} }
+ //^^ error: expected bool, found ()
match (false,) { (true | (),) => {} }
+ //^^ error: expected bool, found ()
}
"#,
);
@@ -738,17 +745,13 @@ fn main() {
#[test]
fn binding_ref_has_correct_type() {
- cov_mark::check_count!(validate_match_bailed_out, 1);
-
// Asserts `PatKind::Binding(ref _x): bool`, not &bool.
// If that's not true match checking will panic with "incompatible constructors"
// FIXME: make facilities to test this directly like `tests::check_infer(..)`
- check_diagnostics(
+ check_diagnostics_no_bails(
r#"
enum Foo { A }
fn main() {
- // FIXME: this should not bail out but current behavior is such as the old algorithm.
- // ExprValidator::validate_match(..) checks types of top level patterns incorrectly.
match Foo::A {
ref _x => {}
Foo::A => {}
@@ -1024,6 +1027,7 @@ fn main() {
check_diagnostics(
r#"
+//- minicore: copy
fn main() {
match &false {
&true => {}
@@ -1035,11 +1039,13 @@ fn main() {
#[test]
fn reference_patterns_in_fields() {
- cov_mark::check_count!(validate_match_bailed_out, 2);
+ cov_mark::check_count!(validate_match_bailed_out, 1);
check_diagnostics(
r#"
+//- minicore: copy
fn main() {
match (&false,) {
+ //^^^^^^^^^ error: missing match arm: `(&false,)` not covered
(true,) => {}
}
match (&false,) {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index eb32db250..2026b6fce 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -24,7 +24,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option<Vec<Ass
return None;
}
- let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(d.expr.file_id);
let expr = d.expr.value.to_node(&root);
let node_to_add_unsafe_block = pick_best_node_to_add_unsafe_block(&expr)?;
@@ -142,6 +142,8 @@ fn main() {
fn missing_unsafe_diagnostic_with_static_mut() {
check_diagnostics(
r#"
+//- minicore: copy
+
struct Ty {
a: u8,
}
@@ -256,6 +258,7 @@ fn main() {
fn add_unsafe_block_when_accessing_mutable_static() {
check_fix(
r#"
+//- minicore: copy
struct Ty {
a: u8,
}
@@ -374,6 +377,7 @@ fn main() {
fn unsafe_expr_as_right_hand_side_of_assignment() {
check_fix(
r#"
+//- minicore: copy
static mut STATIC_MUT: u8 = 0;
fn main() {
@@ -396,6 +400,7 @@ fn main() {
fn unsafe_expr_in_binary_plus() {
check_fix(
r#"
+//- minicore: copy
static mut STATIC_MUT: u8 = 0;
fn main() {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
new file mode 100644
index 000000000..32e321107
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -0,0 +1,175 @@
+use crate::{Diagnostic, DiagnosticsContext};
+use hir::HirDisplay;
+
+// Diagnostic: moved-out-of-ref
+//
+// This diagnostic is triggered on moving non copy things out of references.
+pub(crate) fn moved_out_of_ref(ctx: &DiagnosticsContext<'_>, d: &hir::MovedOutOfRef) -> Diagnostic {
+ Diagnostic::new(
+ "moved-out-of-ref",
+ format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db)),
+ ctx.sema.diagnostics_display_range(d.span.clone()).range,
+ )
+ .experimental() // spans are broken, and I'm not sure how precise we can detect copy types
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ // FIXME: spans are broken
+
+ #[test]
+ fn move_by_explicit_deref() {
+ check_diagnostics(
+ r#"
+struct X;
+fn main() {
+ let a = &X;
+ let b = *a;
+ //^ error: cannot move `X` out of reference
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn move_out_of_field() {
+ check_diagnostics(
+ r#"
+//- minicore: copy
+struct X;
+struct Y(X, i32);
+fn main() {
+ let a = &Y(X, 5);
+ let b = a.0;
+ //^ error: cannot move `X` out of reference
+ let y = a.1;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn move_out_of_static() {
+ check_diagnostics(
+ r#"
+//- minicore: copy
+struct X;
+fn main() {
+ static S: X = X;
+ let s = S;
+ //^ error: cannot move `X` out of reference
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn generic_types() {
+ check_diagnostics(
+ r#"
+//- minicore: derive, copy
+
+#[derive(Copy)]
+struct X<T>(T);
+struct Y;
+
+fn consume<T>(_: X<T>) {
+
+}
+
+fn main() {
+ let a = &X(Y);
+ consume(*a);
+ //^^^^^^^^^^^ error: cannot move `X<Y>` out of reference
+ let a = &X(5);
+ consume(*a);
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_false_positive_simple() {
+ check_diagnostics(
+ r#"
+//- minicore: copy
+fn f(_: i32) {}
+fn main() {
+ let x = &2;
+ f(*x);
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_false_positive_unknown_type() {
+ check_diagnostics(
+ r#"
+//- minicore: derive, copy
+fn f(x: &Unknown) -> Unknown {
+ *x
+}
+
+#[derive(Copy)]
+struct X<T>(T);
+
+struct Y<T>(T);
+
+fn g(x: &X<Unknown>) -> X<Unknown> {
+ *x
+}
+
+fn h(x: &Y<Unknown>) -> Y<Unknown> {
+ // FIXME: we should show error for this, as `Y` is not copy
+ // regardless of its generic parameter.
+ *x
+}
+
+"#,
+ );
+ }
+
+ #[test]
+ fn no_false_positive_dyn_fn() {
+ check_diagnostics(
+ r#"
+//- minicore: copy, fn
+fn f(x: &mut &mut dyn Fn()) {
+ x();
+}
+
+struct X<'a> {
+ field: &'a mut dyn Fn(),
+}
+
+fn f(x: &mut X<'_>) {
+ (x.field)();
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_false_positive_match_and_closure_capture() {
+ check_diagnostics(
+ r#"
+//- minicore: copy, fn
+enum X {
+ Foo(u16),
+ Bar,
+}
+
+fn main() {
+ let x = &X::Bar;
+ let c = || match *x {
+ X::Foo(t) => t,
+ _ => 5,
+ };
+}
+ "#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 96470265d..f61460e31 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -18,7 +18,8 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno
let use_range = d.span.value.text_range();
for source in d.local.sources(ctx.sema.db) {
let Some(ast) = source.name() else { continue };
- edit_builder.insert(ast.syntax().text_range().start(), "mut ".to_string());
+ // FIXME: macros
+ edit_builder.insert(ast.value.syntax().text_range().start(), "mut ".to_string());
}
let edit = edit_builder.finish();
Some(vec![fix(
@@ -30,7 +31,10 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Diagno
})();
Diagnostic::new(
"need-mut",
- format!("cannot mutate immutable variable `{}`", d.local.name(ctx.sema.db)),
+ format!(
+ "cannot mutate immutable variable `{}`",
+ d.local.name(ctx.sema.db).display(ctx.sema.db)
+ ),
ctx.sema.diagnostics_display_range(d.span.clone()).range,
)
.with_fixes(fixes)
@@ -340,6 +344,7 @@ fn main() {
fn regression_14310() {
check_diagnostics(
r#"
+ //- minicore: copy, builtin_impls
fn clone(mut i: &!) -> ! {
//^^^^^ 💡 weak: variable does not need to be mutable
*i
@@ -349,6 +354,32 @@ fn main() {
}
#[test]
+ fn match_closure_capture() {
+ check_diagnostics(
+ r#"
+//- minicore: option
+fn main() {
+ let mut v = &mut Some(2);
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let _ = || match v {
+ Some(k) => {
+ *k = 5;
+ }
+ None => {}
+ };
+ let v = &mut Some(2);
+ let _ = || match v {
+ //^ 💡 error: cannot mutate immutable variable `v`
+ ref mut k => {
+ *k = &mut Some(5);
+ }
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
fn match_bindings() {
check_diagnostics(
r#"
@@ -368,7 +399,7 @@ fn main() {
#[test]
fn mutation_in_dead_code() {
// This one is interesting. Dead code is not represented at all in the MIR, so
- // there would be no mutablility error for locals in dead code. Rustc tries to
+ // there would be no mutability error for locals in dead code. Rustc tries to
// not emit `unused_mut` in this case, but since it works without `mut`, and
// special casing it is not trivial, we emit it.
check_diagnostics(
@@ -485,6 +516,38 @@ fn main() {
);
check_diagnostics(
r#"
+fn check(_: i32) -> bool {
+ false
+}
+fn main() {
+ loop {
+ let x = 1;
+ if check(x) {
+ break;
+ }
+ let y = (1, 2);
+ if check(y.1) {
+ return;
+ }
+ let z = (1, 2);
+ match z {
+ (k @ 5, ref mut t) if { continue; } => {
+ //^^^^^^^^^ 💡 error: cannot mutate immutable variable `z`
+ *t = 5;
+ }
+ _ => {
+ let y = (1, 2);
+ if check(y.1) {
+ return;
+ }
+ }
+ }
+ }
+}
+"#,
+ );
+ check_diagnostics(
+ r#"
fn f(_: i32) {}
fn main() {
loop {
@@ -546,13 +609,35 @@ fn f(x: i32) {
}
"#,
);
+ check_diagnostics(
+ r#"
+fn f((x, y): (i32, i32)) {
+ let t = [0; 2];
+ x = 5;
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_diagnostics_in_case_of_multiple_bounds() {
+ check_diagnostics(
+ r#"
+fn f() {
+ let (b, a, b) = (2, 3, 5);
+ a = 8;
+ //^^^^^ 💡 error: cannot mutate immutable variable `a`
+}
+"#,
+ );
}
#[test]
fn for_loop() {
check_diagnostics(
r#"
-//- minicore: iterators
+//- minicore: iterators, copy
fn f(x: [(i32, u8); 10]) {
for (a, mut b) in x {
//^^^^^ 💡 weak: variable does not need to be mutable
@@ -565,8 +650,96 @@ fn f(x: [(i32, u8); 10]) {
}
#[test]
+ fn while_let() {
+ check_diagnostics(
+ r#"
+//- minicore: iterators, copy
+fn f(x: [(i32, u8); 10]) {
+ let mut it = x.into_iter();
+ while let Some((a, mut b)) = it.next() {
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ while let Some((c, mut d)) = it.next() {
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ a = 2;
+ //^^^^^ 💡 error: cannot mutate immutable variable `a`
+ c = 2;
+ //^^^^^ 💡 error: cannot mutate immutable variable `c`
+ }
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn index() {
+ check_diagnostics(
+ r#"
+//- minicore: coerce_unsized, index, slice
+fn f() {
+ let x = [1, 2, 3];
+ x[2] = 5;
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let x = &mut x;
+ //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let mut x = x;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ x[2] = 5;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn overloaded_index() {
+ check_diagnostics(
+ r#"
+//- minicore: index
+use core::ops::{Index, IndexMut};
+
+struct Foo;
+impl Index<usize> for Foo {
+ type Output = (i32, u8);
+ fn index(&self, index: usize) -> &(i32, u8) {
+ &(5, 2)
+ }
+}
+impl IndexMut<usize> for Foo {
+ fn index_mut(&mut self, index: usize) -> &mut (i32, u8) {
+ &mut (5, 2)
+ }
+}
+fn f() {
+ let mut x = Foo;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let y = &x[2];
+ let x = Foo;
+ let y = &mut x[2];
+ //^💡 error: cannot mutate immutable variable `x`
+ let mut x = &mut Foo;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let y: &mut (i32, u8) = &mut x[2];
+ let x = Foo;
+ let ref mut y = x[7];
+ //^ 💡 error: cannot mutate immutable variable `x`
+ let (ref mut y, _) = x[3];
+ //^ 💡 error: cannot mutate immutable variable `x`
+ match x[10] {
+ //^ 💡 error: cannot mutate immutable variable `x`
+ (ref y, _) => (),
+ (_, ref mut y) => (),
+ }
+ let mut x = Foo;
+ let mut i = 5;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let y = &mut x[i];
+}
+"#,
+ );
+ }
+
+ #[test]
fn overloaded_deref() {
- // FIXME: check for false negative
check_diagnostics(
r#"
//- minicore: deref_mut
@@ -574,22 +747,36 @@ use core::ops::{Deref, DerefMut};
struct Foo;
impl Deref for Foo {
- type Target = i32;
- fn deref(&self) -> &i32 {
- &5
+ type Target = (i32, u8);
+ fn deref(&self) -> &(i32, u8) {
+ &(5, 2)
}
}
impl DerefMut for Foo {
- fn deref_mut(&mut self) -> &mut i32 {
- &mut 5
+ fn deref_mut(&mut self) -> &mut (i32, u8) {
+ &mut (5, 2)
}
}
fn f() {
- let x = Foo;
+ let mut x = Foo;
+ //^^^^^ 💡 weak: variable does not need to be mutable
let y = &*x;
let x = Foo;
- let mut x = Foo;
- let y: &mut i32 = &mut x;
+ let y = &mut *x;
+ //^^ 💡 error: cannot mutate immutable variable `x`
+ let x = Foo;
+ let x = Foo;
+ let y: &mut (i32, u8) = &mut x;
+ //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let ref mut y = *x;
+ //^^ 💡 error: cannot mutate immutable variable `x`
+ let (ref mut y, _) = *x;
+ //^^ 💡 error: cannot mutate immutable variable `x`
+ match *x {
+ //^^ 💡 error: cannot mutate immutable variable `x`
+ (ref y, _) => (),
+ (_, ref mut y) => (),
+ }
}
"#,
);
@@ -632,6 +819,267 @@ fn f(inp: (Foo, Foo, Foo, Foo)) {
}
#[test]
+ // FIXME: We should have tests for `is_ty_uninhabited_from`
+ fn regression_14421() {
+ check_diagnostics(
+ r#"
+pub enum Tree {
+ Node(TreeNode),
+ Leaf(TreeLeaf),
+}
+
+struct Box<T>(&T);
+
+pub struct TreeNode {
+ pub depth: usize,
+ pub children: [Box<Tree>; 8]
+}
+
+pub struct TreeLeaf {
+ pub depth: usize,
+ pub data: u8
+}
+
+pub fn test() {
+ let mut tree = Tree::Leaf(
+ //^^^^^^^^ 💡 weak: variable does not need to be mutable
+ TreeLeaf {
+ depth: 0,
+ data: 0
+ }
+ );
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn fn_traits() {
+ check_diagnostics(
+ r#"
+//- minicore: fn
+fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 {
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ x(2)
+}
+fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 {
+ x(2)
+ //^ 💡 error: cannot mutate immutable variable `x`
+}
+fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 {
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ x(2)
+}
+fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ x(2)
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn closure() {
+ // FIXME: Diagnostic spans are inconsistent inside and outside closure
+ check_diagnostics(
+ r#"
+ //- minicore: copy, fn
+ struct X;
+
+ impl X {
+ fn mutate(&mut self) {}
+ }
+
+ fn f() {
+ let x = 5;
+ let closure1 = || { x = 2; };
+ //^ 💡 error: cannot mutate immutable variable `x`
+ let _ = closure1();
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+ let closure2 = || { x = x; };
+ //^ 💡 error: cannot mutate immutable variable `x`
+ let closure3 = || {
+ let x = 2;
+ x = 5;
+ //^^^^^ 💡 error: cannot mutate immutable variable `x`
+ x
+ };
+ let x = X;
+ let closure4 = || { x.mutate(); };
+ //^ 💡 error: cannot mutate immutable variable `x`
+ }
+ "#,
+ );
+ check_diagnostics(
+ r#"
+ //- minicore: copy, fn
+ fn f() {
+ let mut x = 5;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let mut y = 2;
+ y = 7;
+ let closure = || {
+ let mut z = 8;
+ z = 3;
+ let mut k = z;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ };
+ }
+ "#,
+ );
+ check_diagnostics(
+ r#"
+//- minicore: copy, fn
+fn f() {
+ let closure = || {
+ || {
+ || {
+ let x = 2;
+ || { || { x = 5; } }
+ //^ 💡 error: cannot mutate immutable variable `x`
+ }
+ }
+ };
+}
+ "#,
+ );
+ check_diagnostics(
+ r#"
+//- minicore: copy, fn
+fn f() {
+ struct X;
+ let mut x = X;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let c1 = || x;
+ let mut x = X;
+ let c2 = || { x = X; x };
+ let mut x = X;
+ let c2 = move || { x = X; };
+}
+ "#,
+ );
+ check_diagnostics(
+ r#"
+ //- minicore: copy, fn, deref_mut
+ struct X(i32, i64);
+
+ fn f() {
+ let mut x = &mut 5;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let closure1 = || { *x = 2; };
+ let _ = closure1();
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+ let mut x = &mut 5;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let closure1 = || { *x = 2; &x; };
+ let _ = closure1();
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+ let mut x = &mut 5;
+ let closure1 = || { *x = 2; &x; x = &mut 3; };
+ let _ = closure1();
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+ let mut x = &mut 5;
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let closure1 = move || { *x = 2; };
+ let _ = closure1();
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+ let mut x = &mut X(1, 2);
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ let closure1 = || { x.0 = 2; };
+ let _ = closure1();
+ //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1`
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn slice_pattern() {
+ check_diagnostics(
+ r#"
+//- minicore: coerce_unsized, deref_mut, slice, copy
+fn x(t: &[u8]) {
+ match t {
+ &[a, mut b] | &[a, _, mut b] => {
+ //^^^^^ 💡 weak: variable does not need to be mutable
+
+ a = 2;
+ //^^^^^ 💡 error: cannot mutate immutable variable `a`
+
+ }
+ _ => {}
+ }
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn boxes() {
+ check_diagnostics(
+ r#"
+//- minicore: coerce_unsized, deref_mut, slice
+use core::ops::{Deref, DerefMut};
+use core::{marker::Unsize, ops::CoerceUnsized};
+
+#[lang = "owned_box"]
+pub struct Box<T: ?Sized> {
+ inner: *mut T,
+}
+impl<T> Box<T> {
+ fn new(t: T) -> Self {
+ #[rustc_box]
+ Box::new(t)
+ }
+}
+
+impl<T: ?Sized> Deref for Box<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &**self
+ }
+}
+
+impl<T: ?Sized> DerefMut for Box<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut **self
+ }
+}
+
+fn f() {
+ let x = Box::new(5);
+ x = Box::new(7);
+ //^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let x = Box::new(5);
+ *x = 7;
+ //^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let mut y = Box::new(5);
+ //^^^^^ 💡 weak: variable does not need to be mutable
+ *x = *y;
+ //^^^^^^^ 💡 error: cannot mutate immutable variable `x`
+ let x = Box::new(5);
+ let closure = || *x = 2;
+ //^ 💡 error: cannot mutate immutable variable `x`
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn allow_unused_mut_for_identifiers_starting_with_underline() {
+ check_diagnostics(
+ r#"
+fn f(_: i32) {}
+fn main() {
+ let mut _x = 2;
+ f(_x);
+}
+"#,
+ );
+ }
+
+ #[test]
fn respect_allow_unused_mut() {
// FIXME: respect
check_diagnostics(
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
index 24c521ed1..a39eceab2 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -21,7 +21,7 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField)
}
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> {
- let root = ctx.sema.db.parse_or_expand(d.field.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(d.field.file_id);
missing_record_expr_field_fixes(
&ctx.sema,
d.field.file_id.original_file(ctx.sema.db),
@@ -69,7 +69,7 @@ fn missing_record_expr_field_fixes(
let new_field = make::record_field(
None,
make::name(record_expr_field.field_name()?.ident_token()?.text()),
- make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?),
+ make::ty(&new_field_type.display_source_code(sema.db, module.into(), true).ok()?),
);
let last_field = record_fields.fields().last()?;
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
index 67da5c7f2..4cd85a479 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
@@ -11,7 +11,11 @@ pub(crate) fn private_assoc_item(
d: &hir::PrivateAssocItem,
) -> Diagnostic {
// FIXME: add quickfix
- let name = d.item.name(ctx.sema.db).map(|name| format!("`{name}` ")).unwrap_or_default();
+ let name = d
+ .item
+ .name(ctx.sema.db)
+ .map(|name| format!("`{}` ", name.display(ctx.sema.db)))
+ .unwrap_or_default();
Diagnostic::new(
"private-assoc-item",
format!(
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
index be83ad6aa..de7f51f69 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/private_field.rs
@@ -9,8 +9,8 @@ pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField)
"private-field",
format!(
"field `{}` of `{}` is private",
- d.field.name(ctx.sema.db),
- d.field.parent_def(ctx.sema.db).name(ctx.sema.db)
+ d.field.name(ctx.sema.db).display(ctx.sema.db),
+ d.field.parent_def(ctx.sema.db).name(ctx.sema.db).display(ctx.sema.db)
),
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
)
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index 9b1c65983..d3eda3c5e 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -28,7 +28,7 @@ fn fixes(
ctx: &DiagnosticsContext<'_>,
d: &hir::ReplaceFilterMapNextWithFindMap,
) -> Option<Vec<Assist>> {
- let root = ctx.sema.db.parse_or_expand(d.file)?;
+ let root = ctx.sema.db.parse_or_expand(d.file);
let next_expr = d.next_expr.to_node(&root);
let next_call = ast::MethodCallExpr::cast(next_expr.syntax().clone())?;
@@ -115,7 +115,7 @@ fn foo() {
r#"
//- minicore: iterators
fn foo() {
- let m = core::iter::repeat(())
+ let mut m = core::iter::repeat(())
.filter_map(|()| Some(92));
let n = m.next();
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 4abc25a28..c28f98d83 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1,5 +1,5 @@
use either::Either;
-use hir::{db::ExpandDatabase, HirDisplay, InFile, Type};
+use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, InFile, Type};
use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
use syntax::{
ast::{self, BlockExpr, ExprStmt},
@@ -15,15 +15,25 @@ use crate::{adjusted_display_range, fix, Assist, Diagnostic, DiagnosticsContext}
// the expected type.
pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
let display_range = match &d.expr_or_pat {
- Either::Left(expr) => adjusted_display_range::<ast::BlockExpr>(
- ctx,
- expr.clone().map(|it| it.into()),
- &|block| {
- let r_curly_range = block.stmt_list()?.r_curly_token()?.text_range();
- cov_mark::hit!(type_mismatch_on_block);
- Some(r_curly_range)
- },
- ),
+ Either::Left(expr) => {
+ adjusted_display_range::<ast::Expr>(ctx, expr.clone().map(|it| it.into()), &|expr| {
+ let salient_token_range = match expr {
+ ast::Expr::IfExpr(it) => it.if_token()?.text_range(),
+ ast::Expr::LoopExpr(it) => it.loop_token()?.text_range(),
+ ast::Expr::ForExpr(it) => it.for_token()?.text_range(),
+ ast::Expr::WhileExpr(it) => it.while_token()?.text_range(),
+ ast::Expr::BlockExpr(it) => it.stmt_list()?.r_curly_token()?.text_range(),
+ ast::Expr::MatchExpr(it) => it.match_token()?.text_range(),
+ ast::Expr::MethodCallExpr(it) => it.name_ref()?.ident_token()?.text_range(),
+ ast::Expr::FieldExpr(it) => it.name_ref()?.ident_token()?.text_range(),
+ ast::Expr::AwaitExpr(it) => it.await_token()?.text_range(),
+ _ => return None,
+ };
+
+ cov_mark::hit!(type_mismatch_range_adjustment);
+ Some(salient_token_range)
+ })
+ }
Either::Right(pat) => {
ctx.sema.diagnostics_display_range(pat.clone().map(|it| it.into())).range
}
@@ -32,8 +42,8 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch)
"type-mismatch",
format!(
"expected {}, found {}",
- d.expected.display(ctx.sema.db),
- d.actual.display(ctx.sema.db)
+ d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId),
+ d.actual.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId),
),
display_range,
)
@@ -93,7 +103,7 @@ fn add_missing_ok_or_some(
expr_ptr: &InFile<AstPtr<ast::Expr>>,
acc: &mut Vec<Assist>,
) -> Option<()> {
- let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
let expr_range = expr.syntax().text_range();
let scope = ctx.sema.scope(expr.syntax())?;
@@ -133,7 +143,7 @@ fn remove_semicolon(
expr_ptr: &InFile<AstPtr<ast::Expr>>,
acc: &mut Vec<Assist>,
) -> Option<()> {
- let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
if !d.actual.is_unit() {
return None;
@@ -169,7 +179,7 @@ fn str_ref_to_owned(
return None;
}
- let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
let expr_range = expr.syntax().text_range();
@@ -597,8 +607,21 @@ fn test() -> String {
}
#[test]
- fn type_mismatch_on_block() {
- cov_mark::check!(type_mismatch_on_block);
+ fn closure_mismatch_show_different_type() {
+ check_diagnostics(
+ r#"
+fn f() {
+ let mut x = (|| 1, 2);
+ x = (|| 3, 4);
+ //^^^^ error: expected {closure#0}, found {closure#1}
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn type_mismatch_range_adjustment() {
+ cov_mark::check!(type_mismatch_range_adjustment);
check_diagnostics(
r#"
fn f() -> i32 {
@@ -607,6 +630,57 @@ fn f() -> i32 {
let _ = x + y;
}
//^ error: expected i32, found ()
+
+fn g() -> i32 {
+ while true {}
+} //^^^^^ error: expected i32, found ()
+
+struct S;
+impl S { fn foo(&self) -> &S { self } }
+fn h() {
+ let _: i32 = S.foo().foo().foo();
+} //^^^ error: expected i32, found &S
+"#,
+ );
+ }
+
+ #[test]
+ fn unknown_type_in_function_signature() {
+ check_diagnostics(
+ r#"
+struct X<T>(T);
+
+fn foo(x: X<Unknown>) {}
+fn test1() {
+ // Unknown might be `i32`, so we should not emit type mismatch here.
+ foo(X(42));
+}
+fn test2() {
+ foo(42);
+ //^^ error: expected X<{unknown}>, found i32
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn evaluate_const_generics_in_types() {
+ check_diagnostics(
+ r#"
+pub const ONE: usize = 1;
+
+pub struct Inner<const P: usize>();
+
+pub struct Outer {
+ pub inner: Inner<ONE>,
+}
+
+fn main() {
+ _ = Outer {
+ inner: Inner::<2>(),
+ //^^^^^^^^^^^^ error: expected Inner<1>, found Inner<2>
+ };
+}
"#,
);
}
@@ -617,12 +691,49 @@ fn f() -> i32 {
r#"
fn f() {
let &() = &mut ();
+ //^^^ error: expected &mut (), found &()
match &() {
+ // FIXME: we should only show the deep one.
&9 => ()
+ //^^ error: expected &(), found &i32
//^ error: expected (), found i32
}
}
"#,
);
}
+
+ #[test]
+ fn regression_14768() {
+ check_diagnostics(
+ r#"
+//- minicore: derive, fmt, slice, coerce_unsized, builtin_impls
+use core::fmt::Debug;
+
+#[derive(Debug)]
+struct Foo(u8, u16, [u8]);
+
+#[derive(Debug)]
+struct Bar {
+ f1: u8,
+ f2: &[u16],
+ f3: dyn Debug,
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn return_no_value() {
+ check_diagnostics(
+ r#"
+fn f() -> i32 {
+ return;
+ // ^^^^^^ error: expected i32, found ()
+ 0
+}
+fn g() { return; }
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
new file mode 100644
index 000000000..e12bbcf68
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -0,0 +1,232 @@
+use hir::{db::ExpandDatabase, ClosureStyle, HirDisplay, StructKind};
+use ide_db::{
+ assists::{Assist, AssistId, AssistKind, GroupLabel},
+ label::Label,
+ source_change::SourceChange,
+};
+use syntax::AstNode;
+use text_edit::TextEdit;
+
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: typed-hole
+//
+// This diagnostic is triggered when an underscore expression is used in an invalid position.
+pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Diagnostic {
+ let display_range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into()));
+ let (message, fixes) = if d.expected.is_unknown() {
+ ("`_` expressions may only appear on the left-hand side of an assignment".to_owned(), None)
+ } else {
+ (
+ format!(
+ "invalid `_` expression, expected type `{}`",
+ d.expected.display(ctx.sema.db).with_closure_style(ClosureStyle::ClosureWithId),
+ ),
+ fixes(ctx, d),
+ )
+ };
+
+ Diagnostic::new("typed-hole", message, display_range.range).with_fixes(fixes)
+}
+
+fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>> {
+ let db = ctx.sema.db;
+ let root = db.parse_or_expand(d.expr.file_id);
+ let original_range =
+ d.expr.as_ref().map(|it| it.to_node(&root)).syntax().original_file_range_opt(db)?;
+ let scope = ctx.sema.scope(d.expr.value.to_node(&root).syntax())?;
+ let mut assists = vec![];
+ scope.process_all_names(&mut |name, def| {
+ let ty = match def {
+ hir::ScopeDef::ModuleDef(it) => match it {
+ hir::ModuleDef::Function(it) => it.ty(db),
+ hir::ModuleDef::Adt(hir::Adt::Struct(it)) if it.kind(db) != StructKind::Record => {
+ it.constructor_ty(db)
+ }
+ hir::ModuleDef::Variant(it) if it.kind(db) != StructKind::Record => {
+ it.constructor_ty(db)
+ }
+ hir::ModuleDef::Const(it) => it.ty(db),
+ hir::ModuleDef::Static(it) => it.ty(db),
+ _ => return,
+ },
+ hir::ScopeDef::GenericParam(hir::GenericParam::ConstParam(it)) => it.ty(db),
+ hir::ScopeDef::Local(it) => it.ty(db),
+ _ => return,
+ };
+ // FIXME: should also check coercions if it is at a coercion site
+ if !ty.contains_unknown() && ty.could_unify_with(db, &d.expected) {
+ assists.push(Assist {
+ id: AssistId("typed-hole", AssistKind::QuickFix),
+ label: Label::new(format!("Replace `_` with `{}`", name.display(db))),
+ group: Some(GroupLabel("Replace `_` with a matching entity in scope".to_owned())),
+ target: original_range.range,
+ source_change: Some(SourceChange::from_text_edit(
+ original_range.file_id,
+ TextEdit::replace(original_range.range, name.display(db).to_string()),
+ )),
+ trigger_signature_help: false,
+ });
+ }
+ });
+ if assists.is_empty() {
+ None
+ } else {
+ Some(assists)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_diagnostics, check_fixes};
+
+ #[test]
+ fn unknown() {
+ check_diagnostics(
+ r#"
+fn main() {
+ _;
+ //^ error: `_` expressions may only appear on the left-hand side of an assignment
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn concrete_expectation() {
+ check_diagnostics(
+ r#"
+fn main() {
+ if _ {}
+ //^ error: invalid `_` expression, expected type `bool`
+ let _: fn() -> i32 = _;
+ //^ error: invalid `_` expression, expected type `fn() -> i32`
+ let _: fn() -> () = _; // FIXME: This should trigger an assist because `main` matches via *coercion*
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn integer_ty_var() {
+ check_diagnostics(
+ r#"
+fn main() {
+ let mut x = 3;
+ x = _;
+ //^ 💡 error: invalid `_` expression, expected type `i32`
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn ty_var_resolved() {
+ check_diagnostics(
+ r#"
+fn main() {
+ let mut x = t();
+ x = _;
+ //^ 💡 error: invalid `_` expression, expected type `&str`
+ x = "";
+}
+fn t<T>() -> T { loop {} }
+"#,
+ );
+ }
+
+ #[test]
+ fn valid_positions() {
+ check_diagnostics(
+ r#"
+fn main() {
+ let x = [(); _];
+ let y: [(); 10] = [(); _];
+ _ = 0;
+ (_,) = (1,);
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn check_quick_fix() {
+ check_fixes(
+ r#"
+enum Foo {
+ Bar
+}
+use Foo::Bar;
+const C: Foo = Foo::Bar;
+fn main<const CP: Foo>(param: Foo) {
+ let local = Foo::Bar;
+ let _: Foo = _$0;
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ vec![
+ r#"
+enum Foo {
+ Bar
+}
+use Foo::Bar;
+const C: Foo = Foo::Bar;
+fn main<const CP: Foo>(param: Foo) {
+ let local = Foo::Bar;
+ let _: Foo = local;
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ r#"
+enum Foo {
+ Bar
+}
+use Foo::Bar;
+const C: Foo = Foo::Bar;
+fn main<const CP: Foo>(param: Foo) {
+ let local = Foo::Bar;
+ let _: Foo = param;
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ r#"
+enum Foo {
+ Bar
+}
+use Foo::Bar;
+const C: Foo = Foo::Bar;
+fn main<const CP: Foo>(param: Foo) {
+ let local = Foo::Bar;
+ let _: Foo = CP;
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ r#"
+enum Foo {
+ Bar
+}
+use Foo::Bar;
+const C: Foo = Foo::Bar;
+fn main<const CP: Foo>(param: Foo) {
+ let local = Foo::Bar;
+ let _: Foo = Bar;
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ r#"
+enum Foo {
+ Bar
+}
+use Foo::Bar;
+const C: Foo = Foo::Bar;
+fn main<const CP: Foo>(param: Foo) {
+ let local = Foo::Bar;
+ let _: Foo = C;
+ //^ error: invalid `_` expression, expected type `fn()`
+}
+"#,
+ ],
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
new file mode 100644
index 000000000..034e4fcfb
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -0,0 +1,88 @@
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: undeclared-label
+pub(crate) fn undeclared_label(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::UndeclaredLabel,
+) -> Diagnostic {
+ let name = &d.name;
+ Diagnostic::new(
+ "undeclared-label",
+ format!("use of undeclared label `{}`", name.display(ctx.sema.db)),
+ ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range,
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn smoke_test() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ break 'a;
+ //^^^^^^^^ error: break outside of loop
+ //^^ error: use of undeclared label `'a`
+ continue 'a;
+ //^^^^^^^^^^^ error: continue outside of loop
+ //^^ error: use of undeclared label `'a`
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn for_loop() {
+ check_diagnostics(
+ r#"
+//- minicore: iterator
+fn foo() {
+ 'xxx: for _ in unknown {
+ 'yyy: for _ in unknown {
+ break 'xxx;
+ continue 'yyy;
+ break 'zzz;
+ //^^^^ error: use of undeclared label `'zzz`
+ }
+ continue 'xxx;
+ continue 'yyy;
+ //^^^^ error: use of undeclared label `'yyy`
+ break 'xxx;
+ break 'yyy;
+ //^^^^ error: use of undeclared label `'yyy`
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn try_operator_desugar_works() {
+ check_diagnostics(
+ r#"
+//- minicore: option, try
+fn foo() {
+ None?;
+}
+"#,
+ );
+ check_diagnostics(
+ r#"
+//- minicore: option, try, future
+async fn foo() {
+ None?;
+}
+"#,
+ );
+ check_diagnostics(
+ r#"
+//- minicore: option, try, future, fn
+async fn foo() {
+ || None?;
+}
+"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
index 3d45a7591..271e7ce73 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -2,7 +2,7 @@
use std::iter;
-use hir::{db::DefDatabase, InFile, ModuleSource};
+use hir::{db::DefDatabase, DefMap, InFile, ModuleSource};
use ide_db::{
base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
source_change::SourceChange,
@@ -10,7 +10,7 @@ use ide_db::{
};
use syntax::{
ast::{self, edit::IndentLevel, HasModuleItem, HasName},
- AstNode, TextRange, TextSize,
+ AstNode, TextRange,
};
use text_edit::TextEdit;
@@ -27,14 +27,28 @@ pub(crate) fn unlinked_file(
) {
// Limit diagnostic to the first few characters in the file. This matches how VS Code
// renders it with the full span, but on other editors, and is less invasive.
+ let fixes = fixes(ctx, file_id);
+ // FIXME: This is a hack for the vscode extension to notice whether there is an autofix or not before having to resolve diagnostics.
+ // This is to prevent project linking popups from appearing when there is an autofix. https://github.com/rust-lang/rust-analyzer/issues/14523
+ let message = if fixes.is_none() {
+ "file not included in crate hierarchy"
+ } else {
+ "file not included in module tree"
+ };
+
let range = ctx.sema.db.parse(file_id).syntax_node().text_range();
- // FIXME: This is wrong if one of the first three characters is not ascii: `//Ы`.
- let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range);
+ let range = FileLoader::file_text(ctx.sema.db, file_id)
+ .char_indices()
+ .take(3)
+ .last()
+ .map(|(i, _)| i)
+ .map(|i| TextRange::up_to(i.try_into().unwrap()))
+ .unwrap_or(range);
acc.push(
- Diagnostic::new("unlinked-file", "file not included in module tree", range)
+ Diagnostic::new("unlinked-file", message, range)
.severity(Severity::WeakWarning)
- .with_fixes(fixes(ctx, file_id)),
+ .with_fixes(fixes),
);
}
@@ -60,7 +74,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option<Vec<Assist>> {
'crates: for &krate in &*ctx.sema.db.relevant_crates(file_id) {
let crate_def_map = ctx.sema.db.crate_def_map(krate);
- let root_module = &crate_def_map[crate_def_map.root()];
+ let root_module = &crate_def_map[DefMap::ROOT];
let Some(root_file_id) = root_module.origin.file_id() else { continue };
let Some(crate_root_path) = source_root.path_for_file(&root_file_id) else { continue };
let Some(rel) = parent.strip_prefix(&crate_root_path.parent()?) else { continue };
@@ -92,7 +106,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option<Vec<Assist>> {
// if we aren't adding to a crate root, walk backwards such that we support `#[path = ...]` overrides if possible
// build all parent paths of the form `../module_name/mod.rs` and `../module_name.rs`
- let paths = iter::successors(Some(parent.clone()), |prev| prev.parent()).filter_map(|path| {
+ let paths = iter::successors(Some(parent), |prev| prev.parent()).filter_map(|path| {
let parent = path.parent()?;
let (name, _) = path.name_and_extension()?;
Some(([parent.join(&format!("{name}.rs"))?, path.join("mod.rs")?], name.to_owned()))
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
new file mode 100644
index 000000000..9fedadeae
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unreachable_label.rs
@@ -0,0 +1,91 @@
+use crate::{Diagnostic, DiagnosticsContext};
+
+// Diagnostic: unreachable-label
+pub(crate) fn unreachable_label(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::UnreachableLabel,
+) -> Diagnostic {
+ let name = &d.name;
+ Diagnostic::new(
+ "unreachable-label",
+ format!("use of unreachable label `{}`", name.display(ctx.sema.db)),
+ ctx.sema.diagnostics_display_range(d.node.clone().map(|it| it.into())).range,
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn async_blocks_are_borders() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ 'a: loop {
+ async {
+ break 'a;
+ //^^^^^^^^ error: break outside of loop
+ // ^^ error: use of unreachable label `'a`
+ continue 'a;
+ //^^^^^^^^^^^ error: continue outside of loop
+ // ^^ error: use of unreachable label `'a`
+ };
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn closures_are_borders() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ 'a: loop {
+ || {
+ break 'a;
+ //^^^^^^^^ error: break outside of loop
+ // ^^ error: use of unreachable label `'a`
+ continue 'a;
+ //^^^^^^^^^^^ error: continue outside of loop
+ // ^^ error: use of unreachable label `'a`
+ };
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn blocks_pass_through() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ 'a: loop {
+ {
+ break 'a;
+ continue 'a;
+ }
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn try_blocks_pass_through() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ 'a: loop {
+ try {
+ break 'a;
+ continue 'a;
+ };
+ }
+}
+"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index cefa74e52..5e4efa41f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -26,7 +26,7 @@ pub(crate) fn unresolved_field(
"unresolved-field",
format!(
"no field `{}` on type `{}`{method_suffix}",
- d.name,
+ d.name.display(ctx.sema.db),
d.receiver.display(ctx.sema.db)
),
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
@@ -45,12 +45,12 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField) -> Option<Vec<A
}
}
-// FIXME: We should fill out the call here, mvoe the cursor and trigger signature help
+// FIXME: We should fill out the call here, move the cursor and trigger signature help
fn method_fix(
ctx: &DiagnosticsContext<'_>,
expr_ptr: &InFile<AstPtr<ast::Expr>>,
) -> Option<Vec<Assist>> {
- let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
let FileRange { range, file_id } = ctx.sema.original_range_opt(expr.syntax())?;
Some(vec![Assist {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
index 1a5efff2c..3943b51ab 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
@@ -13,7 +13,7 @@ pub(crate) fn unresolved_macro_call(
let bang = if d.is_bang { "!" } else { "" };
Diagnostic::new(
"unresolved-macro-call",
- format!("unresolved macro `{}{bang}`", d.path),
+ format!("unresolved macro `{}{bang}`", d.path.display(ctx.sema.db)),
display_range,
)
.experimental()
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index f3ec6efa7..8bbb837e6 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -26,7 +26,7 @@ pub(crate) fn unresolved_method(
"unresolved-method",
format!(
"no method `{}` on type `{}`{field_suffix}",
- d.name,
+ d.name.display(ctx.sema.db),
d.receiver.display(ctx.sema.db)
),
ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
@@ -53,7 +53,7 @@ fn field_fix(
return None;
}
let expr_ptr = &d.expr;
- let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
let (file_id, range) = match expr {
ast::Expr::MethodCallExpr(mcall) => {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs
index 94614f11c..6e3fd3b42 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_module.rs
@@ -31,7 +31,7 @@ pub(crate) fn unresolved_module(
}
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> {
- let root = ctx.sema.db.parse_or_expand(d.decl.file_id)?;
+ let root = ctx.sema.db.parse_or_expand(d.decl.file_id);
let unresolved_module = d.decl.value.to_node(&root);
Some(
d.candidates
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
index 9a984ba6b..ae5cf1358 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs
@@ -25,25 +25,21 @@ pub(crate) fn unresolved_proc_macro(
_ => proc_macros_enabled,
};
- let message = match &d.macro_name {
+ let not_expanded_message = match &d.macro_name {
Some(name) => format!("proc macro `{name}` not expanded"),
None => "proc macro not expanded".to_string(),
};
let severity = if config_enabled { Severity::Error } else { Severity::WeakWarning };
let def_map = ctx.sema.db.crate_def_map(d.krate);
- let message = format!(
- "{message}: {}",
- if config_enabled {
- def_map.proc_macro_loading_error().unwrap_or("proc macro not found in the built dylib")
- } else {
- match d.kind {
- hir::MacroKind::Attr if proc_macros_enabled => {
- "attribute macro expansion is disabled"
- }
- _ => "proc-macro expansion is disabled",
- }
- },
- );
+ let message = if config_enabled {
+ def_map.proc_macro_loading_error().unwrap_or("proc macro not found in the built dylib")
+ } else {
+ match d.kind {
+ hir::MacroKind::Attr if proc_macros_enabled => "attribute macro expansion is disabled",
+ _ => "proc-macro expansion is disabled",
+ }
+ };
+ let message = format!("{not_expanded_message}: {message}");
Diagnostic::new("unresolved-proc-macro", message, display_range).severity(severity)
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index 71f136b8c..55a4a482d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -38,11 +38,13 @@ mod handlers {
pub(crate) mod missing_fields;
pub(crate) mod missing_match_arms;
pub(crate) mod missing_unsafe;
+ pub(crate) mod moved_out_of_ref;
pub(crate) mod mutability_errors;
pub(crate) mod no_such_field;
pub(crate) mod private_assoc_item;
pub(crate) mod private_field;
pub(crate) mod replace_filter_map_next_with_find_map;
+ pub(crate) mod typed_hole;
pub(crate) mod type_mismatch;
pub(crate) mod unimplemented_builtin_macro;
pub(crate) mod unresolved_extern_crate;
@@ -52,6 +54,8 @@ mod handlers {
pub(crate) mod unresolved_macro_call;
pub(crate) mod unresolved_module;
pub(crate) mod unresolved_proc_macro;
+ pub(crate) mod undeclared_label;
+ pub(crate) mod unreachable_label;
// The handlers below are unusual, the implement the diagnostics as well.
pub(crate) mod field_shorthand;
@@ -74,6 +78,7 @@ use ide_db::{
};
use syntax::{algo::find_node_at_range, ast::AstNode, SyntaxNodePtr, TextRange};
+// FIXME: Make this an enum
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DiagnosticCode(pub &'static str);
@@ -198,7 +203,7 @@ impl<'a> DiagnosticsContext<'a> {
let sema = &self.sema;
(|| {
let precise_location = precise_location?;
- let root = sema.parse_or_expand(node.file_id)?;
+ let root = sema.parse_or_expand(node.file_id);
match root.covering_element(precise_location) {
syntax::NodeOrToken::Node(it) => Some(sema.original_range(&it)),
syntax::NodeOrToken::Token(it) => {
@@ -246,42 +251,60 @@ pub fn diagnostics(
let mut diags = Vec::new();
if let Some(m) = module {
- m.diagnostics(db, &mut diags)
+ m.diagnostics(db, &mut diags);
}
for diag in diags {
#[rustfmt::skip]
let d = match diag {
- AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
- AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
+ AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
+ Some(it) => it,
+ None => continue,
+ }
AnyDiagnostic::IncoherentImpl(d) => handlers::incoherent_impl::incoherent_impl(&ctx, &d),
+ AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
+ AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
+ AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d),
AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
+ AnyDiagnostic::MacroExpansionParseError(d) => {
+ res.extend(d.errors.iter().take(32).map(|err| {
+ {
+ Diagnostic::new(
+ "syntax-error",
+ format!("Syntax Error in Expansion: {err}"),
+ ctx.resolve_precise_location(&d.node.clone(), d.precise_location),
+ )
+ }
+ .experimental()
+ }));
+ continue;
+ },
AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d),
AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d),
AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d),
+ AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d),
+ AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d),
AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
AnyDiagnostic::PrivateAssocItem(d) => handlers::private_assoc_item::private_assoc_item(&ctx, &d),
AnyDiagnostic::PrivateField(d) => handlers::private_field::private_field(&ctx, &d),
AnyDiagnostic::ReplaceFilterMapNextWithFindMap(d) => handlers::replace_filter_map_next_with_find_map::replace_filter_map_next_with_find_map(&ctx, &d),
+ AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d),
AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
+ AnyDiagnostic::UndeclaredLabel(d) => handlers::undeclared_label::undeclared_label(&ctx, &d),
AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
+ AnyDiagnostic::UnreachableLabel(d) => handlers::unreachable_label:: unreachable_label(&ctx, &d),
AnyDiagnostic::UnresolvedExternCrate(d) => handlers::unresolved_extern_crate::unresolved_extern_crate(&ctx, &d),
+ AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d),
AnyDiagnostic::UnresolvedImport(d) => handlers::unresolved_import::unresolved_import(&ctx, &d),
AnyDiagnostic::UnresolvedMacroCall(d) => handlers::unresolved_macro_call::unresolved_macro_call(&ctx, &d),
+ AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d),
AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled),
- AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
- AnyDiagnostic::UnresolvedField(d) => handlers::unresolved_field::unresolved_field(&ctx, &d),
- AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d),
- AnyDiagnostic::NeedMut(d) => handlers::mutability_errors::need_mut(&ctx, &d),
AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
- AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
- Some(it) => it,
- None => continue,
- }
+ AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
};
res.push(d)
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
index afa641c73..b5cd4e0d6 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
@@ -8,7 +8,7 @@ use ide_db::{
RootDatabase,
};
use stdx::trim_indent;
-use test_utils::{assert_eq_text, extract_annotations};
+use test_utils::{assert_eq_text, extract_annotations, MiniCore};
use crate::{DiagnosticsConfig, ExprFillDefaultMode, Severity};
@@ -121,6 +121,15 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur
})
.collect::<Vec<_>>();
actual.sort_by_key(|(range, _)| range.start());
+ if expected.is_empty() {
+ // makes minicore smoke test debugable
+ for (e, _) in &actual {
+ eprintln!(
+ "Code in range {e:?} = {}",
+ &db.file_text(file_id)[usize::from(e.start())..usize::from(e.end())]
+ )
+ }
+ }
assert_eq!(expected, actual);
}
}
@@ -143,3 +152,28 @@ fn test_disabled_diagnostics() {
);
assert!(!diagnostics.is_empty());
}
+
+#[test]
+fn minicore_smoke_test() {
+ fn check(minicore: MiniCore) {
+ let source = minicore.source_code();
+ let mut config = DiagnosticsConfig::test_sample();
+ // This should be ignored since we conditionaly remove code which creates single item use with braces
+ config.disabled.insert("unnecessary-braces".to_string());
+ check_diagnostics_with_config(config, &source);
+ }
+
+ // Checks that there is no diagnostic in minicore for each flag.
+ for flag in MiniCore::available_flags() {
+ if flag == "clone" {
+ // Clone without copy has `moved-out-of-ref`, so ignoring.
+ // FIXME: Maybe we should merge copy and clone in a single flag?
+ continue;
+ }
+ eprintln!("Checking minicore flag {flag}");
+ check(MiniCore::from_flags([flag]));
+ }
+ // And one time for all flags, to check codes which are behind multiple flags + prevent name collisions
+ eprintln!("Checking all minicore flags");
+ check(MiniCore::from_flags(MiniCore::available_flags()))
+}
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml
index 04efa7b91..70ed6dea5 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml
@@ -15,6 +15,8 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
itertools = "0.10.5"
+triomphe.workspace = true
+nohash-hasher.workspace = true
# local deps
hir.workspace = true
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
index d9834ee63..66832a0be 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs
@@ -87,8 +87,8 @@ pub use crate::{errors::SsrError, from_comment::ssr_from_comment, matching::Matc
use crate::{errors::bail, matching::MatchFailureReason};
use hir::Semantics;
use ide_db::base_db::{FileId, FilePosition, FileRange};
+use nohash_hasher::IntMap;
use resolving::ResolvedRule;
-use stdx::hash::NoHashHashMap;
use syntax::{ast, AstNode, SyntaxNode, TextRange};
use text_edit::TextEdit;
@@ -168,9 +168,9 @@ impl<'db> MatchFinder<'db> {
}
/// Finds matches for all added rules and returns edits for all found matches.
- pub fn edits(&self) -> NoHashHashMap<FileId, TextEdit> {
+ pub fn edits(&self) -> IntMap<FileId, TextEdit> {
use ide_db::base_db::SourceDatabaseExt;
- let mut matches_by_file = NoHashHashMap::default();
+ let mut matches_by_file = IntMap::default();
for m in self.matches().matches {
matches_by_file
.entry(m.range.file_id)
@@ -184,6 +184,7 @@ impl<'db> MatchFinder<'db> {
(
file_id,
replacing::matches_to_edit(
+ self.sema.db,
&matches,
&self.sema.db.file_text(file_id),
&self.rules,
@@ -224,7 +225,7 @@ impl<'db> MatchFinder<'db> {
let file = self.sema.parse(file_id);
let mut res = Vec::new();
let file_text = self.sema.db.file_text(file_id);
- let mut remaining_text = file_text.as_str();
+ let mut remaining_text = &*file_text;
let mut base = 0;
let len = snippet.len() as u32;
while let Some(offset) = remaining_text.find(snippet) {
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs
index e27ef6e35..b4b83f62d 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/replacing.rs
@@ -14,14 +14,16 @@ use crate::{fragments, resolving::ResolvedRule, Match, SsrMatches};
/// template. Placeholders in the template will have been substituted with whatever they matched to
/// in the original code.
pub(crate) fn matches_to_edit(
+ db: &dyn hir::db::ExpandDatabase,
matches: &SsrMatches,
file_src: &str,
rules: &[ResolvedRule],
) -> TextEdit {
- matches_to_edit_at_offset(matches, file_src, 0.into(), rules)
+ matches_to_edit_at_offset(db, matches, file_src, 0.into(), rules)
}
fn matches_to_edit_at_offset(
+ db: &dyn hir::db::ExpandDatabase,
matches: &SsrMatches,
file_src: &str,
relative_start: TextSize,
@@ -31,13 +33,14 @@ fn matches_to_edit_at_offset(
for m in &matches.matches {
edit_builder.replace(
m.range.range.checked_sub(relative_start).unwrap(),
- render_replace(m, file_src, rules),
+ render_replace(db, m, file_src, rules),
);
}
edit_builder.finish()
}
struct ReplacementRenderer<'a> {
+ db: &'a dyn hir::db::ExpandDatabase,
match_info: &'a Match,
file_src: &'a str,
rules: &'a [ResolvedRule],
@@ -53,13 +56,19 @@ struct ReplacementRenderer<'a> {
placeholder_tokens_requiring_parenthesis: FxHashSet<SyntaxToken>,
}
-fn render_replace(match_info: &Match, file_src: &str, rules: &[ResolvedRule]) -> String {
+fn render_replace(
+ db: &dyn hir::db::ExpandDatabase,
+ match_info: &Match,
+ file_src: &str,
+ rules: &[ResolvedRule],
+) -> String {
let rule = &rules[match_info.rule_index];
let template = rule
.template
.as_ref()
.expect("You called MatchFinder::edits after calling MatchFinder::add_search_pattern");
let mut renderer = ReplacementRenderer {
+ db,
match_info,
file_src,
rules,
@@ -96,7 +105,7 @@ impl ReplacementRenderer<'_> {
fn render_node(&mut self, node: &SyntaxNode) {
if let Some(mod_path) = self.match_info.rendered_template_paths.get(node) {
- self.out.push_str(&mod_path.to_string());
+ self.out.push_str(&mod_path.display(self.db).to_string());
// Emit everything except for the segment's name-ref, since we already effectively
// emitted that as part of `mod_path`.
if let Some(path) = ast::Path::cast(node.clone()) {
@@ -144,6 +153,7 @@ impl ReplacementRenderer<'_> {
);
}
let edit = matches_to_edit_at_offset(
+ self.db,
&placeholder_value.inner_matches,
self.file_src,
range.start(),
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs
index 61698fca8..424ba3d7f 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/tests.rs
@@ -3,8 +3,8 @@ use ide_db::{
base_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt},
FxHashSet,
};
-use std::sync::Arc;
use test_utils::RangeOrOffset;
+use triomphe::Arc;
use crate::{MatchFinder, SsrRule};
diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml
index 30e514e41..2aee203c4 100644
--- a/src/tools/rust-analyzer/crates/ide/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml
@@ -23,6 +23,8 @@ pulldown-cmark = { version = "0.9.1", default-features = false }
url = "2.3.1"
dot = "0.1.4"
smallvec.workspace = true
+triomphe.workspace = true
+nohash-hasher.workspace = true
# local deps
cfg.workspace = true
diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
index 48bcd37b6..dd1d0d75c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
@@ -263,7 +263,7 @@ mod tests {
expect![["callee Function FileId(0) 0..14 3..9"]],
expect![[r#"
caller1 Function FileId(0) 15..45 18..25 : [34..40]
- test_caller Function FileId(0) 95..149 110..121 : [134..140]"#]],
+ test_caller Function FileId(0) 95..149 110..121 tests : [134..140]"#]],
expect![[]],
);
}
@@ -283,7 +283,7 @@ fn caller() {
//- /foo/mod.rs
pub fn callee() {}
"#,
- expect![["callee Function FileId(1) 0..18 7..13"]],
+ expect!["callee Function FileId(1) 0..18 7..13 foo"],
expect![["caller Function FileId(0) 27..56 30..36 : [45..51]"]],
expect![[]],
);
@@ -323,7 +323,7 @@ pub fn callee() {}
"#,
expect![["caller Function FileId(0) 27..56 30..36"]],
expect![[]],
- expect![["callee Function FileId(1) 0..18 7..13 : [45..51]"]],
+ expect!["callee Function FileId(1) 0..18 7..13 foo : [45..51]"],
);
}
@@ -477,7 +477,7 @@ fn caller() {
S1::callee();
}
"#,
- expect![["callee Function FileId(0) 15..27 18..24"]],
+ expect!["callee Function FileId(0) 15..27 18..24 T1"],
expect![["caller Function FileId(0) 82..115 85..91 : [104..110]"]],
expect![[]],
);
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index fae25f310..8112c4f72 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -5,6 +5,8 @@ mod tests;
mod intra_doc_links;
+use std::ffi::OsStr;
+
use pulldown_cmark::{BrokenLink, CowStr, Event, InlineStr, LinkType, Options, Parser, Tag};
use pulldown_cmark_to_cmark::{cmark_resume_with_options, Options as CMarkOptions};
use stdx::format_to;
@@ -12,7 +14,7 @@ use url::Url;
use hir::{db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, HasAttrs};
use ide_db::{
- base_db::{CrateOrigin, LangCrateOrigin, SourceDatabase},
+ base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase},
defs::{Definition, NameClass, NameRefClass},
helpers::pick_best_token,
RootDatabase,
@@ -29,8 +31,16 @@ use crate::{
FilePosition, Semantics,
};
-/// Weblink to an item's documentation.
-pub(crate) type DocumentationLink = String;
+/// Web and local links to an item's documentation.
+#[derive(Default, Debug, Clone, PartialEq, Eq)]
+pub struct DocumentationLinks {
+ /// The URL to the documentation on docs.rs.
+ /// May not lead anywhere.
+ pub web_url: Option<String>,
+ /// The URL to the documentation in the local file system.
+ /// May not lead anywhere.
+ pub local_url: Option<String>,
+}
const MARKDOWN_OPTIONS: Options =
Options::ENABLE_FOOTNOTES.union(Options::ENABLE_TABLES).union(Options::ENABLE_TASKLISTS);
@@ -109,7 +119,7 @@ pub(crate) fn remove_links(markdown: &str) -> String {
// Feature: Open Docs
//
-// Retrieve a link to documentation for the given symbol.
+// Retrieve a links to documentation for the given symbol.
//
// The simplest way to use this feature is via the context menu. Right-click on
// the selected item. The context menu opens. Select **Open Docs**.
@@ -122,7 +132,9 @@ pub(crate) fn remove_links(markdown: &str) -> String {
pub(crate) fn external_docs(
db: &RootDatabase,
position: &FilePosition,
-) -> Option<DocumentationLink> {
+ target_dir: Option<&OsStr>,
+ sysroot: Option<&OsStr>,
+) -> Option<DocumentationLinks> {
let sema = &Semantics::new(db);
let file = sema.parse(position.file_id).syntax().clone();
let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
@@ -146,11 +158,11 @@ pub(crate) fn external_docs(
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
NameClass::PatFieldShorthand { local_def: _, field_ref } => Definition::Field(field_ref),
},
- _ => return None,
+ _ => return None
}
};
- get_doc_link(db, definition)
+ Some(get_doc_links(db, definition, target_dir, sysroot))
}
/// Extracts all links from a given markdown text returning the definition text range, link-text
@@ -308,19 +320,35 @@ fn broken_link_clone_cb(link: BrokenLink<'_>) -> Option<(CowStr<'_>, CowStr<'_>)
//
// This should cease to be a problem if RFC2988 (Stable Rustdoc URLs) is implemented
// https://github.com/rust-lang/rfcs/pull/2988
-fn get_doc_link(db: &RootDatabase, def: Definition) -> Option<String> {
- let (target, file, frag) = filename_and_frag_for_def(db, def)?;
+fn get_doc_links(
+ db: &RootDatabase,
+ def: Definition,
+ target_dir: Option<&OsStr>,
+ sysroot: Option<&OsStr>,
+) -> DocumentationLinks {
+ let join_url = |base_url: Option<Url>, path: &str| -> Option<Url> {
+ base_url.and_then(|url| url.join(path).ok())
+ };
+
+ let Some((target, file, frag)) = filename_and_frag_for_def(db, def) else { return Default::default(); };
- let mut url = get_doc_base_url(db, target)?;
+ let (mut web_url, mut local_url) = get_doc_base_urls(db, target, target_dir, sysroot);
if let Some(path) = mod_path_of_def(db, target) {
- url = url.join(&path).ok()?;
+ web_url = join_url(web_url, &path);
+ local_url = join_url(local_url, &path);
}
- url = url.join(&file).ok()?;
- url.set_fragment(frag.as_deref());
+ web_url = join_url(web_url, &file);
+ local_url = join_url(local_url, &file);
- Some(url.into())
+ web_url.as_mut().map(|url| url.set_fragment(frag.as_deref()));
+ local_url.as_mut().map(|url| url.set_fragment(frag.as_deref()));
+
+ DocumentationLinks {
+ web_url: web_url.map(|it| it.into()),
+ local_url: local_url.map(|it| it.into()),
+ }
}
fn rewrite_intra_doc_link(
@@ -332,7 +360,7 @@ fn rewrite_intra_doc_link(
let (link, ns) = parse_intra_doc_link(target);
let resolved = resolve_doc_path_for_def(db, def, link, ns)?;
- let mut url = get_doc_base_url(db, resolved)?;
+ let mut url = get_doc_base_urls(db, resolved, None, None).0?;
let (_, file, frag) = filename_and_frag_for_def(db, resolved)?;
if let Some(path) = mod_path_of_def(db, resolved) {
@@ -351,7 +379,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option<
return None;
}
- let mut url = get_doc_base_url(db, def)?;
+ let mut url = get_doc_base_urls(db, def, None, None).0?;
let (def, file, frag) = filename_and_frag_for_def(db, def)?;
if let Some(path) = mod_path_of_def(db, def) {
@@ -366,7 +394,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option<
fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option<String> {
def.canonical_module_path(db).map(|it| {
let mut path = String::new();
- it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name));
+ it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name.display(db)));
path
})
}
@@ -426,18 +454,38 @@ fn map_links<'e>(
/// ```ignore
/// https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
/// ^^^^^^^^^^^^^^^^^^^^^^^^^^
+/// file:///project/root/target/doc/std/iter/trait.Iterator.html#tymethod.next
+/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/// ```
-fn get_doc_base_url(db: &RootDatabase, def: Definition) -> Option<Url> {
+fn get_doc_base_urls(
+ db: &RootDatabase,
+ def: Definition,
+ target_dir: Option<&OsStr>,
+ sysroot: Option<&OsStr>,
+) -> (Option<Url>, Option<Url>) {
+ let local_doc = target_dir
+ .and_then(|path| path.to_str())
+ .and_then(|path| Url::parse(&format!("file:///{path}/")).ok())
+ .and_then(|it| it.join("doc/").ok());
+ let system_doc = sysroot
+ .and_then(|it| it.to_str())
+ .map(|sysroot| format!("file:///{sysroot}/share/doc/rust/html/"))
+ .and_then(|it| Url::parse(&it).ok());
+
// special case base url of `BuiltinType` to core
// https://github.com/rust-lang/rust-analyzer/issues/12250
if let Definition::BuiltinType(..) = def {
- return Url::parse("https://doc.rust-lang.org/nightly/core/").ok();
+ let web_link = Url::parse("https://doc.rust-lang.org/nightly/core/").ok();
+ let system_link = system_doc.and_then(|it| it.join("core/").ok());
+ return (web_link, system_link);
};
- let krate = def.krate(db)?;
- let display_name = krate.display_name(db)?;
+ let Some(krate) = def.krate(db) else { return Default::default() };
+ let Some(display_name) = krate.display_name(db) else { return Default::default() };
+ let crate_data = &db.crate_graph()[krate.into()];
+ let channel = crate_data.channel.map_or("nightly", ReleaseChannel::as_str);
- let base = match db.crate_graph()[krate.into()].origin {
+ let (web_base, local_base) = match &crate_data.origin {
// std and co do not specify `html_root_url` any longer so we gotta handwrite this ourself.
// FIXME: Use the toolchains channel instead of nightly
CrateOrigin::Lang(
@@ -447,10 +495,17 @@ fn get_doc_base_url(db: &RootDatabase, def: Definition) -> Option<Url> {
| LangCrateOrigin::Std
| LangCrateOrigin::Test),
) => {
- format!("https://doc.rust-lang.org/nightly/{origin}")
+ let system_url = system_doc.and_then(|it| it.join(&format!("{origin}")).ok());
+ let web_url = format!("https://doc.rust-lang.org/{channel}/{origin}");
+ (Some(web_url), system_url)
}
- _ => {
- krate.get_html_root_url(db).or_else(|| {
+ CrateOrigin::Lang(_) => return (None, None),
+ CrateOrigin::Rustc { name: _ } => {
+ (Some(format!("https://doc.rust-lang.org/{channel}/nightly-rustc/")), None)
+ }
+ CrateOrigin::Local { repo: _, name: _ } => {
+ // FIXME: These should not attempt to link to docs.rs!
+ let weblink = krate.get_html_root_url(db).or_else(|| {
let version = krate.version(db);
// Fallback to docs.rs. This uses `display_name` and can never be
// correct, but that's what fallbacks are about.
@@ -462,10 +517,32 @@ fn get_doc_base_url(db: &RootDatabase, def: Definition) -> Option<Url> {
krate = display_name,
version = version.as_deref().unwrap_or("*")
))
- })?
+ });
+ (weblink, local_doc)
+ }
+ CrateOrigin::Library { repo: _, name } => {
+ let weblink = krate.get_html_root_url(db).or_else(|| {
+ let version = krate.version(db);
+ // Fallback to docs.rs. This uses `display_name` and can never be
+ // correct, but that's what fallbacks are about.
+ //
+ // FIXME: clicking on the link should just open the file in the editor,
+ // instead of falling back to external urls.
+ Some(format!(
+ "https://docs.rs/{krate}/{version}/",
+ krate = name,
+ version = version.as_deref().unwrap_or("*")
+ ))
+ });
+ (weblink, local_doc)
}
};
- Url::parse(&base).ok()?.join(&format!("{display_name}/")).ok()
+ let web_base = web_base
+ .and_then(|it| Url::parse(&it).ok())
+ .and_then(|it| it.join(&format!("{display_name}/")).ok());
+ let local_base = local_base.and_then(|it| it.join(&format!("{display_name}/")).ok());
+
+ (web_base, local_base)
}
/// Get the filename and extension generated for a symbol by rustdoc.
@@ -490,9 +567,9 @@ fn filename_and_frag_for_def(
let res = match def {
Definition::Adt(adt) => match adt {
- Adt::Struct(s) => format!("struct.{}.html", s.name(db)),
- Adt::Enum(e) => format!("enum.{}.html", e.name(db)),
- Adt::Union(u) => format!("union.{}.html", u.name(db)),
+ Adt::Struct(s) => format!("struct.{}.html", s.name(db).display(db.upcast())),
+ Adt::Enum(e) => format!("enum.{}.html", e.name(db).display(db.upcast())),
+ Adt::Union(u) => format!("union.{}.html", u.name(db).display(db.upcast())),
},
Definition::Module(m) => match m.name(db) {
// `#[doc(keyword = "...")]` is internal used only by rust compiler
@@ -500,21 +577,25 @@ fn filename_and_frag_for_def(
Some(kw) => {
format!("keyword.{}.html", kw.trim_matches('"'))
}
- None => format!("{name}/index.html"),
+ None => format!("{}/index.html", name.display(db.upcast())),
},
None => String::from("index.html"),
},
- Definition::Trait(t) => format!("trait.{}.html", t.name(db)),
- Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db)),
- Definition::TypeAlias(t) => format!("type.{}.html", t.name(db)),
- Definition::BuiltinType(t) => format!("primitive.{}.html", t.name()),
- Definition::Function(f) => format!("fn.{}.html", f.name(db)),
+ Definition::Trait(t) => format!("trait.{}.html", t.name(db).display(db.upcast())),
+ Definition::TraitAlias(t) => format!("traitalias.{}.html", t.name(db).display(db.upcast())),
+ Definition::TypeAlias(t) => format!("type.{}.html", t.name(db).display(db.upcast())),
+ Definition::BuiltinType(t) => format!("primitive.{}.html", t.name().display(db.upcast())),
+ Definition::Function(f) => format!("fn.{}.html", f.name(db).display(db.upcast())),
Definition::Variant(ev) => {
- format!("enum.{}.html#variant.{}", ev.parent_enum(db).name(db), ev.name(db))
+ format!(
+ "enum.{}.html#variant.{}",
+ ev.parent_enum(db).name(db).display(db.upcast()),
+ ev.name(db).display(db.upcast())
+ )
}
- Definition::Const(c) => format!("const.{}.html", c.name(db)?),
- Definition::Static(s) => format!("static.{}.html", s.name(db)),
- Definition::Macro(mac) => format!("macro.{}.html", mac.name(db)),
+ Definition::Const(c) => format!("const.{}.html", c.name(db)?.display(db.upcast())),
+ Definition::Static(s) => format!("static.{}.html", s.name(db).display(db.upcast())),
+ Definition::Macro(mac) => format!("macro.{}.html", mac.name(db).display(db.upcast())),
Definition::Field(field) => {
let def = match field.parent_def(db) {
hir::VariantDef::Struct(it) => Definition::Adt(it.into()),
@@ -522,7 +603,11 @@ fn filename_and_frag_for_def(
hir::VariantDef::Variant(it) => Definition::Variant(it),
};
let (_, file, _) = filename_and_frag_for_def(db, def)?;
- return Some((def, file, Some(format!("structfield.{}", field.name(db)))));
+ return Some((
+ def,
+ file,
+ Some(format!("structfield.{}", field.name(db).display(db.upcast()))),
+ ));
}
Definition::SelfType(impl_) => {
let adt = impl_.self_ty(db).as_adt()?.into();
@@ -556,12 +641,14 @@ fn get_assoc_item_fragment(db: &dyn HirDatabase, assoc_item: hir::AssocItem) ->
// Rustdoc makes this decision based on whether a method 'has defaultness'.
// Currently this is only the case for provided trait methods.
if is_trait_method && !function.has_body(db) {
- format!("tymethod.{}", function.name(db))
+ format!("tymethod.{}", function.name(db).display(db.upcast()))
} else {
- format!("method.{}", function.name(db))
+ format!("method.{}", function.name(db).display(db.upcast()))
}
}
- AssocItem::Const(constant) => format!("associatedconstant.{}", constant.name(db)?),
- AssocItem::TypeAlias(ty) => format!("associatedtype.{}", ty.name(db)),
+ AssocItem::Const(constant) => {
+ format!("associatedconstant.{}", constant.name(db)?.display(db.upcast()))
+ }
+ AssocItem::TypeAlias(ty) => format!("associatedtype.{}", ty.name(db).display(db.upcast())),
})
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
index 104181a33..05a64b33b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
@@ -1,3 +1,5 @@
+use std::ffi::OsStr;
+
use expect_test::{expect, Expect};
use hir::{HasAttrs, Semantics};
use ide_db::{
@@ -13,11 +15,33 @@ use crate::{
fixture, TryToNav,
};
-fn check_external_docs(ra_fixture: &str, expect: Expect) {
+fn check_external_docs(
+ ra_fixture: &str,
+ target_dir: Option<&OsStr>,
+ expect_web_url: Option<Expect>,
+ expect_local_url: Option<Expect>,
+ sysroot: Option<&OsStr>,
+) {
let (analysis, position) = fixture::position(ra_fixture);
- let url = analysis.external_docs(position).unwrap().expect("could not find url for symbol");
+ let links = analysis.external_docs(position, target_dir, sysroot).unwrap();
+
+ let web_url = links.web_url;
+ let local_url = links.local_url;
+
+ println!("web_url: {:?}", web_url);
+ println!("local_url: {:?}", local_url);
+
+ match (expect_web_url, web_url) {
+ (Some(expect), Some(url)) => expect.assert_eq(&url),
+ (None, None) => (),
+ _ => panic!("Unexpected web url"),
+ }
- expect.assert_eq(&url)
+ match (expect_local_url, local_url) {
+ (Some(expect), Some(url)) => expect.assert_eq(&url),
+ (None, None) => (),
+ _ => panic!("Unexpected local url"),
+ }
}
fn check_rewrite(ra_fixture: &str, expect: Expect) {
@@ -97,6 +121,20 @@ fn node_to_def(
}
#[test]
+fn external_docs_doc_builtin_type() {
+ check_external_docs(
+ r#"
+//- /main.rs crate:foo
+let x: u3$02 = 0;
+"#,
+ Some(&OsStr::new("/home/user/project")),
+ Some(expect![[r#"https://doc.rust-lang.org/nightly/core/primitive.u32.html"#]]),
+ Some(expect![[r#"file:///sysroot/share/doc/rust/html/core/primitive.u32.html"#]]),
+ Some(&OsStr::new("/sysroot")),
+ );
+}
+
+#[test]
fn external_docs_doc_url_crate() {
check_external_docs(
r#"
@@ -105,7 +143,10 @@ use foo$0::Foo;
//- /lib.rs crate:foo
pub struct Foo;
"#,
- expect![[r#"https://docs.rs/foo/*/foo/index.html"#]],
+ Some(&OsStr::new("/home/user/project")),
+ Some(expect![[r#"https://docs.rs/foo/*/foo/index.html"#]]),
+ Some(expect![[r#"file:///home/user/project/doc/foo/index.html"#]]),
+ Some(&OsStr::new("/sysroot")),
);
}
@@ -116,7 +157,10 @@ fn external_docs_doc_url_std_crate() {
//- /main.rs crate:std
use self$0;
"#,
- expect![[r#"https://doc.rust-lang.org/nightly/std/index.html"#]],
+ Some(&OsStr::new("/home/user/project")),
+ Some(expect!["https://doc.rust-lang.org/stable/std/index.html"]),
+ Some(expect!["file:///sysroot/share/doc/rust/html/std/index.html"]),
+ Some(&OsStr::new("/sysroot")),
);
}
@@ -127,7 +171,38 @@ fn external_docs_doc_url_struct() {
//- /main.rs crate:foo
pub struct Fo$0o;
"#,
- expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]],
+ Some(&OsStr::new("/home/user/project")),
+ Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
+ Some(expect![[r#"file:///home/user/project/doc/foo/struct.Foo.html"#]]),
+ Some(&OsStr::new("/sysroot")),
+ );
+}
+
+#[test]
+fn external_docs_doc_url_windows_backslash_path() {
+ check_external_docs(
+ r#"
+//- /main.rs crate:foo
+pub struct Fo$0o;
+"#,
+ Some(&OsStr::new(r"C:\Users\user\project")),
+ Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
+ Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
+ Some(&OsStr::new("/sysroot")),
+ );
+}
+
+#[test]
+fn external_docs_doc_url_windows_slash_path() {
+ check_external_docs(
+ r#"
+//- /main.rs crate:foo
+pub struct Fo$0o;
+"#,
+ Some(&OsStr::new(r"C:/Users/user/project")),
+ Some(expect![[r#"https://docs.rs/foo/*/foo/struct.Foo.html"#]]),
+ Some(expect![[r#"file:///C:/Users/user/project/doc/foo/struct.Foo.html"#]]),
+ Some(&OsStr::new("/sysroot")),
);
}
@@ -140,7 +215,10 @@ pub struct Foo {
field$0: ()
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#structfield.field"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#structfield.field"##]]),
+ None,
+ None,
);
}
@@ -151,7 +229,10 @@ fn external_docs_doc_url_fn() {
//- /main.rs crate:foo
pub fn fo$0o() {}
"#,
- expect![[r#"https://docs.rs/foo/*/foo/fn.foo.html"#]],
+ None,
+ Some(expect![[r#"https://docs.rs/foo/*/foo/fn.foo.html"#]]),
+ None,
+ None,
);
}
@@ -165,7 +246,10 @@ impl Foo {
pub fn method$0() {}
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]]),
+ None,
+ None,
);
check_external_docs(
r#"
@@ -175,7 +259,10 @@ impl Foo {
const CONST$0: () = ();
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]]),
+ None,
+ None,
);
}
@@ -192,7 +279,10 @@ impl Trait for Foo {
pub fn method$0() {}
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#method.method"##]]),
+ None,
+ None,
);
check_external_docs(
r#"
@@ -205,7 +295,10 @@ impl Trait for Foo {
const CONST$0: () = ();
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedconstant.CONST"##]]),
+ None,
+ None,
);
check_external_docs(
r#"
@@ -218,7 +311,10 @@ impl Trait for Foo {
type Type$0 = ();
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedtype.Type"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/struct.Foo.html#associatedtype.Type"##]]),
+ None,
+ None,
);
}
@@ -231,7 +327,10 @@ pub trait Foo {
fn method$0();
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#tymethod.method"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#tymethod.method"##]]),
+ None,
+ None,
);
check_external_docs(
r#"
@@ -240,7 +339,10 @@ pub trait Foo {
const CONST$0: ();
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedconstant.CONST"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedconstant.CONST"##]]),
+ None,
+ None,
);
check_external_docs(
r#"
@@ -249,7 +351,10 @@ pub trait Foo {
type Type$0;
}
"#,
- expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedtype.Type"##]],
+ None,
+ Some(expect![[r##"https://docs.rs/foo/*/foo/trait.Foo.html#associatedtype.Type"##]]),
+ None,
+ None,
);
}
@@ -260,7 +365,10 @@ fn external_docs_trait() {
//- /main.rs crate:foo
trait Trait$0 {}
"#,
- expect![[r#"https://docs.rs/foo/*/foo/trait.Trait.html"#]],
+ None,
+ Some(expect![[r#"https://docs.rs/foo/*/foo/trait.Trait.html"#]]),
+ None,
+ None,
)
}
@@ -273,7 +381,10 @@ pub mod foo {
pub mod ba$0r {}
}
"#,
- expect![[r#"https://docs.rs/foo/*/foo/foo/bar/index.html"#]],
+ None,
+ Some(expect![[r#"https://docs.rs/foo/*/foo/foo/bar/index.html"#]]),
+ None,
+ None,
)
}
@@ -294,7 +405,10 @@ fn foo() {
let bar: wrapper::It$0em;
}
"#,
- expect![[r#"https://docs.rs/foo/*/foo/wrapper/module/struct.Item.html"#]],
+ None,
+ Some(expect![[r#"https://docs.rs/foo/*/foo/wrapper/module/struct.Item.html"#]]),
+ None,
+ None,
)
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
index 418043d67..d6bbf2bf7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
@@ -76,15 +76,17 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
if let Some(item) = ast::Item::cast(node.clone()) {
if let Some(def) = sema.resolve_attr_macro_call(&item) {
break (
- def.name(db).to_string(),
+ def.name(db).display(db).to_string(),
expand_attr_macro_recur(&sema, &item)?,
SyntaxKind::MACRO_ITEMS,
);
}
}
if let Some(mac) = ast::MacroCall::cast(node) {
+ let mut name = mac.path()?.segment()?.name_ref()?.to_string();
+ name.push('!');
break (
- mac.path()?.segment()?.name_ref()?.to_string(),
+ name,
expand_macro_recur(&sema, &mac)?,
mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS),
);
@@ -149,9 +151,11 @@ fn _format(
_db: &RootDatabase,
_kind: SyntaxKind,
_file_id: FileId,
- _expansion: &str,
+ expansion: &str,
) -> Option<String> {
- None
+ // remove trailing spaces for test
+ use itertools::Itertools;
+ Some(expansion.lines().map(|x| x.trim_end()).join("\n"))
}
#[cfg(not(any(test, target_arch = "wasm32", target_os = "emscripten")))]
@@ -235,7 +239,7 @@ fn main() {
}
"#,
expect![[r#"
- bar
+ bar!
5i64 as _"#]],
);
}
@@ -252,7 +256,7 @@ fn main() {
}
"#,
expect![[r#"
- bar
+ bar!
for _ in 0..42{}"#]],
);
}
@@ -273,9 +277,8 @@ macro_rules! baz {
f$0oo!();
"#,
expect![[r#"
- foo
- fn b(){}
- "#]],
+ foo!
+ fn b(){}"#]],
);
}
@@ -294,7 +297,7 @@ macro_rules! foo {
f$0oo!();
"#,
expect![[r#"
- foo
+ foo!
fn some_thing() -> u32 {
let a = 0;
a+10
@@ -328,16 +331,16 @@ fn main() {
}
"#,
expect![[r#"
- match_ast
- {
- if let Some(it) = ast::TraitDef::cast(container.clone()){}
- else if let Some(it) = ast::ImplDef::cast(container.clone()){}
- else {
- {
- continue
- }
- }
- }"#]],
+ match_ast!
+ {
+ if let Some(it) = ast::TraitDef::cast(container.clone()){}
+ else if let Some(it) = ast::ImplDef::cast(container.clone()){}
+ else {
+ {
+ continue
+ }
+ }
+ }"#]],
);
}
@@ -358,7 +361,7 @@ fn main() {
}
"#,
expect![[r#"
- match_ast
+ match_ast!
{}"#]],
);
}
@@ -383,7 +386,7 @@ fn main() {
}
"#,
expect![[r#"
- foo
+ foo!
{
macro_rules! bar {
() => {
@@ -411,7 +414,7 @@ fn main() {
}
"#,
expect![[r#"
- foo
+ foo!
"#]],
);
}
@@ -433,7 +436,7 @@ fn main() {
}
"#,
expect![[r#"
- foo
+ foo!
0"#]],
);
}
@@ -451,7 +454,7 @@ fn main() {
}
"#,
expect![[r#"
- foo
+ foo!
fn f<T>(_: &dyn ::std::marker::Copy){}"#]],
);
}
@@ -469,8 +472,17 @@ struct Foo {}
"#,
expect![[r#"
Clone
- impl < >core::clone::Clone for Foo< >{}
- "#]],
+ impl < >core::clone::Clone for Foo< >where {
+ fn clone(&self) -> Self {
+ match self {
+ Foo{}
+ => Foo{}
+ ,
+
+ }
+ }
+
+ }"#]],
);
}
@@ -486,8 +498,7 @@ struct Foo {}
"#,
expect![[r#"
Copy
- impl < >core::marker::Copy for Foo< >{}
- "#]],
+ impl < >core::marker::Copy for Foo< >where{}"#]],
);
}
@@ -502,8 +513,7 @@ struct Foo {}
"#,
expect![[r#"
Copy
- impl < >core::marker::Copy for Foo< >{}
- "#]],
+ impl < >core::marker::Copy for Foo< >where{}"#]],
);
check(
r#"
@@ -514,8 +524,17 @@ struct Foo {}
"#,
expect![[r#"
Clone
- impl < >core::clone::Clone for Foo< >{}
- "#]],
+ impl < >core::clone::Clone for Foo< >where {
+ fn clone(&self) -> Self {
+ match self {
+ Foo{}
+ => Foo{}
+ ,
+
+ }
+ }
+
+ }"#]],
);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
index 9f78c75e9..f90618222 100644
--- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
@@ -39,7 +39,7 @@ fn try_extend_selection(
) -> Option<TextRange> {
let range = frange.range;
- let string_kinds = [COMMENT, STRING, BYTE_STRING];
+ let string_kinds = [COMMENT, STRING, BYTE_STRING, C_STRING];
let list_kinds = [
RECORD_PAT_FIELD_LIST,
MATCH_ARM_LIST,
diff --git a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs
new file mode 100644
index 000000000..46ee671de
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs
@@ -0,0 +1,42 @@
+use ide_db::{
+ base_db::{CrateOrigin, FileId, SourceDatabase},
+ FxIndexSet, RootDatabase,
+};
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct CrateInfo {
+ pub name: Option<String>,
+ pub version: Option<String>,
+ pub root_file_id: FileId,
+}
+
+// Feature: Show Dependency Tree
+//
+// Shows a view tree with all the dependencies of this project
+//
+// |===
+// | Editor | Panel Name
+//
+// | VS Code | **Rust Dependencies**
+// |===
+//
+// image::https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png[]
+pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> {
+ let crate_graph = db.crate_graph();
+ crate_graph
+ .iter()
+ .map(|crate_id| &crate_graph[crate_id])
+ .filter(|&data| !matches!(data.origin, CrateOrigin::Local { .. }))
+ .map(|data| crate_info(data))
+ .collect()
+}
+
+fn crate_info(data: &ide_db::base_db::CrateData) -> CrateInfo {
+ let crate_name = crate_name(data);
+ let version = data.version.clone();
+ CrateInfo { name: crate_name, version, root_file_id: data.root_file_id }
+}
+
+fn crate_name(data: &ide_db::base_db::CrateData) -> Option<String> {
+ data.display_name.as_ref().map(|it| it.canonical_name().to_owned())
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
index a32ac3549..b27892472 100644
--- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
@@ -219,7 +219,7 @@ mod tests {
}
#[test]
- fn test_nagative_trait_bound() {
+ fn test_negative_trait_bound() {
let txt = r#"impl !Unpin for Test {}"#;
check(
txt,
diff --git a/src/tools/rust-analyzer/crates/ide/src/fixture.rs b/src/tools/rust-analyzer/crates/ide/src/fixture.rs
index 2ea6f6a9a..2e5903c06 100644
--- a/src/tools/rust-analyzer/crates/ide/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/fixture.rs
@@ -1,5 +1,4 @@
//! Utilities for creating `Analysis` instances for tests.
-use hir::db::DefDatabase;
use ide_db::base_db::fixture::ChangeFixture;
use test_utils::{extract_annotations, RangeOrOffset};
@@ -9,7 +8,7 @@ use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange};
pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
- host.db.set_enable_proc_attr_macros(true);
+ host.db.enable_proc_attr_macros();
host.db.apply_change(change_fixture.change);
(host.analysis(), change_fixture.files[0])
}
@@ -18,7 +17,7 @@ pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) {
pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
- host.db.set_enable_proc_attr_macros(true);
+ host.db.enable_proc_attr_macros();
host.db.apply_change(change_fixture.change);
let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
let offset = range_or_offset.expect_offset();
@@ -29,7 +28,7 @@ pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) {
pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
- host.db.set_enable_proc_attr_macros(true);
+ host.db.enable_proc_attr_macros();
host.db.apply_change(change_fixture.change);
let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
let range = range_or_offset.expect_range();
@@ -40,7 +39,7 @@ pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) {
pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrOffset) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
- host.db.set_enable_proc_attr_macros(true);
+ host.db.enable_proc_attr_macros();
host.db.apply_change(change_fixture.change);
let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
(host.analysis(), file_id, range_or_offset)
@@ -50,7 +49,7 @@ pub(crate) fn range_or_position(ra_fixture: &str) -> (Analysis, FileId, RangeOrO
pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
- host.db.set_enable_proc_attr_macros(true);
+ host.db.enable_proc_attr_macros();
host.db.apply_change(change_fixture.change);
let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker ($0)");
let offset = range_or_offset.expect_offset();
@@ -67,11 +66,11 @@ pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(Fil
(host.analysis(), FilePosition { file_id, offset }, annotations)
}
-/// Creates analysis from a multi-file fixture with annonations without $0
+/// Creates analysis from a multi-file fixture with annotations without $0
pub(crate) fn annotations_without_marker(ra_fixture: &str) -> (Analysis, Vec<(FileRange, String)>) {
let mut host = AnalysisHost::default();
let change_fixture = ChangeFixture::parse(ra_fixture);
- host.db.set_enable_proc_attr_macros(true);
+ host.db.enable_proc_attr_macros();
host.db.apply_change(change_fixture.change);
let annotations = change_fixture
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index cf0819a25..4e641357e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -113,6 +113,7 @@ fn try_lookup_include_path(
file_id,
full_range: TextRange::new(0.into(), size),
name: path.into(),
+ alias: None,
focus_range: None,
kind: None,
container_name: None,
@@ -833,8 +834,7 @@ fn test() {
#[rustc_builtin_macro]
macro_rules! include {}
- include!("foo.rs");
-//^^^^^^^^^^^^^^^^^^^
+include!("foo.rs");
fn f() {
foo$0();
@@ -846,6 +846,33 @@ mod confuse_index {
//- /foo.rs
fn foo() {}
+ //^^^
+ "#,
+ );
+ }
+
+ #[test]
+ fn goto_through_included_file_struct_with_doc_comment() {
+ check(
+ r#"
+//- /main.rs
+#[rustc_builtin_macro]
+macro_rules! include {}
+
+include!("foo.rs");
+
+fn f() {
+ let x = Foo$0;
+}
+
+mod confuse_index {
+ pub struct Foo;
+}
+
+//- /foo.rs
+/// This is a doc comment
+pub struct Foo;
+ //^^^
"#,
);
}
@@ -1471,6 +1498,29 @@ fn f() {
);
}
#[test]
+ fn method_call_inside_block() {
+ check(
+ r#"
+trait Twait {
+ fn a(&self);
+}
+
+fn outer() {
+ struct Stwuct;
+
+ impl Twait for Stwuct {
+ fn a(&self){}
+ //^
+ }
+ fn f() {
+ let s = Stwuct;
+ s.a$0();
+ }
+}
+ "#,
+ );
+ }
+ #[test]
fn path_call() {
check(
r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
index 6d2d0bd63..6048990f7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
@@ -10,7 +10,7 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
// |===
// | Editor | Action Name
//
-// | VS Code | **Go to Type Definition*
+// | VS Code | **Go to Type Definition**
// |===
//
// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
@@ -38,32 +38,41 @@ pub(crate) fn goto_type_definition(
};
let range = token.text_range();
sema.descend_into_macros(token)
- .iter()
+ .into_iter()
.filter_map(|token| {
- let ty = sema.token_ancestors_with_macros(token.clone()).find_map(|node| {
- let ty = match_ast! {
- match node {
- ast::Expr(it) => sema.type_of_expr(&it)?.original,
- ast::Pat(it) => sema.type_of_pat(&it)?.original,
- ast::SelfParam(it) => sema.type_of_self(&it)?,
- ast::Type(it) => sema.resolve_type(&it)?,
- ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?,
- // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise
- ast::NameRef(it) => {
- if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) {
- let (_, _, ty) = sema.resolve_record_field(&record_field)?;
- ty
- } else {
- let record_field = ast::RecordPatField::for_field_name_ref(&it)?;
- sema.resolve_record_pat_field(&record_field)?.1
- }
- },
- _ => return None,
- }
- };
+ let ty = sema
+ .token_ancestors_with_macros(token)
+ // When `token` is within a macro call, we can't determine its type. Don't continue
+ // this traversal because otherwise we'll end up returning the type of *that* macro
+ // call, which is not what we want in general.
+ //
+ // Macro calls always wrap `TokenTree`s, so it's sufficient and efficient to test
+ // if the current node is a `TokenTree`.
+ .take_while(|node| !ast::TokenTree::can_cast(node.kind()))
+ .find_map(|node| {
+ let ty = match_ast! {
+ match node {
+ ast::Expr(it) => sema.type_of_expr(&it)?.original,
+ ast::Pat(it) => sema.type_of_pat(&it)?.original,
+ ast::SelfParam(it) => sema.type_of_self(&it)?,
+ ast::Type(it) => sema.resolve_type(&it)?,
+ ast::RecordField(it) => sema.to_def(&it)?.ty(db.upcast()),
+ // can't match on RecordExprField directly as `ast::Expr` will match an iteration too early otherwise
+ ast::NameRef(it) => {
+ if let Some(record_field) = ast::RecordExprField::for_name_ref(&it) {
+ let (_, _, ty) = sema.resolve_record_field(&record_field)?;
+ ty
+ } else {
+ let record_field = ast::RecordPatField::for_field_name_ref(&it)?;
+ sema.resolve_record_pat_field(&record_field)?.1
+ }
+ },
+ _ => return None,
+ }
+ };
- Some(ty)
- });
+ Some(ty)
+ });
ty
})
.for_each(|ty| {
@@ -94,7 +103,7 @@ mod tests {
fn check(ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
let navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
- assert_ne!(navs.len(), 0);
+ assert!(!navs.is_empty(), "navigation is empty");
let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
let navs = navs
@@ -104,7 +113,7 @@ mod tests {
.collect::<Vec<_>>();
let expected = expected
.into_iter()
- .map(|(FileRange { file_id, range }, _)| FileRange { file_id, range })
+ .map(|(file_range, _)| file_range)
.sorted_by_key(cmp)
.collect::<Vec<_>>();
assert_eq!(expected, navs);
@@ -199,6 +208,32 @@ id! {
}
#[test]
+ fn dont_collect_type_from_token_in_macro_call() {
+ check(
+ r#"
+struct DontCollectMe;
+struct S;
+ //^
+
+macro_rules! inner {
+ ($t:tt) => { DontCollectMe }
+}
+macro_rules! m {
+ ($t:ident) => {
+ match $t {
+ _ => inner!($t);
+ }
+ }
+}
+
+fn test() {
+ m!($0S);
+}
+"#,
+ );
+ }
+
+ #[test]
fn goto_type_definition_for_param() {
check(
r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
index d88ffd25c..7e545491f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -1,10 +1,12 @@
use hir::Semantics;
use ide_db::{
- base_db::{FileId, FilePosition},
+ base_db::{FileId, FilePosition, FileRange},
defs::{Definition, IdentClass},
helpers::pick_best_token,
search::{FileReference, ReferenceCategory, SearchScope},
- syntax_helpers::node_ext::{for_each_break_and_continue_expr, for_each_tail_expr, walk_expr},
+ syntax_helpers::node_ext::{
+ for_each_break_and_continue_expr, for_each_tail_expr, full_path_of_name_ref, walk_expr,
+ },
FxHashSet, RootDatabase,
};
use syntax::{
@@ -30,6 +32,7 @@ pub struct HighlightRelatedConfig {
pub references: bool,
pub exit_points: bool,
pub break_points: bool,
+ pub closure_captures: bool,
pub yield_points: bool,
}
@@ -38,11 +41,13 @@ pub struct HighlightRelatedConfig {
// Highlights constructs related to the thing under the cursor:
//
// . if on an identifier, highlights all references to that identifier in the current file
+// .. additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope
// . if on an `async` or `await token, highlights all yield points for that async context
// . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context
// . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context
+// . if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure.
//
-// Note: `?` and `->` do not currently trigger this behavior in the VSCode editor.
+// Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor.
pub(crate) fn highlight_related(
sema: &Semantics<'_, RootDatabase>,
config: HighlightRelatedConfig,
@@ -53,11 +58,13 @@ pub(crate) fn highlight_related(
let token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind {
T![?] => 4, // prefer `?` when the cursor is sandwiched like in `await$0?`
- T![->] => 3,
- kind if kind.is_keyword() => 2,
- IDENT | INT_NUMBER => 1,
+ T![->] => 4,
+ kind if kind.is_keyword() => 3,
+ IDENT | INT_NUMBER => 2,
+ T![|] => 1,
_ => 0,
})?;
+ // most if not all of these should be re-implemented with information seeded from hir
match token.kind() {
T![?] if config.exit_points && token.parent().and_then(ast::TryExpr::cast).is_some() => {
highlight_exit_points(sema, token)
@@ -70,18 +77,64 @@ pub(crate) fn highlight_related(
T![break] | T![loop] | T![while] | T![continue] if config.break_points => {
highlight_break_points(token)
}
+ T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id),
+ T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id),
_ if config.references => highlight_references(sema, &syntax, token, file_id),
_ => None,
}
}
+fn highlight_closure_captures(
+ sema: &Semantics<'_, RootDatabase>,
+ token: SyntaxToken,
+ file_id: FileId,
+) -> Option<Vec<HighlightedRange>> {
+ let closure = token.parent_ancestors().take(2).find_map(ast::ClosureExpr::cast)?;
+ let search_range = closure.body()?.syntax().text_range();
+ let ty = &sema.type_of_expr(&closure.into())?.original;
+ let c = ty.as_closure()?;
+ Some(
+ c.captured_items(sema.db)
+ .into_iter()
+ .map(|capture| capture.local())
+ .flat_map(|local| {
+ let usages = Definition::Local(local)
+ .usages(sema)
+ .set_scope(Some(SearchScope::file_range(FileRange {
+ file_id,
+ range: search_range,
+ })))
+ .include_self_refs()
+ .all()
+ .references
+ .remove(&file_id)
+ .into_iter()
+ .flatten()
+ .map(|FileReference { category, range, .. }| HighlightedRange {
+ range,
+ category,
+ });
+ let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
+ local
+ .sources(sema.db)
+ .into_iter()
+ .map(|x| x.to_nav(sema.db))
+ .filter(|decl| decl.file_id == file_id)
+ .filter_map(|decl| decl.focus_range)
+ .map(move |range| HighlightedRange { range, category })
+ .chain(usages)
+ })
+ .collect(),
+ )
+}
+
fn highlight_references(
sema: &Semantics<'_, RootDatabase>,
node: &SyntaxNode,
token: SyntaxToken,
file_id: FileId,
) -> Option<Vec<HighlightedRange>> {
- let defs = find_defs(sema, token);
+ let defs = find_defs(sema, token.clone());
let usages = defs
.iter()
.filter_map(|&d| {
@@ -93,12 +146,62 @@ fn highlight_references(
.remove(&file_id)
})
.flatten()
- .map(|FileReference { category: access, range, .. }| HighlightedRange {
- range,
- category: access,
- });
+ .map(|FileReference { category, range, .. }| HighlightedRange { range, category });
let mut res = FxHashSet::default();
for &def in &defs {
+ // highlight trait usages
+ if let Definition::Trait(t) = def {
+ let trait_item_use_scope = (|| {
+ let name_ref = token.parent().and_then(ast::NameRef::cast)?;
+ let path = full_path_of_name_ref(&name_ref)?;
+ let parent = path.syntax().parent()?;
+ match_ast! {
+ match parent {
+ ast::UseTree(it) => it.syntax().ancestors().find(|it| {
+ ast::SourceFile::can_cast(it.kind()) || ast::Module::can_cast(it.kind())
+ }),
+ ast::PathType(it) => it
+ .syntax()
+ .ancestors()
+ .nth(2)
+ .and_then(ast::TypeBoundList::cast)?
+ .syntax()
+ .parent()
+ .filter(|it| ast::WhereClause::can_cast(it.kind()) || ast::TypeParam::can_cast(it.kind()))?
+ .ancestors()
+ .find(|it| {
+ ast::Item::can_cast(it.kind())
+ }),
+ _ => None,
+ }
+ }
+ })();
+ if let Some(trait_item_use_scope) = trait_item_use_scope {
+ res.extend(
+ t.items_with_supertraits(sema.db)
+ .into_iter()
+ .filter_map(|item| {
+ Definition::from(item)
+ .usages(sema)
+ .set_scope(Some(SearchScope::file_range(FileRange {
+ file_id,
+ range: trait_item_use_scope.text_range(),
+ })))
+ .include_self_refs()
+ .all()
+ .references
+ .remove(&file_id)
+ })
+ .flatten()
+ .map(|FileReference { category, range, .. }| HighlightedRange {
+ range,
+ category,
+ }),
+ );
+ }
+ }
+
+ // highlight the defs themselves
match def {
Definition::Local(local) => {
let category = local.is_mut(sema.db).then_some(ReferenceCategory::Write);
@@ -148,9 +251,16 @@ fn highlight_exit_points(
) -> Option<Vec<HighlightedRange>> {
fn hl(
sema: &Semantics<'_, RootDatabase>,
+ def_ranges: [Option<TextRange>; 2],
body: Option<ast::Expr>,
) -> Option<Vec<HighlightedRange>> {
let mut highlights = Vec::new();
+ highlights.extend(
+ def_ranges
+ .into_iter()
+ .flatten()
+ .map(|range| HighlightedRange { category: None, range }),
+ );
let body = body?;
walk_expr(&body, &mut |expr| match expr {
ast::Expr::ReturnExpr(expr) => {
@@ -194,10 +304,21 @@ fn highlight_exit_points(
for anc in token.parent_ancestors() {
return match_ast! {
match anc {
- ast::Fn(fn_) => hl(sema, fn_.body().map(ast::Expr::BlockExpr)),
- ast::ClosureExpr(closure) => hl(sema, closure.body()),
+ ast::Fn(fn_) => hl(sema, [fn_.fn_token().map(|it| it.text_range()), None], fn_.body().map(ast::Expr::BlockExpr)),
+ ast::ClosureExpr(closure) => hl(
+ sema,
+ closure.param_list().map_or([None; 2], |p| [p.l_paren_token().map(|it| it.text_range()), p.r_paren_token().map(|it| it.text_range())]),
+ closure.body()
+ ),
ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try(_)| ast::BlockModifier::Const(_))) {
- hl(sema, Some(block_expr.into()))
+ hl(
+ sema,
+ [block_expr.modifier().and_then(|modifier| match modifier {
+ ast::BlockModifier::Async(t) | ast::BlockModifier::Try(t) | ast::BlockModifier::Const(t) => Some(t.text_range()),
+ _ => None,
+ }), None],
+ Some(block_expr.into())
+ )
} else {
continue;
},
@@ -352,16 +473,17 @@ mod tests {
use super::*;
+ const ENABLED_CONFIG: HighlightRelatedConfig = HighlightRelatedConfig {
+ break_points: true,
+ exit_points: true,
+ references: true,
+ closure_captures: true,
+ yield_points: true,
+ };
+
#[track_caller]
fn check(ra_fixture: &str) {
- let config = HighlightRelatedConfig {
- break_points: true,
- exit_points: true,
- references: true,
- yield_points: true,
- };
-
- check_with_config(ra_fixture, config);
+ check_with_config(ra_fixture, ENABLED_CONFIG);
}
#[track_caller]
@@ -571,6 +693,29 @@ pub async$0 fn foo() {
}
#[test]
+ fn test_hl_let_else_yield_points() {
+ check(
+ r#"
+pub async fn foo() {
+ // ^^^^^
+ let x = foo()
+ .await$0
+ // ^^^^^
+ .await;
+ // ^^^^^
+ || { 0.await };
+ let Some(_) = None else {
+ foo().await
+ // ^^^^^
+ };
+ (async { 0.await }).await
+ // ^^^^^
+}
+"#,
+ );
+ }
+
+ #[test]
fn test_hl_yield_nested_fn() {
check(
r#"
@@ -610,7 +755,8 @@ async fn foo() {
fn test_hl_exit_points() {
check(
r#"
-fn foo() -> u32 {
+ fn foo() -> u32 {
+//^^
if true {
return$0 0;
// ^^^^^^
@@ -629,7 +775,8 @@ fn foo() -> u32 {
fn test_hl_exit_points2() {
check(
r#"
-fn foo() ->$0 u32 {
+ fn foo() ->$0 u32 {
+//^^
if true {
return 0;
// ^^^^^^
@@ -648,7 +795,8 @@ fn foo() ->$0 u32 {
fn test_hl_exit_points3() {
check(
r#"
-fn$0 foo() -> u32 {
+ fn$0 foo() -> u32 {
+//^^
if true {
return 0;
// ^^^^^^
@@ -664,6 +812,26 @@ fn$0 foo() -> u32 {
}
#[test]
+ fn test_hl_let_else_exit_points() {
+ check(
+ r#"
+ fn$0 foo() -> u32 {
+//^^
+ let Some(bar) = None else {
+ return 0;
+ // ^^^^^^
+ };
+
+ 0?;
+ // ^
+ 0xDEAD_BEEF
+ // ^^^^^^^^^^^
+}
+"#,
+ );
+ }
+
+ #[test]
fn test_hl_prefer_ref_over_tail_exit() {
check(
r#"
@@ -694,7 +862,8 @@ macro_rules! never {
() => { never() }
}
fn never() -> ! { loop {} }
-fn foo() ->$0 u32 {
+ fn foo() ->$0 u32 {
+//^^
never();
// ^^^^^^^
never!();
@@ -714,7 +883,8 @@ fn foo() ->$0 u32 {
fn test_hl_inner_tail_exit_points() {
check(
r#"
-fn foo() ->$0 u32 {
+ fn foo() ->$0 u32 {
+//^^
if true {
unsafe {
return 5;
@@ -755,7 +925,8 @@ fn foo() ->$0 u32 {
fn test_hl_inner_tail_exit_points_labeled_block() {
check(
r#"
-fn foo() ->$0 u32 {
+ fn foo() ->$0 u32 {
+//^^
'foo: {
break 'foo 0;
// ^^^^^
@@ -776,7 +947,8 @@ fn foo() ->$0 u32 {
fn test_hl_inner_tail_exit_points_loops() {
check(
r#"
-fn foo() ->$0 u32 {
+ fn foo() ->$0 u32 {
+//^^
'foo: while { return 0; true } {
// ^^^^^^
break 'foo 0;
@@ -1086,12 +1258,7 @@ fn function(field: u32) {
#[test]
fn test_hl_disabled_ref_local() {
- let config = HighlightRelatedConfig {
- references: false,
- break_points: true,
- exit_points: true,
- yield_points: true,
- };
+ let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1106,12 +1273,7 @@ fn foo() {
#[test]
fn test_hl_disabled_ref_local_preserved_break() {
- let config = HighlightRelatedConfig {
- references: false,
- break_points: true,
- exit_points: true,
- yield_points: true,
- };
+ let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1146,12 +1308,7 @@ fn foo() {
#[test]
fn test_hl_disabled_ref_local_preserved_yield() {
- let config = HighlightRelatedConfig {
- references: false,
- break_points: true,
- exit_points: true,
- yield_points: true,
- };
+ let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1182,12 +1339,7 @@ async fn foo() {
#[test]
fn test_hl_disabled_ref_local_preserved_exit() {
- let config = HighlightRelatedConfig {
- references: false,
- break_points: true,
- exit_points: true,
- yield_points: true,
- };
+ let config = HighlightRelatedConfig { references: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1207,7 +1359,8 @@ fn foo() -> i32 {
check_with_config(
r#"
-fn foo() ->$0 i32 {
+ fn foo() ->$0 i32 {
+//^^
let x = 5;
let y = x * 2;
@@ -1225,12 +1378,7 @@ fn foo() ->$0 i32 {
#[test]
fn test_hl_disabled_break() {
- let config = HighlightRelatedConfig {
- references: true,
- break_points: false,
- exit_points: true,
- yield_points: true,
- };
+ let config = HighlightRelatedConfig { break_points: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1246,12 +1394,7 @@ fn foo() {
#[test]
fn test_hl_disabled_yield() {
- let config = HighlightRelatedConfig {
- references: true,
- break_points: true,
- exit_points: true,
- yield_points: false,
- };
+ let config = HighlightRelatedConfig { yield_points: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1265,12 +1408,7 @@ async$0 fn foo() {
#[test]
fn test_hl_disabled_exit() {
- let config = HighlightRelatedConfig {
- references: true,
- break_points: true,
- exit_points: false,
- yield_points: true,
- };
+ let config = HighlightRelatedConfig { exit_points: false, ..ENABLED_CONFIG };
check_with_config(
r#"
@@ -1414,4 +1552,73 @@ impl Trait for () {
"#,
);
}
+
+ #[test]
+ fn test_closure_capture_pipe() {
+ check(
+ r#"
+fn f() {
+ let x = 1;
+ // ^
+ let c = $0|y| x + y;
+ // ^ read
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_closure_capture_move() {
+ check(
+ r#"
+fn f() {
+ let x = 1;
+ // ^
+ let c = move$0 |y| x + y;
+ // ^ read
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_trait_highlights_assoc_item_uses() {
+ check(
+ r#"
+trait Foo {
+ //^^^
+ type T;
+ const C: usize;
+ fn f() {}
+ fn m(&self) {}
+}
+impl Foo for i32 {
+ //^^^
+ type T = i32;
+ const C: usize = 0;
+ fn f() {}
+ fn m(&self) {}
+}
+fn f<T: Foo$0>(t: T) {
+ //^^^
+ let _: T::T;
+ //^
+ t.m();
+ //^
+ T::C;
+ //^
+ T::f();
+ //^
+}
+
+fn f2<T: Foo>(t: T) {
+ //^^^
+ let _: T::T;
+ t.m();
+ T::C;
+ T::f();
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 64b2221bd..5ef6ac948 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -6,7 +6,7 @@ mod tests;
use std::iter;
use either::Either;
-use hir::{HasSource, Semantics};
+use hir::{db::DefDatabase, HasSource, LangItem, Semantics};
use ide_db::{
base_db::FileRange,
defs::{Definition, IdentClass, OperatorClass},
@@ -27,10 +27,25 @@ use crate::{
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct HoverConfig {
pub links_in_hover: bool,
+ pub memory_layout: Option<MemoryLayoutHoverConfig>,
pub documentation: bool,
pub keywords: bool,
pub format: HoverDocFormat,
- pub interpret_tests: bool,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct MemoryLayoutHoverConfig {
+ pub size: Option<MemoryLayoutHoverRenderKind>,
+ pub offset: Option<MemoryLayoutHoverRenderKind>,
+ pub alignment: Option<MemoryLayoutHoverRenderKind>,
+ pub niches: bool,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum MemoryLayoutHoverRenderKind {
+ Decimal,
+ Hexadecimal,
+ Both,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -56,7 +71,7 @@ impl HoverAction {
mod_path: render::path(
db,
it.module(db)?,
- it.name(db).map(|name| name.to_string()),
+ it.name(db).map(|name| name.display(db).to_string()),
),
nav: it.try_to_nav(db)?,
})
@@ -119,8 +134,8 @@ fn hover_simple(
| T![crate]
| T![Self]
| T![_] => 4,
- // index and prefix ops
- T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
+ // index and prefix ops and closure pipe
+ T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] | T![|] => 3,
kind if kind.is_keyword() => 2,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
@@ -219,6 +234,16 @@ fn hover_simple(
};
render::type_info_of(sema, config, &Either::Left(call_expr))
})
+ })
+ // try closure
+ .or_else(|| {
+ descended().find_map(|token| {
+ if token.kind() != T![|] {
+ return None;
+ }
+ let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?;
+ render::closure_expr(sema, config, c)
+ })
});
result.map(|mut res: HoverResult| {
@@ -344,7 +369,14 @@ fn goto_type_action_for_def(db: &RootDatabase, def: Definition) -> Option<HoverA
};
if let Definition::GenericParam(hir::GenericParam::TypeParam(it)) = def {
- it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into()));
+ let krate = it.module(db).krate();
+ let sized_trait =
+ db.lang_item(krate.into(), LangItem::Sized).and_then(|lang_item| lang_item.as_trait());
+
+ it.trait_bounds(db)
+ .into_iter()
+ .filter(|&it| Some(it.into()) != sized_trait)
+ .for_each(|it| push_new_def(it.into()));
} else {
let ty = match def {
Definition::Local(it) => it.ty(db),
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index da725ce50..136214641 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -3,8 +3,8 @@ use std::fmt::Display;
use either::Either;
use hir::{
- db::DefDatabase, Adt, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay,
- MirEvalError, Semantics, TypeInfo,
+ Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasSource, HirDisplay, Layout,
+ LayoutError, Semantics, TypeInfo,
};
use ide_db::{
base_db::SourceDatabase,
@@ -27,7 +27,8 @@ use syntax::{
use crate::{
doc_links::{remove_links, rewrite_links},
hover::walk_and_push_ty,
- HoverAction, HoverConfig, HoverResult, Markup,
+ HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig,
+ MemoryLayoutHoverRenderKind,
};
pub(super) fn type_info_of(
@@ -35,11 +36,20 @@ pub(super) fn type_info_of(
_config: &HoverConfig,
expr_or_pat: &Either<ast::Expr, ast::Pat>,
) -> Option<HoverResult> {
- let TypeInfo { original, adjusted } = match expr_or_pat {
+ let ty_info = match expr_or_pat {
Either::Left(expr) => sema.type_of_expr(expr)?,
Either::Right(pat) => sema.type_of_pat(pat)?,
};
- type_info(sema, _config, original, adjusted)
+ type_info(sema, _config, ty_info)
+}
+
+pub(super) fn closure_expr(
+ sema: &Semantics<'_, RootDatabase>,
+ config: &HoverConfig,
+ c: ast::ClosureExpr,
+) -> Option<HoverResult> {
+ let TypeInfo { original, .. } = sema.type_of_expr(&c.into())?;
+ closure_ty(sema, config, &TypeInfo { original, adjusted: None })
}
pub(super) fn try_expr(
@@ -361,7 +371,7 @@ fn definition_owner_name(db: &RootDatabase, def: &Definition) -> Option<String>
Definition::Variant(e) => Some(e.parent_enum(db).name(db)),
_ => None,
}
- .map(|name| name.to_string())
+ .map(|name| name.display(db).to_string())
}
pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option<String>) -> String {
@@ -371,7 +381,7 @@ pub(super) fn path(db: &RootDatabase, module: hir::Module, item_name: Option<Str
.path_to_root(db)
.into_iter()
.rev()
- .flat_map(|it| it.name(db).map(|name| name.to_string()));
+ .flat_map(|it| it.name(db).map(|name| name.display(db).to_string()));
crate_name.into_iter().chain(module_path).chain(item_name).join("::")
}
@@ -384,53 +394,46 @@ pub(super) fn definition(
let mod_path = definition_mod_path(db, &def);
let (label, docs) = match def {
Definition::Macro(it) => label_and_docs(db, it),
- Definition::Field(it) => label_and_layout_info_and_docs(db, it, |&it| {
- let var_def = it.parent_def(db);
- let id = it.index();
- let layout = it.layout(db).ok()?;
- let offset = match var_def {
- hir::VariantDef::Struct(s) => Adt::from(s)
- .layout(db)
- .ok()
- .map(|layout| format!(", offset = {}", layout.fields.offset(id).bytes())),
- _ => None,
- };
- Some(format!(
- "size = {}, align = {}{}",
- layout.size.bytes(),
- layout.align.abi.bytes(),
- offset.as_deref().unwrap_or_default()
- ))
- }),
- Definition::Module(it) => label_and_docs(db, it),
- Definition::Function(it) => label_and_layout_info_and_docs(db, it, |_| {
- if !config.interpret_tests {
- return None;
- }
- match it.eval(db) {
- Ok(()) => Some("pass".into()),
- Err(MirEvalError::Panic) => Some("fail".into()),
- Err(MirEvalError::MirLowerError(f, e)) => {
- let name = &db.function_data(f).name;
- Some(format!("error: fail to lower {name} due {e:?}"))
+ Definition::Field(it) => label_and_layout_info_and_docs(
+ db,
+ it,
+ config,
+ |&it| it.layout(db),
+ |_| {
+ let var_def = it.parent_def(db);
+ let id = it.index();
+ match var_def {
+ hir::VariantDef::Struct(s) => {
+ Adt::from(s).layout(db).ok().and_then(|layout| layout.field_offset(id))
+ }
+ _ => None,
}
- Err(e) => Some(format!("error: {e:?}")),
- }
- }),
- Definition::Adt(it) => label_and_layout_info_and_docs(db, it, |&it| {
- let layout = it.layout(db).ok()?;
- Some(format!("size = {}, align = {}", layout.size.bytes(), layout.align.abi.bytes()))
- }),
- Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
- if !it.parent_enum(db).is_data_carrying(db) {
- match it.eval(db) {
- Ok(x) => Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") }),
- Err(_) => it.value(db).map(|x| format!("{x:?}")),
+ },
+ ),
+ Definition::Module(it) => label_and_docs(db, it),
+ Definition::Function(it) => label_and_docs(db, it),
+ Definition::Adt(it) => {
+ label_and_layout_info_and_docs(db, it, config, |&it| it.layout(db), |_| None)
+ }
+ Definition::Variant(it) => label_value_and_layout_info_and_docs(
+ db,
+ it,
+ config,
+ |&it| {
+ if !it.parent_enum(db).is_data_carrying(db) {
+ match it.eval(db) {
+ Ok(x) => {
+ Some(if x >= 10 { format!("{x} ({x:#X})") } else { format!("{x}") })
+ }
+ Err(_) => it.value(db).map(|x| format!("{x:?}")),
+ }
+ } else {
+ None
}
- } else {
- None
- }
- }),
+ },
+ |it| it.layout(db),
+ |layout| layout.enum_tag_size(),
+ ),
Definition::Const(it) => label_value_and_docs(db, it, |it| {
let body = it.render_eval(db);
match body {
@@ -455,22 +458,26 @@ pub(super) fn definition(
}),
Definition::Trait(it) => label_and_docs(db, it),
Definition::TraitAlias(it) => label_and_docs(db, it),
- Definition::TypeAlias(it) => label_and_docs(db, it),
+ Definition::TypeAlias(it) => {
+ label_and_layout_info_and_docs(db, it, config, |&it| it.ty(db).layout(db), |_| None)
+ }
Definition::BuiltinType(it) => {
return famous_defs
.and_then(|fd| builtin(fd, it))
- .or_else(|| Some(Markup::fenced_block(&it.name())))
+ .or_else(|| Some(Markup::fenced_block(&it.name().display(db))))
}
- Definition::Local(it) => return local(db, it),
+ Definition::Local(it) => return local(db, it, config),
Definition::SelfType(impl_def) => {
impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
}
Definition::GenericParam(it) => label_and_docs(db, it),
- Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db))),
+ Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))),
// FIXME: We should be able to show more info about these
Definition::BuiltinAttr(it) => return render_builtin_attr(db, it),
Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))),
- Definition::DeriveHelper(it) => (format!("derive_helper {}", it.name(db)), None),
+ Definition::DeriveHelper(it) => {
+ (format!("derive_helper {}", it.name(db).display(db)), None)
+ }
};
let docs = docs
@@ -490,10 +497,13 @@ pub(super) fn definition(
fn type_info(
sema: &Semantics<'_, RootDatabase>,
- _config: &HoverConfig,
- original: hir::Type,
- adjusted: Option<hir::Type>,
+ config: &HoverConfig,
+ ty: TypeInfo,
) -> Option<HoverResult> {
+ if let Some(res) = closure_ty(sema, config, &ty) {
+ return Some(res);
+ };
+ let TypeInfo { original, adjusted } = ty;
let mut res = HoverResult::default();
let mut targets: Vec<hir::ModuleDef> = Vec::new();
let mut push_new_def = |item: hir::ModuleDef| {
@@ -523,6 +533,67 @@ fn type_info(
Some(res)
}
+fn closure_ty(
+ sema: &Semantics<'_, RootDatabase>,
+ config: &HoverConfig,
+ TypeInfo { original, adjusted }: &TypeInfo,
+) -> Option<HoverResult> {
+ let c = original.as_closure()?;
+ let mut captures_rendered = c.captured_items(sema.db)
+ .into_iter()
+ .map(|it| {
+ let borrow_kind = match it.kind() {
+ CaptureKind::SharedRef => "immutable borrow",
+ CaptureKind::UniqueSharedRef => "unique immutable borrow ([read more](https://doc.rust-lang.org/stable/reference/types/closure.html#unique-immutable-borrows-in-captures))",
+ CaptureKind::MutableRef => "mutable borrow",
+ CaptureKind::Move => "move",
+ };
+ format!("* `{}` by {}", it.display_place(sema.db), borrow_kind)
+ })
+ .join("\n");
+ if captures_rendered.trim().is_empty() {
+ captures_rendered = "This closure captures nothing".to_string();
+ }
+ let mut targets: Vec<hir::ModuleDef> = Vec::new();
+ let mut push_new_def = |item: hir::ModuleDef| {
+ if !targets.contains(&item) {
+ targets.push(item);
+ }
+ };
+ walk_and_push_ty(sema.db, original, &mut push_new_def);
+ c.capture_types(sema.db).into_iter().for_each(|ty| {
+ walk_and_push_ty(sema.db, &ty, &mut push_new_def);
+ });
+
+ let adjusted = if let Some(adjusted_ty) = adjusted {
+ walk_and_push_ty(sema.db, &adjusted_ty, &mut push_new_def);
+ format!(
+ "\nCoerced to: {}",
+ adjusted_ty.display(sema.db).with_closure_style(hir::ClosureStyle::ImplFn)
+ )
+ } else {
+ String::new()
+ };
+ let mut markup = format!("```rust\n{}", c.display_with_id(sema.db),);
+
+ if let Some(layout) =
+ render_memory_layout(config.memory_layout, || original.layout(sema.db), |_| None, |_| None)
+ {
+ format_to!(markup, "{layout}");
+ }
+ format_to!(
+ markup,
+ "\n{}\n```{adjusted}\n\n## Captures\n{}",
+ c.display_with_impl(sema.db),
+ captures_rendered,
+ );
+
+ let mut res = HoverResult::default();
+ res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
+ res.markup = markup.into();
+ Some(res)
+}
+
fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Markup> {
let name = attr.name(db);
let desc = format!("#[{name}]");
@@ -553,21 +624,59 @@ where
(label, docs)
}
-fn label_and_layout_info_and_docs<D, E, V>(
+fn label_and_layout_info_and_docs<D, E, E2>(
+ db: &RootDatabase,
+ def: D,
+ config: &HoverConfig,
+ layout_extractor: E,
+ layout_offset_extractor: E2,
+) -> (String, Option<hir::Documentation>)
+where
+ D: HasAttrs + HirDisplay,
+ E: Fn(&D) -> Result<Layout, LayoutError>,
+ E2: Fn(&Layout) -> Option<u64>,
+{
+ let mut label = def.display(db).to_string();
+ if let Some(layout) = render_memory_layout(
+ config.memory_layout,
+ || layout_extractor(&def),
+ layout_offset_extractor,
+ |_| None,
+ ) {
+ format_to!(label, "{layout}");
+ }
+ let docs = def.attrs(db).docs();
+ (label, docs)
+}
+
+fn label_value_and_layout_info_and_docs<D, E, E2, E3, V>(
db: &RootDatabase,
def: D,
+ config: &HoverConfig,
value_extractor: E,
+ layout_extractor: E2,
+ layout_tag_extractor: E3,
) -> (String, Option<hir::Documentation>)
where
D: HasAttrs + HirDisplay,
E: Fn(&D) -> Option<V>,
+ E2: Fn(&D) -> Result<Layout, LayoutError>,
+ E3: Fn(&Layout) -> Option<usize>,
V: Display,
{
- let label = if let Some(value) = value_extractor(&def) {
- format!("{} // {value}", def.display(db))
- } else {
- def.display(db).to_string()
+ let value = value_extractor(&def);
+ let mut label = match value {
+ Some(value) => format!("{} = {value}", def.display(db)),
+ None => def.display(db).to_string(),
};
+ if let Some(layout) = render_memory_layout(
+ config.memory_layout,
+ || layout_extractor(&def),
+ |_| None,
+ layout_tag_extractor,
+ ) {
+ format_to!(label, "{layout}");
+ }
let docs = def.attrs(db).docs();
(label, docs)
}
@@ -616,26 +725,26 @@ fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Optio
fn builtin(famous_defs: &FamousDefs<'_, '_>, builtin: hir::BuiltinType) -> Option<Markup> {
// std exposes prim_{} modules with docstrings on the root to document the builtins
- let primitive_mod = format!("prim_{}", builtin.name());
+ let primitive_mod = format!("prim_{}", builtin.name().display(famous_defs.0.db));
let doc_owner = find_std_module(famous_defs, &primitive_mod)?;
let docs = doc_owner.attrs(famous_defs.0.db).docs()?;
- markup(Some(docs.into()), builtin.name().to_string(), None)
+ markup(Some(docs.into()), builtin.name().display(famous_defs.0.db).to_string(), None)
}
fn find_std_module(famous_defs: &FamousDefs<'_, '_>, name: &str) -> Option<hir::Module> {
let db = famous_defs.0.db;
let std_crate = famous_defs.std()?;
let std_root_module = std_crate.root_module(db);
- std_root_module
- .children(db)
- .find(|module| module.name(db).map_or(false, |module| module.to_string() == name))
+ std_root_module.children(db).find(|module| {
+ module.name(db).map_or(false, |module| module.display(db).to_string() == name)
+ })
}
-fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
+fn local(db: &RootDatabase, it: hir::Local, config: &HoverConfig) -> Option<Markup> {
let ty = it.ty(db);
let ty = ty.display_truncated(db, None);
let is_mut = if it.is_mut(db) { "mut " } else { "" };
- let desc = match it.primary_source(db).into_ident_pat() {
+ let mut desc = match it.primary_source(db).into_ident_pat() {
Some(ident) => {
let name = it.name(db);
let let_kw = if ident
@@ -647,13 +756,91 @@ fn local(db: &RootDatabase, it: hir::Local) -> Option<Markup> {
} else {
""
};
- format!("{let_kw}{is_mut}{name}: {ty}")
+ format!("{let_kw}{is_mut}{}: {ty}", name.display(db))
}
None => format!("{is_mut}self: {ty}"),
};
+ if let Some(layout) =
+ render_memory_layout(config.memory_layout, || it.ty(db).layout(db), |_| None, |_| None)
+ {
+ format_to!(desc, "{layout}");
+ }
markup(None, desc, None)
}
+fn render_memory_layout(
+ config: Option<MemoryLayoutHoverConfig>,
+ layout: impl FnOnce() -> Result<Layout, LayoutError>,
+ offset: impl FnOnce(&Layout) -> Option<u64>,
+ tag: impl FnOnce(&Layout) -> Option<usize>,
+) -> Option<String> {
+ // field
+
+ let config = config?;
+ let layout = layout().ok()?;
+
+ let mut label = String::from(" // ");
+
+ if let Some(render) = config.size {
+ let size = match tag(&layout) {
+ Some(tag) => layout.size() as usize - tag,
+ None => layout.size() as usize,
+ };
+ format_to!(label, "size = ");
+ match render {
+ MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{size}"),
+ MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{size:#X}"),
+ MemoryLayoutHoverRenderKind::Both if size >= 10 => {
+ format_to!(label, "{size} ({size:#X})")
+ }
+ MemoryLayoutHoverRenderKind::Both => format_to!(label, "{size}"),
+ }
+ format_to!(label, ", ");
+ }
+
+ if let Some(render) = config.alignment {
+ let align = layout.align();
+ format_to!(label, "align = ");
+ match render {
+ MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{align}",),
+ MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{align:#X}",),
+ MemoryLayoutHoverRenderKind::Both if align >= 10 => {
+ format_to!(label, "{align} ({align:#X})")
+ }
+ MemoryLayoutHoverRenderKind::Both => {
+ format_to!(label, "{align}")
+ }
+ }
+ format_to!(label, ", ");
+ }
+
+ if let Some(render) = config.offset {
+ if let Some(offset) = offset(&layout) {
+ format_to!(label, "offset = ");
+ match render {
+ MemoryLayoutHoverRenderKind::Decimal => format_to!(label, "{offset}"),
+ MemoryLayoutHoverRenderKind::Hexadecimal => format_to!(label, "{offset:#X}"),
+ MemoryLayoutHoverRenderKind::Both if offset >= 10 => {
+ format_to!(label, "{offset} ({offset:#X})")
+ }
+ MemoryLayoutHoverRenderKind::Both => {
+ format_to!(label, "{offset}")
+ }
+ }
+ format_to!(label, ", ");
+ }
+ }
+
+ if config.niches {
+ if let Some(niches) = layout.niches() {
+ format_to!(label, "niches = {niches}, ");
+ }
+ }
+ label.pop(); // ' '
+ label.pop(); // ','
+ Some(label)
+}
+
struct KeywordHint {
description: String,
keyword_mod: String,
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 57bf0f9ad..f75ebfa12 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -2,14 +2,21 @@ use expect_test::{expect, Expect};
use ide_db::base_db::{FileLoader, FileRange};
use syntax::TextRange;
-use crate::{fixture, HoverConfig, HoverDocFormat};
+use crate::{
+ fixture, HoverConfig, HoverDocFormat, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
+};
const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
links_in_hover: false,
+ memory_layout: Some(MemoryLayoutHoverConfig {
+ size: Some(MemoryLayoutHoverRenderKind::Both),
+ offset: Some(MemoryLayoutHoverRenderKind::Both),
+ alignment: Some(MemoryLayoutHoverRenderKind::Both),
+ niches: true,
+ }),
documentation: true,
format: HoverDocFormat::Markdown,
keywords: true,
- interpret_tests: false,
};
fn check_hover_no_result(ra_fixture: &str) {
@@ -58,6 +65,23 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) {
expect.assert_eq(&actual)
}
+fn check_hover_no_memory_layout(ra_fixture: &str, expect: Expect) {
+ let (analysis, position) = fixture::position(ra_fixture);
+ let hover = analysis
+ .hover(
+ &HoverConfig { memory_layout: None, ..HOVER_BASE_CONFIG },
+ FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) },
+ )
+ .unwrap()
+ .unwrap();
+
+ let content = analysis.db.file_text(position.file_id);
+ let hovered_element = &content[hover.range];
+
+ let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup);
+ expect.assert_eq(&actual)
+}
+
fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture);
let hover = analysis
@@ -97,6 +121,15 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) {
expect.assert_eq(hover.info.markup.as_str())
}
+fn check_hover_range_actions(ra_fixture: &str, expect: Expect) {
+ let (analysis, range) = fixture::range(ra_fixture);
+ let hover = analysis
+ .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range)
+ .unwrap()
+ .unwrap();
+ expect.assert_debug_eq(&hover.info.actions);
+}
+
fn check_hover_range_no_results(ra_fixture: &str) {
let (analysis, range) = fixture::range(ra_fixture);
let hover = analysis.hover(&HOVER_BASE_CONFIG, range).unwrap();
@@ -124,7 +157,7 @@ fn foo() {
*local*
```rust
- let local: i32
+ let local: i32 // size = 4, align = 4
```
"#]],
);
@@ -199,6 +232,181 @@ fn main() {
}
#[test]
+fn hover_closure() {
+ check(
+ r#"
+//- minicore: copy
+fn main() {
+ let x = 2;
+ let y = $0|z| x + z;
+}
+"#,
+ expect![[r#"
+ *|*
+ ```rust
+ {closure#0} // size = 8, align = 8, niches = 1
+ impl Fn(i32) -> i32
+ ```
+
+ ## Captures
+ * `x` by immutable borrow
+ "#]],
+ );
+
+ check(
+ r#"
+//- minicore: copy
+fn foo(x: impl Fn(i32) -> i32) {
+
+}
+fn main() {
+ foo($0|x: i32| x)
+}
+"#,
+ expect![[r#"
+ *|*
+ ```rust
+ {closure#0} // size = 0, align = 1
+ impl Fn(i32) -> i32
+ ```
+
+ ## Captures
+ This closure captures nothing
+ "#]],
+ );
+
+ check(
+ r#"
+//- minicore: copy
+
+struct Z { f: i32 }
+
+struct Y(&'static mut Z)
+
+struct X {
+ f1: Y,
+ f2: (Y, Y),
+}
+
+fn main() {
+ let x: X;
+ let y = $0|| {
+ x.f1;
+ &mut x.f2.0 .0.f;
+ };
+}
+"#,
+ expect![[r#"
+ *|*
+ ```rust
+ {closure#0} // size = 16 (0x10), align = 8, niches = 1
+ impl FnOnce()
+ ```
+
+ ## Captures
+ * `x.f1` by move
+ * `(*x.f2.0.0).f` by mutable borrow
+ "#]],
+ );
+ check(
+ r#"
+//- minicore: copy, option
+
+fn do_char(c: char) {}
+
+fn main() {
+ let x = None;
+ let y = |$0| {
+ match x {
+ Some(c) => do_char(c),
+ None => x = None,
+ }
+ };
+}
+"#,
+ expect![[r#"
+ *|*
+ ```rust
+ {closure#0} // size = 8, align = 8, niches = 1
+ impl FnMut()
+ ```
+
+ ## Captures
+ * `x` by mutable borrow
+ "#]],
+ );
+}
+
+#[test]
+fn hover_ranged_closure() {
+ check_hover_range(
+ r#"
+//- minicore: fn
+struct S;
+struct S2;
+fn main() {
+ let x = &S;
+ let y = ($0|| {x; S2}$0).call();
+}
+"#,
+ expect![[r#"
+ ```rust
+ {closure#0} // size = 8, align = 8, niches = 1
+ impl FnOnce() -> S2
+ ```
+ Coerced to: &impl FnOnce() -> S2
+
+ ## Captures
+ * `x` by move"#]],
+ );
+ check_hover_range_actions(
+ r#"
+//- minicore: fn
+struct S;
+struct S2;
+fn main() {
+ let x = &S;
+ let y = ($0|| {x; S2}$0).call();
+}
+"#,
+ expect![[r#"
+ [
+ GoToType(
+ [
+ HoverGotoTypeData {
+ mod_path: "test::S2",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 10..20,
+ focus_range: 17..19,
+ name: "S2",
+ kind: Struct,
+ description: "struct S2",
+ },
+ },
+ HoverGotoTypeData {
+ mod_path: "test::S",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 0..9,
+ focus_range: 7..8,
+ name: "S",
+ kind: Struct,
+ description: "struct S",
+ },
+ },
+ ],
+ ),
+ ]
+ "#]],
+ );
+}
+
+#[test]
fn hover_shows_long_type_of_an_expression() {
check(
r#"
@@ -222,12 +430,12 @@ fn main() {
}
"#,
expect![[r#"
- *iter*
+ *iter*
- ```rust
- let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
- ```
- "#]],
+ ```rust
+ let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, impl Fn(&mut u32, &u32, &mut u32) -> Option<u32>, u32>> // size = 8, align = 4
+ ```
+ "#]],
);
}
@@ -604,12 +812,12 @@ fn main() {
let zz$0 = Test { t: 23u8, k: 33 };
}"#,
expect![[r#"
- *zz*
+ *zz*
- ```rust
- let zz: Test<i32>
- ```
- "#]],
+ ```rust
+ let zz: Test<i32> // size = 8, align = 4
+ ```
+ "#]],
);
check_hover_range(
r#"
@@ -655,12 +863,12 @@ use Option::Some;
fn main() { let b$0ar = Some(12); }
"#,
expect![[r#"
- *bar*
+ *bar*
- ```rust
- let bar: Option<i32>
- ```
- "#]],
+ ```rust
+ let bar: Option<i32> // size = 4, align = 4
+ ```
+ "#]],
);
}
@@ -724,12 +932,12 @@ fn hover_for_local_variable() {
check(
r#"fn func(foo: i32) { fo$0o; }"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- foo: i32
- ```
- "#]],
+ ```rust
+ foo: i32 // size = 4, align = 4
+ ```
+ "#]],
)
}
@@ -738,12 +946,12 @@ fn hover_for_local_variable_pat() {
check(
r#"fn func(fo$0o: i32) {}"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- foo: i32
- ```
- "#]],
+ ```rust
+ foo: i32 // size = 4, align = 4
+ ```
+ "#]],
)
}
@@ -752,12 +960,12 @@ fn hover_local_var_edge() {
check(
r#"fn func(foo: i32) { if true { $0foo; }; }"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- foo: i32
- ```
- "#]],
+ ```rust
+ foo: i32 // size = 4, align = 4
+ ```
+ "#]],
)
}
@@ -766,12 +974,12 @@ fn hover_for_param_edge() {
check(
r#"fn func($0foo: i32) {}"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- foo: i32
- ```
- "#]],
+ ```rust
+ foo: i32 // size = 4, align = 4
+ ```
+ "#]],
)
}
@@ -810,12 +1018,12 @@ impl Thing {
fn main() { let foo_$0test = Thing::new(); }
"#,
expect![[r#"
- *foo_test*
+ *foo_test*
- ```rust
- let foo_test: Thing
- ```
- "#]],
+ ```rust
+ let foo_test: Thing // size = 4, align = 4
+ ```
+ "#]],
)
}
@@ -970,12 +1178,12 @@ fn y() {
}
"#,
expect![[r#"
- *x*
+ *x*
- ```rust
- let x: i32
- ```
- "#]],
+ ```rust
+ let x: i32 // size = 4, align = 4
+ ```
+ "#]],
)
}
@@ -1100,12 +1308,12 @@ macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
fn foo(bar:u32) { let a = id!(ba$0r); }
"#,
expect![[r#"
- *bar*
+ *bar*
- ```rust
- bar: u32
- ```
- "#]],
+ ```rust
+ bar: u32 // size = 4, align = 4
+ ```
+ "#]],
);
}
@@ -1118,12 +1326,12 @@ macro_rules! id { ($($tt:tt)*) => { id_deep!($($tt)*) } }
fn foo(bar:u32) { let a = id!(ba$0r); }
"#,
expect![[r#"
- *bar*
+ *bar*
- ```rust
- bar: u32
- ```
- "#]],
+ ```rust
+ bar: u32 // size = 4, align = 4
+ ```
+ "#]],
);
}
@@ -1320,16 +1528,16 @@ fn test_hover_function_pointer_show_identifiers() {
check(
r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- test
- ```
+ ```rust
+ test
+ ```
- ```rust
- type foo = fn(a: i32, b: i32) -> i32
- ```
- "#]],
+ ```rust
+ type foo = fn(a: i32, b: i32) -> i32 // size = 8, align = 8, niches = 1
+ ```
+ "#]],
);
}
@@ -1338,16 +1546,16 @@ fn test_hover_function_pointer_no_identifier() {
check(
r#"type foo$0 = fn(i32, _: i32) -> i32;"#,
expect![[r#"
- *foo*
+ *foo*
- ```rust
- test
- ```
+ ```rust
+ test
+ ```
- ```rust
- type foo = fn(i32, i32) -> i32
- ```
- "#]],
+ ```rust
+ type foo = fn(i32, i32) -> i32 // size = 8, align = 8, niches = 1
+ ```
+ "#]],
);
}
@@ -1668,6 +1876,86 @@ pub fn fo$0o() {}
}
#[test]
+fn test_hover_layout_of_variant() {
+ check(
+ r#"enum Foo {
+ Va$0riant1(u8, u16),
+ Variant2(i32, u8, i64),
+ }"#,
+ expect![[r#"
+ *Variant1*
+
+ ```rust
+ test::Foo
+ ```
+
+ ```rust
+ Variant1(u8, u16) // size = 4, align = 2
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn test_hover_layout_of_enum() {
+ check(
+ r#"enum $0Foo {
+ Variant1(u8, u16),
+ Variant2(i32, u8, i64),
+ }"#,
+ expect![[r#"
+ *Foo*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ enum Foo // size = 16 (0x10), align = 8, niches = 254
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn test_hover_no_memory_layout() {
+ check_hover_no_memory_layout(
+ r#"struct Foo { fiel$0d_a: u8, field_b: i32, field_c: i16 }"#,
+ expect![[r#"
+ *field_a*
+
+ ```rust
+ test::Foo
+ ```
+
+ ```rust
+ field_a: u8
+ ```
+ "#]],
+ );
+
+ check_hover_no_memory_layout(
+ r#"
+//- minicore: copy
+fn main() {
+ let x = 2;
+ let y = $0|z| x + z;
+}
+"#,
+ expect![[r#"
+ *|*
+ ```rust
+ {closure#0}
+ impl Fn(i32) -> i32
+ ```
+
+ ## Captures
+ * `x` by immutable borrow
+ "#]],
+ );
+}
+
+#[test]
fn test_hover_macro_generated_struct_fn_doc_comment() {
cov_mark::check!(hover_macro_generated_struct_fn_doc_comment);
@@ -2021,6 +2309,19 @@ fn main() { let s$0t = S{ f1:Arg(0) }; }
}
#[test]
+fn test_hover_generic_excludes_sized_go_to_action() {
+ check_actions(
+ r#"
+//- minicore: sized
+struct S<T$0>(T);
+ "#,
+ expect![[r#"
+ []
+ "#]],
+ );
+}
+
+#[test]
fn test_hover_generic_struct_has_flattened_goto_type_actions() {
check_actions(
r#"
@@ -2079,52 +2380,53 @@ mod M {
fn main() { let s$0t = (A(1), B(2), M::C(3) ); }
"#,
expect![[r#"
- [
- GoToType(
- [
- HoverGotoTypeData {
- mod_path: "test::A",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 0..14,
- focus_range: 7..8,
- name: "A",
- kind: Struct,
- description: "struct A",
- },
+ [
+ GoToType(
+ [
+ HoverGotoTypeData {
+ mod_path: "test::A",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 0..14,
+ focus_range: 7..8,
+ name: "A",
+ kind: Struct,
+ description: "struct A",
},
- HoverGotoTypeData {
- mod_path: "test::B",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 15..29,
- focus_range: 22..23,
- name: "B",
- kind: Struct,
- description: "struct B",
- },
+ },
+ HoverGotoTypeData {
+ mod_path: "test::B",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 15..29,
+ focus_range: 22..23,
+ name: "B",
+ kind: Struct,
+ description: "struct B",
},
- HoverGotoTypeData {
- mod_path: "test::M::C",
- nav: NavigationTarget {
- file_id: FileId(
- 0,
- ),
- full_range: 42..60,
- focus_range: 53..54,
- name: "C",
- kind: Struct,
- description: "pub struct C",
- },
+ },
+ HoverGotoTypeData {
+ mod_path: "test::M::C",
+ nav: NavigationTarget {
+ file_id: FileId(
+ 0,
+ ),
+ full_range: 42..60,
+ focus_range: 53..54,
+ name: "C",
+ kind: Struct,
+ container_name: "M",
+ description: "pub struct C",
},
- ],
- ),
- ]
- "#]],
+ },
+ ],
+ ),
+ ]
+ "#]],
);
}
@@ -2453,6 +2755,7 @@ pub mod future {
focus_range: 60..66,
name: "Future",
kind: Trait,
+ container_name: "future",
description: "pub trait Future",
},
},
@@ -2908,7 +3211,7 @@ fn main() {
*f*
```rust
- f: &i32
+ f: &i32 // size = 8, align = 8, niches = 1
```
---
@@ -2958,7 +3261,7 @@ fn main() {
*value*
```rust
- let value: Const<1>
+ let value: Const<1> // size = 0, align = 1
```
"#]],
);
@@ -2978,7 +3281,7 @@ fn main() {
*value*
```rust
- let value: Const<0>
+ let value: Const<0> // size = 0, align = 1
```
"#]],
);
@@ -2998,7 +3301,7 @@ fn main() {
*value*
```rust
- let value: Const<-1>
+ let value: Const<-1> // size = 0, align = 1
```
"#]],
);
@@ -3018,7 +3321,7 @@ fn main() {
*value*
```rust
- let value: Const<true>
+ let value: Const<true> // size = 0, align = 1
```
"#]],
);
@@ -3038,7 +3341,7 @@ fn main() {
*value*
```rust
- let value: Const<'🦀'>
+ let value: Const<'🦀'> // size = 0, align = 1
```
"#]],
);
@@ -3054,12 +3357,12 @@ impl Foo {
}
"#,
expect![[r#"
- *self*
+ *self*
- ```rust
- self: &Foo
- ```
- "#]],
+ ```rust
+ self: &Foo // size = 8, align = 8, niches = 1
+ ```
+ "#]],
);
}
@@ -3074,12 +3377,12 @@ impl Foo {
}
"#,
expect![[r#"
- *self*
+ *self*
- ```rust
- self: Arc<Foo>
- ```
- "#]],
+ ```rust
+ self: Arc<Foo> // size = 0, align = 1
+ ```
+ "#]],
);
}
@@ -3115,7 +3418,7 @@ mod Foo$0 {
}
#[test]
-fn hover_doc_outer_inner_attribue() {
+fn hover_doc_outer_inner_attribute() {
check(
r#"
#[doc = "Be quick;"]
@@ -3146,7 +3449,7 @@ mod Foo$0 {
}
#[test]
-fn hover_doc_block_style_indentend() {
+fn hover_doc_block_style_indent_end() {
check(
r#"
/**
@@ -3455,16 +3758,16 @@ struct Foo<const LEN: usize>;
type Fo$0o2 = Foo<2>;
"#,
expect![[r#"
- *Foo2*
+ *Foo2*
- ```rust
- test
- ```
+ ```rust
+ test
+ ```
- ```rust
- type Foo2 = Foo<2>
- ```
- "#]],
+ ```rust
+ type Foo2 = Foo<2> // size = 0, align = 1
+ ```
+ "#]],
);
}
@@ -3504,7 +3807,7 @@ enum E {
```
```rust
- A = 8
+ A = 8 // size = 1, align = 1
```
---
@@ -3529,7 +3832,7 @@ enum E {
```
```rust
- A = 12 (0xC)
+ A = 12 (0xC) // size = 1, align = 1
```
---
@@ -3555,7 +3858,7 @@ enum E {
```
```rust
- B = 2
+ B = 2 // size = 1, align = 1
```
---
@@ -3581,7 +3884,7 @@ enum E {
```
```rust
- B = 5
+ B = 5 // size = 1, align = 1
```
---
@@ -4035,6 +4338,278 @@ const FOO$0: f64 = 1.0f64;
}
#[test]
+fn hover_const_eval_floating_point() {
+ check(
+ r#"
+extern "rust-intrinsic" {
+ pub fn expf64(x: f64) -> f64;
+}
+
+const FOO$0: f64 = expf64(1.2);
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: f64 = 3.3201169227365472
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn hover_const_eval_enum() {
+ check(
+ r#"
+enum Enum {
+ V1,
+ V2,
+}
+
+const VX: Enum = Enum::V1;
+
+const FOO$0: Enum = VX;
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: Enum = V1
+ ```
+ "#]],
+ );
+ check(
+ r#"
+//- minicore: option
+const FOO$0: Option<i32> = Some(2);
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: Option<i32> = Some(2)
+ ```
+ "#]],
+ );
+ check(
+ r#"
+//- minicore: option
+const FOO$0: Option<&i32> = Some(2).as_ref();
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: Option<&i32> = Some(&2)
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn hover_const_eval_dyn_trait() {
+ check(
+ r#"
+//- minicore: fmt, coerce_unsized, builtin_impls
+use core::fmt::Debug;
+
+const FOO$0: &dyn Debug = &2i32;
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: &dyn Debug = &2
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn hover_const_eval_slice() {
+ check(
+ r#"
+//- minicore: slice, index, coerce_unsized
+const FOO$0: &[i32] = &[1, 2, 3 + 4];
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: &[i32] = &[1, 2, 7]
+ ```
+ "#]],
+ );
+ check(
+ r#"
+//- minicore: slice, index, coerce_unsized
+const FOO$0: &[i32; 5] = &[12; 5];
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: &[i32; 5] = &[12, 12, 12, 12, 12]
+ ```
+ "#]],
+ );
+ check(
+ r#"
+//- minicore: slice, index, coerce_unsized
+
+const FOO$0: (&i32, &[i32], &i32) = {
+ let a: &[i32] = &[1, 2, 3];
+ (&a[0], a, &a[0])
+}
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: (&i32, &[i32], &i32) = (&1, &[1, 2, 3], &1)
+ ```
+ "#]],
+ );
+ check(
+ r#"
+//- minicore: slice, index, coerce_unsized
+
+struct Tree(&[Tree]);
+
+const FOO$0: Tree = {
+ let x = &[Tree(&[]), Tree(&[Tree(&[])])];
+ Tree(&[Tree(x), Tree(x)])
+}
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: Tree = Tree(&[Tree(&[Tree(&[]), Tree(&[Tree(&[])])]), Tree(&[Tree(&[]), Tree(&[Tree(&[])])])])
+ ```
+ "#]],
+ );
+ // FIXME: Show the data of unsized structs
+ check(
+ r#"
+//- minicore: slice, index, coerce_unsized, transmute
+#[repr(transparent)]
+struct S<T: ?Sized>(T);
+const FOO$0: &S<[u8]> = core::mem::transmute::<&[u8], _>(&[1, 2, 3]);
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: &S<[u8]> = &S
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn hover_const_eval_str() {
+ check(
+ r#"
+const FOO$0: &str = "foo";
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: &str = "foo"
+ ```
+ "#]],
+ );
+ check(
+ r#"
+struct X {
+ a: &'static str,
+ b: &'static str,
+}
+const FOO$0: X = X {
+ a: "axiom",
+ b: "buy N large",
+};
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: X = X { a: "axiom", b: "buy N large" }
+ ```
+ "#]],
+ );
+ check(
+ r#"
+const FOO$0: (&str, &str) = {
+ let x = "foo";
+ (x, x)
+};
+"#,
+ expect![[r#"
+ *FOO*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ const FOO: (&str, &str) = ("foo", "foo")
+ ```
+ "#]],
+ );
+}
+
+#[test]
fn hover_const_eval_in_generic_trait() {
// Doesn't compile, but we shouldn't crash.
check(
@@ -4115,7 +4690,7 @@ fn foo(e: E) {
```
```rust
- A = 3
+ A = 3 // size = 0, align = 1
```
---
@@ -4137,9 +4712,9 @@ fn main() {
*tile4*
```rust
- let tile4: [u32; 8]
+ let tile4: [u32; 8] // size = 32 (0x20), align = 4
```
- "#]],
+ "#]],
);
}
@@ -4242,7 +4817,7 @@ fn foo() {
/// [threads]: ../book/ch16-01-threads.html#using-move-closures-with-threads
mod move_keyword {}
"#,
- expect![[r##"
+ expect![[r#"
*move*
```rust
@@ -4251,11 +4826,11 @@ mod move_keyword {}
---
- [closure](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html)
- [closures](https://doc.rust-lang.org/nightly/book/ch13-01-closures.html)
- [threads](https://doc.rust-lang.org/nightly/book/ch16-01-threads.html#using-move-closures-with-threads)
+ [closure](https://doc.rust-lang.org/stable/book/ch13-01-closures.html)
+ [closures](https://doc.rust-lang.org/stable/book/ch13-01-closures.html)
+ [threads](https://doc.rust-lang.org/stable/book/ch16-01-threads.html#using-move-closures-with-threads)
<https://doc.rust-lang.org/nightly/book/ch13-01-closures.html>
- "##]],
+ "#]],
);
}
@@ -4288,7 +4863,7 @@ fn hover_builtin() {
check(
r#"
//- /main.rs crate:main deps:std
-cosnt _: &str$0 = ""; }
+const _: &str$0 = ""; }
//- /libstd.rs crate:std
/// Docs for prim_str
@@ -5009,7 +5584,7 @@ fn foo() {
fn hover_try_expr_res() {
check_hover_range(
r#"
-//- minicore:result
+//- minicore: try, from, result
struct FooError;
fn foo() -> Result<(), FooError> {
@@ -5023,7 +5598,7 @@ fn foo() -> Result<(), FooError> {
);
check_hover_range(
r#"
-//- minicore:result
+//- minicore: try, from, result
struct FooError;
struct BarError;
@@ -5044,6 +5619,7 @@ fn foo() -> Result<(), FooError> {
fn hover_try_expr() {
check_hover_range(
r#"
+//- minicore: try
struct NotResult<T, U>(T, U);
struct Short;
struct Looooong;
@@ -5061,6 +5637,7 @@ fn foo() -> NotResult<(), Looooong> {
);
check_hover_range(
r#"
+//- minicore: try
struct NotResult<T, U>(T, U);
struct Short;
struct Looooong;
@@ -5092,7 +5669,7 @@ fn foo() -> Option<()> {
"#,
expect![[r#"
```rust
- <Option<i32> as Try>::Output
+ i32
```"#]],
);
}
@@ -5312,7 +5889,7 @@ enum Enum {
```
```rust
- RecordV { field: u32 }
+ RecordV { field: u32 } // size = 4, align = 4
```
"#]],
);
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index ac477339e..292591674 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -5,7 +5,8 @@ use std::{
use either::Either;
use hir::{
- known, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, ModuleDefId, Semantics,
+ known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef,
+ ModuleDefId, Semantics,
};
use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase};
use itertools::Itertools;
@@ -13,21 +14,23 @@ use smallvec::{smallvec, SmallVec};
use stdx::never;
use syntax::{
ast::{self, AstNode},
- match_ast, NodeOrToken, SyntaxNode, TextRange,
+ match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize,
};
+use text_edit::TextEdit;
use crate::{navigation_target::TryToNav, FileId};
-mod closing_brace;
-mod implicit_static;
-mod fn_lifetime_fn;
-mod closure_ret;
mod adjustment;
-mod chaining;
-mod param_name;
-mod binding_mode;
mod bind_pat;
+mod binding_mode;
+mod chaining;
+mod closing_brace;
+mod closure_ret;
+mod closure_captures;
mod discriminant;
+mod fn_lifetime_fn;
+mod implicit_static;
+mod param_name;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InlayHintsConfig {
@@ -40,11 +43,13 @@ pub struct InlayHintsConfig {
pub adjustment_hints_mode: AdjustmentHintsMode,
pub adjustment_hints_hide_outside_unsafe: bool,
pub closure_return_type_hints: ClosureReturnTypeHints,
+ pub closure_capture_hints: bool,
pub binding_mode_hints: bool,
pub lifetime_elision_hints: LifetimeElisionHints,
pub param_names_for_lifetime_elision_hints: bool,
pub hide_named_constructor_hints: bool,
pub hide_closure_initialization_hints: bool,
+ pub closure_style: ClosureStyle,
pub max_length: Option<usize>,
pub closing_brace_hints_min_lines: Option<usize>,
}
@@ -87,38 +92,61 @@ pub enum AdjustmentHintsMode {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum InlayKind {
+ Adjustment,
BindingMode,
Chaining,
ClosingBrace,
- ClosureReturnType,
+ ClosureCapture,
+ Discriminant,
GenericParamList,
- Adjustment,
- AdjustmentPostfix,
Lifetime,
Parameter,
Type,
- Discriminant,
- OpeningParenthesis,
- ClosingParenthesis,
+}
+
+#[derive(Debug)]
+pub enum InlayHintPosition {
+ Before,
+ After,
}
#[derive(Debug)]
pub struct InlayHint {
/// The text range this inlay hint applies to.
pub range: TextRange,
- /// The kind of this inlay hint. This is used to determine side and padding of the hint for
- /// rendering purposes.
+ pub position: InlayHintPosition,
+ pub pad_left: bool,
+ pub pad_right: bool,
+ /// The kind of this inlay hint.
pub kind: InlayKind,
/// The actual label to show in the inlay hint.
pub label: InlayHintLabel,
+ /// Text edit to apply when "accepting" this inlay hint.
+ pub text_edit: Option<TextEdit>,
}
impl InlayHint {
- fn closing_paren(range: TextRange) -> InlayHint {
- InlayHint { range, kind: InlayKind::ClosingParenthesis, label: InlayHintLabel::from(")") }
+ fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint {
+ InlayHint {
+ range,
+ kind,
+ label: InlayHintLabel::from(")"),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ }
}
- fn opening_paren(range: TextRange) -> InlayHint {
- InlayHint { range, kind: InlayKind::OpeningParenthesis, label: InlayHintLabel::from("(") }
+ fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint {
+ InlayHint {
+ range,
+ kind,
+ label: InlayHintLabel::from("("),
+ text_edit: None,
+ position: InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: false,
+ }
}
}
@@ -283,14 +311,15 @@ impl InlayHintLabelBuilder<'_> {
fn label_of_ty(
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
- ty: hir::Type,
+ ty: &hir::Type,
) -> Option<InlayHintLabel> {
fn rec(
sema: &Semantics<'_, RootDatabase>,
famous_defs: &FamousDefs<'_, '_>,
mut max_length: Option<usize>,
- ty: hir::Type,
+ ty: &hir::Type,
label_builder: &mut InlayHintLabelBuilder<'_>,
+ config: &InlayHintsConfig,
) -> Result<(), HirDisplayError> {
let iter_item_type = hint_iterator(sema, famous_defs, &ty);
match iter_item_type {
@@ -321,11 +350,14 @@ fn label_of_ty(
label_builder.write_str(LABEL_ITEM)?;
label_builder.end_location_link();
label_builder.write_str(LABEL_MIDDLE2)?;
- rec(sema, famous_defs, max_length, ty, label_builder)?;
+ rec(sema, famous_defs, max_length, &ty, label_builder, config)?;
label_builder.write_str(LABEL_END)?;
Ok(())
}
- None => ty.display_truncated(sema.db, max_length).write_to(label_builder),
+ None => ty
+ .display_truncated(sema.db, max_length)
+ .with_closure_style(config.closure_style)
+ .write_to(label_builder),
}
}
@@ -335,11 +367,28 @@ fn label_of_ty(
location: None,
result: InlayHintLabel::default(),
};
- let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder);
+ let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config);
let r = label_builder.finish();
Some(r)
}
+fn ty_to_text_edit(
+ sema: &Semantics<'_, RootDatabase>,
+ node_for_hint: &SyntaxNode,
+ ty: &hir::Type,
+ offset_to_insert: TextSize,
+ prefix: String,
+) -> Option<TextEdit> {
+ let scope = sema.scope(node_for_hint)?;
+ // FIXME: Limit the length and bail out on excess somehow?
+ let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?;
+
+ let mut builder = TextEdit::builder();
+ builder.insert(offset_to_insert, prefix);
+ builder.insert(offset_to_insert, rendered);
+ Some(builder.finish())
+}
+
// Feature: Inlay Hints
//
// rust-analyzer shows additional information inline with the source code.
@@ -408,10 +457,10 @@ fn hints(
ast::Expr::MethodCallExpr(it) => {
param_name::hints(hints, sema, config, ast::Expr::from(it))
}
- ast::Expr::ClosureExpr(it) => closure_ret::hints(hints, famous_defs, config, file_id, it),
- // We could show reborrows for all expressions, but usually that is just noise to the user
- // and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
- // ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
+ ast::Expr::ClosureExpr(it) => {
+ closure_captures::hints(hints, famous_defs, config, file_id, it.clone());
+ closure_ret::hints(hints, famous_defs, config, file_id, it)
+ },
_ => None,
}
},
@@ -481,6 +530,7 @@ fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool {
#[cfg(test)]
mod tests {
use expect_test::Expect;
+ use hir::ClosureStyle;
use itertools::Itertools;
use test_utils::extract_annotations;
@@ -498,12 +548,14 @@ mod tests {
chaining_hints: false,
lifetime_elision_hints: LifetimeElisionHints::Never,
closure_return_type_hints: ClosureReturnTypeHints::Never,
+ closure_capture_hints: false,
adjustment_hints: AdjustmentHints::Never,
adjustment_hints_mode: AdjustmentHintsMode::Prefix,
adjustment_hints_hide_outside_unsafe: false,
binding_mode_hints: false,
hide_named_constructor_hints: false,
hide_closure_initialization_hints: false,
+ closure_style: ClosureStyle::ImplFn,
param_names_for_lifetime_elision_hints: false,
max_length: None,
closing_brace_hints_min_lines: None,
@@ -530,7 +582,8 @@ mod tests {
let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
let actual = inlay_hints
.into_iter()
- .map(|it| (it.range, it.label.to_string()))
+ // FIXME: We trim the start because some inlay produces leading whitespace which is not properly supported by our annotation extraction
+ .map(|it| (it.range, it.label.to_string().trim_start().to_owned()))
.sorted_by_key(|(range, _)| range.start())
.collect::<Vec<_>>();
expected.sort_by_key(|(range, _)| range.start());
@@ -545,6 +598,37 @@ mod tests {
expect.assert_debug_eq(&inlay_hints)
}
+ /// Computes inlay hints for the fixture, applies all the provided text edits and then runs
+ /// expect test.
+ #[track_caller]
+ pub(super) fn check_edit(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
+ let (analysis, file_id) = fixture::file(ra_fixture);
+ let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
+
+ let edits = inlay_hints
+ .into_iter()
+ .filter_map(|hint| hint.text_edit)
+ .reduce(|mut acc, next| {
+ acc.union(next).expect("merging text edits failed");
+ acc
+ })
+ .expect("no edit returned");
+
+ let mut actual = analysis.file_text(file_id).unwrap().to_string();
+ edits.apply(&mut actual);
+ expect.assert_eq(&actual);
+ }
+
+ #[track_caller]
+ pub(super) fn check_no_edit(config: InlayHintsConfig, ra_fixture: &str) {
+ let (analysis, file_id) = fixture::file(ra_fixture);
+ let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
+
+ let edits: Vec<_> = inlay_hints.into_iter().filter_map(|hint| hint.text_edit).collect();
+
+ assert!(edits.is_empty(), "unexpected edits: {edits:?}");
+ }
+
#[test]
fn hints_disabled() {
check_with_config(
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
index 46505b304..10bee2a6a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
@@ -3,7 +3,11 @@
//! let _: u32 = /* <never-to-any> */ loop {};
//! let _: &u32 = /* &* */ &mut 0;
//! ```
-use hir::{Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, PointerCast, Safety, Semantics};
+use either::Either;
+use hir::{
+ Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
+ Semantics,
+};
use ide_db::RootDatabase;
use stdx::never;
@@ -13,8 +17,8 @@ use syntax::{
};
use crate::{
- AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
- InlayTooltip,
+ AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintPosition,
+ InlayHintsConfig, InlayKind, InlayTooltip,
};
pub(super) fn hints(
@@ -60,22 +64,26 @@ pub(super) fn hints(
mode_and_needs_parens_for_adjustment_hints(expr, config.adjustment_hints_mode);
if needs_outer_parens {
- acc.push(InlayHint::opening_paren(expr.syntax().text_range()));
+ acc.push(InlayHint::opening_paren_before(
+ InlayKind::Adjustment,
+ expr.syntax().text_range(),
+ ));
}
if postfix && needs_inner_parens {
- acc.push(InlayHint::opening_paren(expr.syntax().text_range()));
- acc.push(InlayHint::closing_paren(expr.syntax().text_range()));
+ acc.push(InlayHint::opening_paren_before(
+ InlayKind::Adjustment,
+ expr.syntax().text_range(),
+ ));
+ acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
}
- let (mut tmp0, mut tmp1);
- let iter: &mut dyn Iterator<Item = _> = if postfix {
- tmp0 = adjustments.into_iter();
- &mut tmp0
+ let mut iter = if postfix {
+ Either::Left(adjustments.into_iter())
} else {
- tmp1 = adjustments.into_iter().rev();
- &mut tmp1
+ Either::Right(adjustments.into_iter().rev())
};
+ let iter: &mut dyn Iterator<Item = _> = iter.as_mut().either(|it| it as _, |it| it as _);
for Adjustment { source, target, kind } in iter {
if source == target {
@@ -88,7 +96,13 @@ pub(super) fn hints(
Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
("<never-to-any>", "never to any")
}
- Adjust::Deref(_) => ("*", "dereference"),
+ Adjust::Deref(None) => ("*", "dereference"),
+ Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => {
+ ("*", "`Deref` dereference")
+ }
+ Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => {
+ ("*", "`DerefMut` dereference")
+ }
Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => ("&", "borrow"),
Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => ("&mut ", "unique borrow"),
Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => {
@@ -125,7 +139,10 @@ pub(super) fn hints(
};
acc.push(InlayHint {
range: expr.syntax().text_range(),
- kind: if postfix { InlayKind::AdjustmentPostfix } else { InlayKind::Adjustment },
+ pad_left: false,
+ pad_right: false,
+ position: if postfix { InlayHintPosition::After } else { InlayHintPosition::Before },
+ kind: InlayKind::Adjustment,
label: InlayHintLabel::simple(
if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
Some(InlayTooltip::Markdown(format!(
@@ -135,19 +152,23 @@ pub(super) fn hints(
))),
None,
),
+ text_edit: None,
});
}
if !postfix && needs_inner_parens {
- acc.push(InlayHint::opening_paren(expr.syntax().text_range()));
- acc.push(InlayHint::closing_paren(expr.syntax().text_range()));
+ acc.push(InlayHint::opening_paren_before(
+ InlayKind::Adjustment,
+ expr.syntax().text_range(),
+ ));
+ acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
}
if needs_outer_parens {
- acc.push(InlayHint::closing_paren(expr.syntax().text_range()));
+ acc.push(InlayHint::closing_paren_after(InlayKind::Adjustment, expr.syntax().text_range()));
}
Some(())
}
-/// Returns whatever the hint should be postfix and if we need to add paretheses on the inside and/or outside of `expr`,
+/// Returns whatever the hint should be postfix and if we need to add parentheses on the inside and/or outside of `expr`,
/// if we are going to add (`postfix`) adjustments hints to it.
fn mode_and_needs_parens_for_adjustment_hints(
expr: &ast::Expr,
@@ -182,7 +203,7 @@ fn mode_and_needs_parens_for_adjustment_hints(
}
}
-/// Returns whatever we need to add paretheses on the inside and/or outside of `expr`,
+/// Returns whatever we need to add parentheses on the inside and/or outside of `expr`,
/// if we are going to add (`postfix`) adjustments hints to it.
fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, bool) {
// This is a very miserable pile of hacks...
@@ -193,10 +214,10 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool,
// But we want to check what would happen if we add `*`/`.*` to the inner expression.
// To check for inner we need `` expr.needs_parens_in(`*expr`) ``,
// to check for outer we need `` `*expr`.needs_parens_in(parent) ``,
- // where "expr" is the `expr` parameter, `*expr` is the editted `expr`,
+ // where "expr" is the `expr` parameter, `*expr` is the edited `expr`,
// and "parent" is the parent of the original expression...
//
- // For this we utilize mutable mutable trees, which is a HACK, but it works.
+ // For this we utilize mutable trees, which is a HACK, but it works.
//
// FIXME: comeup with a better API for `needs_parens_in`, so that we don't have to do *this*
@@ -242,7 +263,7 @@ fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool,
};
// At this point
- // - `parent` is the parrent of the original expression
+ // - `parent` is the parent of the original expression
// - `dummy_expr` is the original expression wrapped in the operator we want (`*`/`.*`)
// - `expr` is the clone of the original expression (with `dummy_expr` as the parent)
@@ -264,7 +285,7 @@ mod tests {
check_with_config(
InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
r#"
-//- minicore: coerce_unsized, fn, eq
+//- minicore: coerce_unsized, fn, eq, index
fn main() {
let _: u32 = loop {};
//^^^^^^^<never-to-any>
@@ -315,6 +336,8 @@ fn main() {
(&Struct).consume();
//^^^^^^^*
(&Struct).by_ref();
+ //^^^^^^^&
+ //^^^^^^^*
(&mut Struct).consume();
//^^^^^^^^^^^*
@@ -322,6 +345,8 @@ fn main() {
//^^^^^^^^^^^&
//^^^^^^^^^^^*
(&mut Struct).by_ref_mut();
+ //^^^^^^^^^^^&mut $
+ //^^^^^^^^^^^*
// Check that block-like expressions don't duplicate hints
let _: &mut [u32] = (&mut []);
@@ -360,6 +385,19 @@ fn main() {
(()) == {()};
// ^^&
// ^^^^&
+ let closure: dyn Fn = || ();
+ closure();
+ //^^^^^^^(
+ //^^^^^^^&
+ //^^^^^^^)
+ Struct[0];
+ //^^^^^^(
+ //^^^^^^&
+ //^^^^^^)
+ &mut Struct[0];
+ //^^^^^^(
+ //^^^^^^&mut $
+ //^^^^^^)
}
#[derive(Copy, Clone)]
@@ -369,8 +407,13 @@ impl Struct {
fn by_ref(&self) {}
fn by_ref_mut(&mut self) {}
}
+struct StructMut;
+impl core::ops::Index<usize> for Struct {
+ type Output = ();
+}
+impl core::ops::IndexMut for Struct {}
"#,
- )
+ );
}
#[test]
@@ -382,7 +425,7 @@ impl Struct {
..DISABLED_CONFIG
},
r#"
-//- minicore: coerce_unsized, fn, eq
+//- minicore: coerce_unsized, fn, eq, index
fn main() {
Struct.consume();
@@ -396,6 +439,10 @@ fn main() {
//^^^^^^^)
//^^^^^^^.*
(&Struct).by_ref();
+ //^^^^^^^(
+ //^^^^^^^)
+ //^^^^^^^.*
+ //^^^^^^^.&
(&mut Struct).consume();
//^^^^^^^^^^^(
@@ -407,6 +454,10 @@ fn main() {
//^^^^^^^^^^^.*
//^^^^^^^^^^^.&
(&mut Struct).by_ref_mut();
+ //^^^^^^^^^^^(
+ //^^^^^^^^^^^)
+ //^^^^^^^^^^^.*
+ //^^^^^^^^^^^.&mut
// Check that block-like expressions don't duplicate hints
let _: &mut [u32] = (&mut []);
@@ -457,6 +508,13 @@ fn main() {
(()) == {()};
// ^^.&
// ^^^^.&
+ let closure: dyn Fn = || ();
+ closure();
+ //^^^^^^^.&
+ Struct[0];
+ //^^^^^^.&
+ &mut Struct[0];
+ //^^^^^^.&mut
}
#[derive(Copy, Clone)]
@@ -466,6 +524,11 @@ impl Struct {
fn by_ref(&self) {}
fn by_ref_mut(&mut self) {}
}
+struct StructMut;
+impl core::ops::Index<usize> for Struct {
+ type Output = ();
+}
+impl core::ops::IndexMut for Struct {}
"#,
);
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
index 6a5092733..07b9f9cc1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
@@ -3,7 +3,7 @@
//! fn f(a: i32, b: i32) -> i32 { a + b }
//! let _x /* i32 */= f(4, 4);
//! ```
-use hir::{Semantics, TypeInfo};
+use hir::Semantics;
use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase};
use itertools::Itertools;
@@ -12,9 +12,10 @@ use syntax::{
match_ast,
};
-use crate::{inlay_hints::closure_has_block_body, InlayHint, InlayHintsConfig, InlayKind};
-
-use super::label_of_ty;
+use crate::{
+ inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit},
+ InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind,
+};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -27,15 +28,45 @@ pub(super) fn hints(
return None;
}
+ let parent = pat.syntax().parent()?;
+ let type_ascriptable = match_ast! {
+ match parent {
+ ast::Param(it) => {
+ if it.ty().is_some() {
+ return None;
+ }
+ Some(it.colon_token())
+ },
+ ast::LetStmt(it) => {
+ if config.hide_closure_initialization_hints {
+ if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() {
+ if closure_has_block_body(&closure) {
+ return None;
+ }
+ }
+ }
+ if it.ty().is_some() {
+ return None;
+ }
+ Some(it.colon_token())
+ },
+ _ => None
+ }
+ };
+
let descended = sema.descend_node_into_attributes(pat.clone()).pop();
let desc_pat = descended.as_ref().unwrap_or(pat);
- let ty = sema.type_of_pat(&desc_pat.clone().into())?.original;
+ let ty = sema.type_of_binding_in_pat(desc_pat)?;
- if should_not_display_type_hint(sema, config, pat, &ty) {
+ if ty.is_unknown() {
return None;
}
- let label = label_of_ty(famous_defs, config, ty)?;
+ if sema.resolve_bind_pat_to_const(pat).is_some() {
+ return None;
+ }
+
+ let mut label = label_of_ty(famous_defs, config, &ty)?;
if config.hide_named_constructor_hints
&& is_named_constructor(sema, pat, &label.to_string()).is_some()
@@ -43,69 +74,46 @@ pub(super) fn hints(
return None;
}
+ let text_edit = if let Some(colon_token) = &type_ascriptable {
+ ty_to_text_edit(
+ sema,
+ desc_pat.syntax(),
+ &ty,
+ colon_token
+ .as_ref()
+ .map_or_else(|| pat.syntax().text_range(), |t| t.text_range())
+ .end(),
+ if colon_token.is_some() { String::new() } else { String::from(": ") },
+ )
+ } else {
+ None
+ };
+
+ let render_colons = config.render_colons && !matches!(type_ascriptable, Some(Some(_)));
+ if render_colons {
+ label.prepend_str(": ");
+ }
+
+ let text_range = match pat.name() {
+ Some(name) => name.syntax().text_range(),
+ None => pat.syntax().text_range(),
+ };
acc.push(InlayHint {
- range: match pat.name() {
- Some(name) => name.syntax().text_range(),
- None => pat.syntax().text_range(),
+ range: match type_ascriptable {
+ Some(Some(t)) => text_range.cover(t.text_range()),
+ _ => text_range,
},
kind: InlayKind::Type,
label,
+ text_edit,
+ position: InlayHintPosition::After,
+ pad_left: !render_colons,
+ pad_right: false,
});
Some(())
}
-fn should_not_display_type_hint(
- sema: &Semantics<'_, RootDatabase>,
- config: &InlayHintsConfig,
- bind_pat: &ast::IdentPat,
- pat_ty: &hir::Type,
-) -> bool {
- let db = sema.db;
-
- if pat_ty.is_unknown() {
- return true;
- }
-
- if sema.resolve_bind_pat_to_const(bind_pat).is_some() {
- return true;
- }
-
- for node in bind_pat.syntax().ancestors() {
- match_ast! {
- match node {
- ast::LetStmt(it) => {
- if config.hide_closure_initialization_hints {
- if let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() {
- if closure_has_block_body(&closure) {
- return true;
- }
- }
- }
- return it.ty().is_some()
- },
- // FIXME: We might wanna show type hints in parameters for non-top level patterns as well
- ast::Param(it) => return it.ty().is_some(),
- ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
- ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
- ast::IfExpr(_) => return false,
- ast::WhileExpr(_) => return false,
- ast::ForExpr(it) => {
- // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
- // Type of expr should be iterable.
- return it.in_token().is_none() ||
- it.iterable()
- .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr))
- .map(TypeInfo::original)
- .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
- },
- _ => (),
- }
- }
- }
- false
-}
-
fn is_named_constructor(
sema: &Semantics<'_, RootDatabase>,
pat: &ast::IdentPat,
@@ -159,30 +167,20 @@ fn is_named_constructor(
(ctor_name == ty_name).then_some(())
}
-fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool {
- if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() {
- let pat_text = bind_pat.to_string();
- enum_data
- .variants(db)
- .into_iter()
- .map(|variant| variant.name(db).to_smol_str())
- .any(|enum_name| enum_name == pat_text)
- } else {
- false
- }
-}
-
#[cfg(test)]
mod tests {
// This module also contains tests for super::closure_ret
+ use expect_test::expect;
+ use hir::ClosureStyle;
use syntax::{TextRange, TextSize};
use test_utils::extract_annotations;
- use crate::{fixture, inlay_hints::InlayHintsConfig};
+ use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints};
- use crate::inlay_hints::tests::{check, check_with_config, DISABLED_CONFIG, TEST_CONFIG};
- use crate::ClosureReturnTypeHints;
+ use crate::inlay_hints::tests::{
+ check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
+ };
#[track_caller]
fn check_types(ra_fixture: &str) {
@@ -235,7 +233,7 @@ fn main() {
let zz_ref = &zz;
//^^^^^^ &Test<i32>
let test = || zz;
- //^^^^ || -> Test<i32>
+ //^^^^ impl FnOnce() -> Test<i32>
}"#,
);
}
@@ -528,24 +526,7 @@ fn main() {
struct Test { a: Option<u32>, b: u8 }
fn main() {
- let test = Some(Test { a: Some(3), b: 1 });
- //^^^^ Option<Test>
- if let None = &test {};
- if let test = &test {};
- //^^^^ &Option<Test>
- if let Some(test) = &test {};
- //^^^^ &Test
- if let Some(Test { a, b }) = &test {};
- //^ &Option<u32> ^ &u8
- if let Some(Test { a: x, b: y }) = &test {};
- //^ &Option<u32> ^ &u8
- if let Some(Test { a: Some(x), b: y }) = &test {};
- //^ &u32 ^ &u8
- if let Some(Test { a: None, b: y }) = &test {};
- //^ &u8
- if let Some(Test { b: y, .. }) = &test {};
- //^ &u8
- if test == None {}
+
}"#,
);
}
@@ -560,8 +541,8 @@ struct Test { a: Option<u32>, b: u8 }
fn main() {
let test = Some(Test { a: Some(3), b: 1 });
//^^^^ Option<Test>
- while let Some(Test { a: Some(x), b: y }) = &test {};
- //^ &u32 ^ &u8
+ while let Some(Test { a: Some(x), b: y }) = &test {};
+ //^ &u32 ^ &u8
}"#,
);
}
@@ -753,7 +734,7 @@ fn main() {
let func = times2;
// ^^^^ fn times2(i32) -> i32
let closure = |x: i32| x * 2;
- // ^^^^^^^ |i32| -> i32
+ // ^^^^^^^ impl Fn(i32) -> i32
}
fn fallible() -> ControlFlow<()> {
@@ -811,49 +792,90 @@ fn fallible() -> ControlFlow<()> {
}
#[test]
- fn closures() {
- check(
+ fn closure_style() {
+ check_with_config(
+ InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
r#"
+//- minicore: fn
fn main() {
- let mut start = 0;
- //^^^^^ i32
- (0..2).for_each(|increment | { start += increment; });
- //^^^^^^^^^ i32
-
- let multiply =
- //^^^^^^^^ |i32, i32| -> i32
- | a, b| a * b
- //^ i32 ^ i32
-
- ;
-
- let _: i32 = multiply(1, 2);
- //^ a ^ b
- let multiply_ref = &multiply;
- //^^^^^^^^^^^^ &|i32, i32| -> i32
-
- let return_42 = || 42;
- //^^^^^^^^^ || -> i32
- || { 42 };
- //^^ i32
-}"#,
+ let x = || 2;
+ //^ impl Fn() -> i32
+ let y = |t: i32| x() + t;
+ //^ impl Fn(i32) -> i32
+ let mut t = 5;
+ //^ i32
+ let z = |k: i32| { t += k; };
+ //^ impl FnMut(i32)
+ let p = (y, z);
+ //^ (impl Fn(i32) -> i32, impl FnMut(i32))
+}
+ "#,
);
- }
-
- #[test]
- fn return_type_hints_for_closure_without_block() {
check_with_config(
InlayHintsConfig {
- closure_return_type_hints: ClosureReturnTypeHints::Always,
+ type_hints: true,
+ closure_style: ClosureStyle::RANotation,
..DISABLED_CONFIG
},
r#"
+//- minicore: fn
fn main() {
- let a = || { 0 };
- //^^ i32
- let b = || 0;
- //^^ i32
-}"#,
+ let x = || 2;
+ //^ || -> i32
+ let y = |t: i32| x() + t;
+ //^ |i32| -> i32
+ let mut t = 5;
+ //^ i32
+ let z = |k: i32| { t += k; };
+ //^ |i32| -> ()
+ let p = (y, z);
+ //^ (|i32| -> i32, |i32| -> ())
+}
+ "#,
+ );
+ check_with_config(
+ InlayHintsConfig {
+ type_hints: true,
+ closure_style: ClosureStyle::ClosureWithId,
+ ..DISABLED_CONFIG
+ },
+ r#"
+//- minicore: fn
+fn main() {
+ let x = || 2;
+ //^ {closure#0}
+ let y = |t: i32| x() + t;
+ //^ {closure#1}
+ let mut t = 5;
+ //^ i32
+ let z = |k: i32| { t += k; };
+ //^ {closure#2}
+ let p = (y, z);
+ //^ ({closure#1}, {closure#2})
+}
+ "#,
+ );
+ check_with_config(
+ InlayHintsConfig {
+ type_hints: true,
+ closure_style: ClosureStyle::Hide,
+ ..DISABLED_CONFIG
+ },
+ r#"
+//- minicore: fn
+fn main() {
+ let x = || 2;
+ //^ …
+ let y = |t: i32| x() + t;
+ //^ …
+ let mut t = 5;
+ //^ i32
+ let z = |k: i32| { t += k; };
+ //^ …
+ let p = (y, z);
+ //^ (…, …)
+}
+ "#,
);
}
@@ -871,13 +893,13 @@ fn main() {
let multiple_2 = |x: i32| { x * 2 };
let multiple_2 = |x: i32| x * 2;
- // ^^^^^^^^^^ |i32| -> i32
+ // ^^^^^^^^^^ impl Fn(i32) -> i32
let (not) = (|x: bool| { !x });
- // ^^^ |bool| -> bool
+ // ^^^ impl Fn(bool) -> bool
let (is_zero, _b) = (|x: usize| { x == 0 }, false);
- // ^^^^^^^ |usize| -> bool
+ // ^^^^^^^ impl Fn(usize) -> bool
// ^^ bool
let plus_one = |x| { x + 1 };
@@ -923,4 +945,160 @@ fn main() {
}"#,
);
}
+
+ #[test]
+ fn edit_for_let_stmt() {
+ check_edit(
+ TEST_CONFIG,
+ r#"
+struct S<T>(T);
+fn test<F>(v: S<(S<i32>, S<()>)>, f: F) {
+ let a = v;
+ let S((b, c)) = v;
+ let a @ S((b, c)) = v;
+ let a = f;
+}
+"#,
+ expect![[r#"
+ struct S<T>(T);
+ fn test<F>(v: S<(S<i32>, S<()>)>, f: F) {
+ let a: S<(S<i32>, S<()>)> = v;
+ let S((b, c)) = v;
+ let a @ S((b, c)): S<(S<i32>, S<()>)> = v;
+ let a: F = f;
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn edit_for_closure_param() {
+ check_edit(
+ TEST_CONFIG,
+ r#"
+fn test<T>(t: T) {
+ let f = |a, b, c| {};
+ let result = f(42, "", t);
+}
+"#,
+ expect![[r#"
+ fn test<T>(t: T) {
+ let f = |a: i32, b: &str, c: T| {};
+ let result: () = f(42, "", t);
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn edit_for_closure_ret() {
+ check_edit(
+ TEST_CONFIG,
+ r#"
+struct S<T>(T);
+fn test() {
+ let f = || { 3 };
+ let f = |a: S<usize>| { S(a) };
+}
+"#,
+ expect![[r#"
+ struct S<T>(T);
+ fn test() {
+ let f = || -> i32 { 3 };
+ let f = |a: S<usize>| -> S<S<usize>> { S(a) };
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn edit_prefixes_paths() {
+ check_edit(
+ TEST_CONFIG,
+ r#"
+pub struct S<T>(T);
+mod middle {
+ pub struct S<T, U>(T, U);
+ pub fn make() -> S<inner::S<i64>, super::S<usize>> { loop {} }
+
+ mod inner {
+ pub struct S<T>(T);
+ }
+
+ fn test() {
+ let a = make();
+ }
+}
+"#,
+ expect![[r#"
+ pub struct S<T>(T);
+ mod middle {
+ pub struct S<T, U>(T, U);
+ pub fn make() -> S<inner::S<i64>, super::S<usize>> { loop {} }
+
+ mod inner {
+ pub struct S<T>(T);
+ }
+
+ fn test() {
+ let a: S<inner::S<i64>, crate::S<usize>> = make();
+ }
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn no_edit_for_top_pat_where_type_annotation_is_invalid() {
+ check_no_edit(
+ TEST_CONFIG,
+ r#"
+fn test() {
+ if let a = 42 {}
+ while let a = 42 {}
+ match 42 {
+ a => (),
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
+ fn no_edit_for_opaque_type() {
+ check_no_edit(
+ TEST_CONFIG,
+ r#"
+trait Trait {}
+struct S<T>(T);
+fn foo() -> impl Trait {}
+fn bar() -> S<impl Trait> {}
+fn test() {
+ let a = foo();
+ let a = bar();
+ let f = || { foo() };
+ let f = || { bar() };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_edit_for_closure_return_without_body_block() {
+ // We can lift this limitation; see FIXME in closure_ret module.
+ let config = InlayHintsConfig {
+ closure_return_type_hints: ClosureReturnTypeHints::Always,
+ ..TEST_CONFIG
+ };
+ check_no_edit(
+ config,
+ r#"
+struct S<T>(T);
+fn test() {
+ let f = || 3;
+ let f = |a: S<usize>| S(a);
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
index 5d9729263..343cf17e5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
@@ -7,7 +7,7 @@ use ide_db::RootDatabase;
use syntax::ast::{self, AstNode};
-use crate::{InlayHint, InlayHintsConfig, InlayKind};
+use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -49,7 +49,15 @@ pub(super) fn hints(
(true, false) => "&",
_ => return,
};
- acc.push(InlayHint { range, kind: InlayKind::BindingMode, label: r.to_string().into() });
+ acc.push(InlayHint {
+ range,
+ kind: InlayKind::BindingMode,
+ label: r.to_string().into(),
+ text_edit: None,
+ position: InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: mut_reference,
+ });
});
match pat {
ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
@@ -63,11 +71,21 @@ pub(super) fn hints(
range: pat.syntax().text_range(),
kind: InlayKind::BindingMode,
label: bm.to_string().into(),
+ text_edit: None,
+ position: InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: true,
});
}
ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => {
- acc.push(InlayHint::opening_paren(pat.syntax().text_range()));
- acc.push(InlayHint::closing_paren(pat.syntax().text_range()));
+ acc.push(InlayHint::opening_paren_before(
+ InlayKind::BindingMode,
+ pat.syntax().text_range(),
+ ));
+ acc.push(InlayHint::closing_paren_after(
+ InlayKind::BindingMode,
+ pat.syntax().text_range(),
+ ));
}
_ => (),
}
@@ -142,7 +160,6 @@ struct Struct {
field: &'static str,
}
fn foo(s @ Struct { field, .. }: &Struct) {}
- //^^^^^^^^^^^^^^^^^^^^^^^^ref
//^^^^^^^^^^^^^^^^^^^^&
//^^^^^ref
"#,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
index 1e1771259..84eac16b9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
@@ -5,7 +5,7 @@ use syntax::{
Direction, NodeOrToken, SyntaxKind, T,
};
-use crate::{FileId, InlayHint, InlayHintsConfig, InlayKind};
+use crate::{FileId, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind};
use super::label_of_ty;
@@ -60,7 +60,11 @@ pub(super) fn hints(
acc.push(InlayHint {
range: expr.syntax().text_range(),
kind: InlayKind::Chaining,
- label: label_of_ty(famous_defs, config, ty)?,
+ label: label_of_ty(famous_defs, config, &ty)?,
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: true,
+ pad_right: false,
});
}
}
@@ -103,6 +107,9 @@ fn main() {
[
InlayHint {
range: 147..172,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -120,9 +127,13 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 147..154,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -140,6 +151,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
]
"#]],
@@ -188,6 +200,9 @@ fn main() {
[
InlayHint {
range: 143..190,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -205,9 +220,13 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 143..179,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -225,6 +244,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
]
"#]],
@@ -257,6 +277,9 @@ fn main() {
[
InlayHint {
range: 143..190,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -274,9 +297,13 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 143..179,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -294,6 +321,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
]
"#]],
@@ -327,6 +355,9 @@ fn main() {
[
InlayHint {
range: 246..283,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -357,9 +388,13 @@ fn main() {
},
"<i32, bool>>",
],
+ text_edit: None,
},
InlayHint {
range: 246..265,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -390,6 +425,7 @@ fn main() {
},
"<i32, bool>>",
],
+ text_edit: None,
},
]
"#]],
@@ -425,6 +461,9 @@ fn main() {
[
InlayHint {
range: 174..241,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"impl ",
@@ -435,7 +474,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 3415..3423,
+ range: 9287..9295,
},
),
tooltip: "",
@@ -448,16 +487,20 @@ fn main() {
file_id: FileId(
1,
),
- range: 3447..3451,
+ range: 9319..9323,
},
),
tooltip: "",
},
" = ()>",
],
+ text_edit: None,
},
InlayHint {
range: 174..224,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"impl ",
@@ -468,7 +511,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 3415..3423,
+ range: 9287..9295,
},
),
tooltip: "",
@@ -481,16 +524,20 @@ fn main() {
file_id: FileId(
1,
),
- range: 3447..3451,
+ range: 9319..9323,
},
),
tooltip: "",
},
" = ()>",
],
+ text_edit: None,
},
InlayHint {
range: 174..206,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"impl ",
@@ -501,7 +548,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 3415..3423,
+ range: 9287..9295,
},
),
tooltip: "",
@@ -514,16 +561,20 @@ fn main() {
file_id: FileId(
1,
),
- range: 3447..3451,
+ range: 9319..9323,
},
),
tooltip: "",
},
" = ()>",
],
+ text_edit: None,
},
InlayHint {
range: 174..189,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"&mut ",
@@ -541,6 +592,7 @@ fn main() {
},
"",
],
+ text_edit: None,
},
]
"#]],
@@ -573,6 +625,9 @@ fn main() {
[
InlayHint {
range: 124..130,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Type,
label: [
"",
@@ -590,9 +645,22 @@ fn main() {
},
"",
],
+ text_edit: Some(
+ TextEdit {
+ indels: [
+ Indel {
+ insert: ": Struct",
+ delete: 130..130,
+ },
+ ],
+ },
+ ),
},
InlayHint {
range: 145..185,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -610,9 +678,13 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 145..168,
+ position: After,
+ pad_left: true,
+ pad_right: false,
kind: Chaining,
label: [
"",
@@ -630,9 +702,13 @@ fn main() {
},
"",
],
+ text_edit: None,
},
InlayHint {
range: 222..228,
+ position: Before,
+ pad_left: false,
+ pad_right: true,
kind: Parameter,
label: [
InlayHintLabelPart {
@@ -648,6 +724,7 @@ fn main() {
tooltip: "",
},
],
+ text_edit: None,
},
]
"#]],
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
index 14c11be54..2cefd5acd 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
@@ -10,7 +10,7 @@ use syntax::{
match_ast, SyntaxKind, SyntaxNode, T,
};
-use crate::{FileId, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind};
+use crate::{FileId, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -35,7 +35,7 @@ pub(super) fn hints(
let ty = imp.self_ty(sema.db);
let trait_ = imp.trait_(sema.db);
let hint_text = match trait_ {
- Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)),
+ Some(tr) => format!("impl {} for {}", tr.name(sema.db).display(sema.db), ty.display_truncated(sema.db, config.max_length)),
None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)),
};
(hint_text, None)
@@ -112,6 +112,10 @@ pub(super) fn hints(
range: closing_token.text_range(),
kind: InlayKind::ClosingBrace,
label: InlayHintLabel::simple(label, None, linked_location),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: true,
+ pad_right: false,
});
None
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
new file mode 100644
index 000000000..9d5defcbb
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
@@ -0,0 +1,207 @@
+//! Implementation of "closure return type" inlay hints.
+//!
+//! Tests live in [`bind_pat`][super::bind_pat] module.
+use ide_db::{base_db::FileId, famous_defs::FamousDefs};
+use syntax::ast::{self, AstNode};
+use text_edit::{TextRange, TextSize};
+
+use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
+
+pub(super) fn hints(
+ acc: &mut Vec<InlayHint>,
+ FamousDefs(sema, _): &FamousDefs<'_, '_>,
+ config: &InlayHintsConfig,
+ _file_id: FileId,
+ closure: ast::ClosureExpr,
+) -> Option<()> {
+ if !config.closure_capture_hints {
+ return None;
+ }
+ let ty = &sema.type_of_expr(&closure.clone().into())?.original;
+ let c = ty.as_closure()?;
+ let captures = c.captured_items(sema.db);
+
+ if captures.is_empty() {
+ return None;
+ }
+
+ let move_kw_range = match closure.move_token() {
+ Some(t) => t.text_range(),
+ None => {
+ let range = closure.syntax().first_token()?.prev_token()?.text_range();
+ let range = TextRange::new(range.end() - TextSize::from(1), range.end());
+ acc.push(InlayHint {
+ range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::simple("move", None, None),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ });
+ range
+ }
+ };
+ acc.push(InlayHint {
+ range: move_kw_range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::from("("),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ });
+ let last = captures.len() - 1;
+ for (idx, capture) in captures.into_iter().enumerate() {
+ let local = capture.local();
+ let source = local.primary_source(sema.db);
+
+ // force cache the source file, otherwise sema lookup will potentially panic
+ _ = sema.parse_or_expand(source.file());
+
+ acc.push(InlayHint {
+ range: move_kw_range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::simple(
+ format!(
+ "{}{}",
+ match capture.kind() {
+ hir::CaptureKind::SharedRef => "&",
+ hir::CaptureKind::UniqueSharedRef => "&unique ",
+ hir::CaptureKind::MutableRef => "&mut ",
+ hir::CaptureKind::Move => "",
+ },
+ capture.display_place(sema.db)
+ ),
+ None,
+ source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)),
+ ),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ });
+
+ if idx != last {
+ acc.push(InlayHint {
+ range: move_kw_range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::simple(", ", None, None),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
+ });
+ }
+ }
+ acc.push(InlayHint {
+ range: move_kw_range,
+ kind: InlayKind::ClosureCapture,
+ label: InlayHintLabel::from(")"),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: true,
+ });
+
+ Some(())
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{
+ inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
+ InlayHintsConfig,
+ };
+
+ #[test]
+ fn all_capture_kinds() {
+ check_with_config(
+ InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
+ r#"
+//- minicore: copy, derive
+
+
+#[derive(Copy, Clone)]
+struct Copy;
+
+struct NonCopy;
+
+fn main() {
+ let foo = Copy;
+ let bar = NonCopy;
+ let mut baz = NonCopy;
+ let qux = &mut NonCopy;
+ || {
+// ^ move
+// ^ (
+// ^ &foo
+// ^ , $
+// ^ bar
+// ^ , $
+// ^ baz
+// ^ , $
+// ^ qux
+// ^ )
+ foo;
+ bar;
+ baz;
+ qux;
+ };
+ || {
+// ^ move
+// ^ (
+// ^ &foo
+// ^ , $
+// ^ &bar
+// ^ , $
+// ^ &baz
+// ^ , $
+// ^ &qux
+// ^ )
+ &foo;
+ &bar;
+ &baz;
+ &qux;
+ };
+ || {
+// ^ move
+// ^ (
+// ^ &mut baz
+// ^ )
+ &mut baz;
+ };
+ || {
+// ^ move
+// ^ (
+// ^ &mut baz
+// ^ , $
+// ^ &mut *qux
+// ^ )
+ baz = NonCopy;
+ *qux = NonCopy;
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn move_token() {
+ check_with_config(
+ InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
+ r#"
+//- minicore: copy, derive
+fn main() {
+ let foo = u32;
+ move || {
+// ^^^^ (
+// ^^^^ foo
+// ^^^^ )
+ foo;
+ };
+}
+"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
index f03a18b8e..3b41db0f1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
@@ -1,14 +1,14 @@
//! Implementation of "closure return type" inlay hints.
+//!
+//! Tests live in [`bind_pat`][super::bind_pat] module.
use ide_db::{base_db::FileId, famous_defs::FamousDefs};
use syntax::ast::{self, AstNode};
use crate::{
- inlay_hints::closure_has_block_body, ClosureReturnTypeHints, InlayHint, InlayHintsConfig,
- InlayKind,
+ inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit},
+ ClosureReturnTypeHints, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind,
};
-use super::label_of_ty;
-
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
@@ -20,29 +20,81 @@ pub(super) fn hints(
return None;
}
- if closure.ret_type().is_some() {
- return None;
- }
+ let ret_type = closure.ret_type().map(|rt| (rt.thin_arrow_token(), rt.ty().is_some()));
+ let arrow = match ret_type {
+ Some((_, true)) => return None,
+ Some((arrow, _)) => arrow,
+ None => None,
+ };
- if !closure_has_block_body(&closure)
- && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock
- {
+ let has_block_body = closure_has_block_body(&closure);
+ if !has_block_body && config.closure_return_type_hints == ClosureReturnTypeHints::WithBlock {
return None;
}
let param_list = closure.param_list()?;
let closure = sema.descend_node_into_attributes(closure).pop()?;
- let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure))?.adjusted();
+ let ty = sema.type_of_expr(&ast::Expr::ClosureExpr(closure.clone()))?.adjusted();
let callable = ty.as_callable(sema.db)?;
let ty = callable.return_type();
- if ty.is_unit() {
+ if arrow.is_none() && ty.is_unit() {
return None;
}
+
+ let mut label = label_of_ty(famous_defs, config, &ty)?;
+
+ if arrow.is_none() {
+ label.prepend_str(" -> ");
+ }
+ // FIXME?: We could provide text edit to insert braces for closures with non-block body.
+ let text_edit = if has_block_body {
+ ty_to_text_edit(
+ sema,
+ closure.syntax(),
+ &ty,
+ arrow
+ .as_ref()
+ .map_or_else(|| param_list.syntax().text_range(), |t| t.text_range())
+ .end(),
+ if arrow.is_none() { String::from(" -> ") } else { String::new() },
+ )
+ } else {
+ None
+ };
+
acc.push(InlayHint {
range: param_list.syntax().text_range(),
- kind: InlayKind::ClosureReturnType,
- label: label_of_ty(famous_defs, config, ty)?,
+ kind: InlayKind::Type,
+ label,
+ text_edit,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
});
Some(())
}
+
+#[cfg(test)]
+mod tests {
+ use crate::inlay_hints::tests::{check_with_config, DISABLED_CONFIG};
+
+ use super::*;
+
+ #[test]
+ fn return_type_hints_for_closure_without_block() {
+ check_with_config(
+ InlayHintsConfig {
+ closure_return_type_hints: ClosureReturnTypeHints::Always,
+ ..DISABLED_CONFIG
+ },
+ r#"
+fn main() {
+ let a = || { 0 };
+ //^^ -> i32
+ let b = || 0;
+ //^^ -> i32
+}"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
index 67eaa553a..c4d2ac75c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
@@ -9,7 +9,8 @@ use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase};
use syntax::ast::{self, AstNode, HasName};
use crate::{
- DiscriminantHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, InlayTooltip,
+ DiscriminantHints, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind,
+ InlayTooltip,
};
pub(super) fn enum_hints(
@@ -19,21 +20,23 @@ pub(super) fn enum_hints(
_: FileId,
enum_: ast::Enum,
) -> Option<()> {
- let enabled = match config.discriminant_hints {
- DiscriminantHints::Always => true,
- DiscriminantHints::Fieldless => {
- !sema.to_def(&enum_)?.is_data_carrying(sema.db)
- || enum_.variant_list()?.variants().any(|v| v.expr().is_some())
- }
- DiscriminantHints::Never => false,
- };
- if !enabled {
+ if let DiscriminantHints::Never = config.discriminant_hints {
+ return None;
+ }
+
+ let def = sema.to_def(&enum_)?;
+ let data_carrying = def.is_data_carrying(sema.db);
+ if matches!(config.discriminant_hints, DiscriminantHints::Fieldless) && data_carrying {
+ return None;
+ }
+ // data carrying enums without a primitive repr have no stable discriminants
+ if data_carrying && def.repr(sema.db).map_or(true, |r| r.int.is_none()) {
return None;
}
for variant in enum_.variant_list()?.variants() {
variant_hints(acc, sema, &variant);
}
- None
+ Some(())
}
fn variant_hints(
@@ -41,10 +44,11 @@ fn variant_hints(
sema: &Semantics<'_, RootDatabase>,
variant: &ast::Variant,
) -> Option<()> {
- if variant.eq_token().is_some() {
+ if variant.expr().is_some() {
return None;
}
+ let eq_token = variant.eq_token();
let name = variant.name()?;
let descended = sema.descend_node_into_attributes(variant.clone()).pop();
@@ -52,34 +56,43 @@ fn variant_hints(
let v = sema.to_def(desc_pat)?;
let d = v.eval(sema.db);
+ let range = match variant.field_list() {
+ Some(field_list) => name.syntax().text_range().cover(field_list.syntax().text_range()),
+ None => name.syntax().text_range(),
+ };
+ let eq_ = if eq_token.is_none() { " =" } else { "" };
+ let label = InlayHintLabel::simple(
+ match d {
+ Ok(x) => {
+ if x >= 10 {
+ format!("{eq_} {x} ({x:#X})")
+ } else {
+ format!("{eq_} {x}")
+ }
+ }
+ Err(_) => format!("{eq_} ?"),
+ },
+ Some(InlayTooltip::String(match &d {
+ Ok(_) => "enum variant discriminant".into(),
+ Err(e) => format!("{e:?}").into(),
+ })),
+ None,
+ );
acc.push(InlayHint {
- range: match variant.field_list() {
- Some(field_list) => name.syntax().text_range().cover(field_list.syntax().text_range()),
- None => name.syntax().text_range(),
+ range: match eq_token {
+ Some(t) => range.cover(t.text_range()),
+ _ => range,
},
kind: InlayKind::Discriminant,
- label: InlayHintLabel::simple(
- match d {
- Ok(x) => {
- if x >= 10 {
- format!("{x} ({x:#X})")
- } else {
- format!("{x}")
- }
- }
- Err(_) => "?".into(),
- },
- Some(InlayTooltip::String(match &d {
- Ok(_) => "enum variant discriminant".into(),
- Err(e) => format!("{e:?}").into(),
- })),
- None,
- ),
+ label,
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
});
Some(())
}
-
#[cfg(test)]
mod tests {
use crate::inlay_hints::{
@@ -111,30 +124,30 @@ mod tests {
check_discriminants(
r#"
enum Enum {
- Variant,
-//^^^^^^^0
- Variant1,
-//^^^^^^^^1
- Variant2,
-//^^^^^^^^2
- Variant5 = 5,
- Variant6,
-//^^^^^^^^6
+ Variant,
+// ^^^^^^^ = 0$
+ Variant1,
+// ^^^^^^^^ = 1$
+ Variant2,
+// ^^^^^^^^ = 2$
+ Variant5 = 5,
+ Variant6,
+// ^^^^^^^^ = 6$
}
"#,
);
check_discriminants_fieldless(
r#"
enum Enum {
- Variant,
-//^^^^^^^0
- Variant1,
-//^^^^^^^^1
- Variant2,
-//^^^^^^^^2
- Variant5 = 5,
- Variant6,
-//^^^^^^^^6
+ Variant,
+// ^^^^^^^ = 0
+ Variant1,
+// ^^^^^^^^ = 1
+ Variant2,
+// ^^^^^^^^ = 2
+ Variant5 = 5,
+ Variant6,
+// ^^^^^^^^ = 6
}
"#,
);
@@ -144,26 +157,23 @@ enum Enum {
fn datacarrying_mixed() {
check_discriminants(
r#"
+#[repr(u8)]
enum Enum {
Variant(),
- //^^^^^^^^^0
+// ^^^^^^^^^ = 0
Variant1,
- //^^^^^^^^1
+// ^^^^^^^^ = 1
Variant2 {},
- //^^^^^^^^^^^2
+// ^^^^^^^^^^^ = 2
Variant3,
- //^^^^^^^^3
+// ^^^^^^^^ = 3
Variant5 = 5,
Variant6,
- //^^^^^^^^6
+// ^^^^^^^^ = 6
}
"#,
);
- }
-
- #[test]
- fn datacarrying_mixed_fieldless_set() {
- check_discriminants_fieldless(
+ check_discriminants(
r#"
enum Enum {
Variant(),
@@ -175,20 +185,20 @@ enum Enum {
}
"#,
);
+ }
+
+ #[test]
+ fn datacarrying_mixed_fieldless_set() {
check_discriminants_fieldless(
r#"
+#[repr(u8)]
enum Enum {
Variant(),
- //^^^^^^^^^0
Variant1,
- //^^^^^^^^1
Variant2 {},
- //^^^^^^^^^^^2
Variant3,
- //^^^^^^^^3
- Variant5 = 5,
+ Variant5,
Variant6,
- //^^^^^^^^6
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
index b7182085b..5fce11b78 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
@@ -10,7 +10,7 @@ use syntax::{
SyntaxToken,
};
-use crate::{InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints};
+use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -25,6 +25,10 @@ pub(super) fn hints(
range: t.text_range(),
kind: InlayKind::Lifetime,
label: label.into(),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: true,
};
let param_list = func.param_list()?;
@@ -189,12 +193,20 @@ pub(super) fn hints(
if is_empty { "" } else { ", " }
)
.into(),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: true,
});
}
(None, allocated_lifetimes) => acc.push(InlayHint {
range: func.name()?.syntax().text_range(),
kind: InlayKind::GenericParamList,
label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: false,
}),
}
Some(())
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
index 1122ee2e3..fc297a8d8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
@@ -8,7 +8,7 @@ use syntax::{
SyntaxKind,
};
-use crate::{InlayHint, InlayHintsConfig, InlayKind, LifetimeElisionHints};
+use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -34,6 +34,10 @@ pub(super) fn hints(
range: t.text_range(),
kind: InlayKind::Lifetime,
label: "'static".to_owned().into(),
+ text_edit: None,
+ position: InlayHintPosition::After,
+ pad_left: false,
+ pad_right: true,
});
}
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
index 9cdae6324..c4f43f411 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
@@ -10,7 +10,7 @@ use ide_db::{base_db::FileRange, RootDatabase};
use stdx::to_lower_snake_case;
use syntax::ast::{self, AstNode, HasArgList, HasName, UnaryOp};
-use crate::{InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind};
+use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -31,16 +31,16 @@ pub(super) fn hints(
// Only annotate hints for expressions that exist in the original file
let range = sema.original_range_opt(arg.syntax())?;
let (param_name, name_syntax) = match param.as_ref()? {
- Either::Left(pat) => ("self".to_string(), pat.name()),
+ Either::Left(pat) => (pat.name()?, pat.name()),
Either::Right(pat) => match pat {
- ast::Pat::IdentPat(it) => (it.name()?.to_string(), it.name()),
+ ast::Pat::IdentPat(it) => (it.name()?, it.name()),
_ => return None,
},
};
Some((name_syntax, param_name, arg, range))
})
.filter(|(_, param_name, arg, _)| {
- !should_hide_param_name_hint(sema, &callable, param_name, arg)
+ !should_hide_param_name_hint(sema, &callable, &param_name.text(), arg)
})
.map(|(param, param_name, _, FileRange { range, .. })| {
let mut linked_location = None;
@@ -53,10 +53,17 @@ pub(super) fn hints(
}
}
+ let colon = if config.render_colons { ":" } else { "" };
+ let label =
+ InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location);
InlayHint {
range,
kind: InlayKind::Parameter,
- label: InlayHintLabel::simple(param_name, None, linked_location),
+ label,
+ text_edit: None,
+ position: InlayHintPosition::Before,
+ pad_left: false,
+ pad_right: true,
}
});
diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs
new file mode 100644
index 000000000..cbcbb4b09
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/interpret_function.rs
@@ -0,0 +1,46 @@
+use hir::Semantics;
+use ide_db::base_db::SourceDatabaseExt;
+use ide_db::RootDatabase;
+use ide_db::{base_db::FilePosition, LineIndexDatabase};
+use std::{fmt::Write, time::Instant};
+use syntax::TextRange;
+use syntax::{algo::find_node_at_offset, ast, AstNode};
+
+// Feature: Interpret Function
+//
+// |===
+// | Editor | Action Name
+//
+// | VS Code | **rust-analyzer: Interpret Function**
+// |===
+pub(crate) fn interpret_function(db: &RootDatabase, position: FilePosition) -> String {
+ let start_time = Instant::now();
+ let mut result = find_and_interpret(db, position)
+ .unwrap_or_else(|| "Not inside a function body".to_string());
+ let duration = Instant::now() - start_time;
+ writeln!(result, "").unwrap();
+ writeln!(result, "----------------------").unwrap();
+ writeln!(result, " Finished in {}s", duration.as_secs_f32()).unwrap();
+ result
+}
+
+fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<String> {
+ let sema = Semantics::new(db);
+ let source_file = sema.parse(position.file_id);
+
+ let item = find_node_at_offset::<ast::Item>(source_file.syntax(), position.offset)?;
+ let def = match item {
+ ast::Item::Fn(it) => sema.to_def(&it)?,
+ _ => return None,
+ };
+ let span_formatter = |file_id, text_range: TextRange| {
+ let line_col = db.line_index(file_id).line_col(text_range.start());
+ let path = &db
+ .source_root(db.file_source_root(file_id))
+ .path_for_file(&file_id)
+ .map(|x| x.to_string());
+ let path = path.as_deref().unwrap_or("<unknown file>");
+ format!("file://{path}#{}:{}", line_col.line + 1, line_col.col)
+ };
+ Some(def.eval(db, span_formatter))
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index 078b66dd3..f195f78b3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -56,20 +56,24 @@ mod typing;
mod view_crate_graph;
mod view_hir;
mod view_mir;
+mod interpret_function;
mod view_item_tree;
mod shuffle_crate_graph;
+mod fetch_crates;
-use std::sync::Arc;
+use std::ffi::OsStr;
use cfg::CfgOptions;
+use fetch_crates::CrateInfo;
use ide_db::{
base_db::{
salsa::{self, ParallelDatabase},
CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
},
- symbol_index, LineIndexDatabase,
+ symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase,
};
use syntax::SourceFile;
+use triomphe::Arc;
use crate::navigation_target::{ToNav, TryToNav};
@@ -80,11 +84,14 @@ pub use crate::{
file_structure::{StructureNode, StructureNodeKind},
folding_ranges::{Fold, FoldKind},
highlight_related::{HighlightRelatedConfig, HighlightedRange},
- hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
+ hover::{
+ HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult,
+ MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
+ },
inlay_hints::{
AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint,
- InlayHintLabel, InlayHintLabelPart, InlayHintsConfig, InlayKind, InlayTooltip,
- LifetimeElisionHints,
+ InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind,
+ InlayTooltip, LifetimeElisionHints,
},
join_lines::JoinLinesConfig,
markup::Markup,
@@ -154,7 +161,11 @@ impl AnalysisHost {
}
pub fn update_lru_capacity(&mut self, lru_capacity: Option<usize>) {
- self.db.update_lru_capacity(lru_capacity);
+ self.db.update_parse_query_lru_capacity(lru_capacity);
+ }
+
+ pub fn update_lru_capacities(&mut self, lru_capacities: &FxHashMap<Box<str>, usize>) {
+ self.db.update_lru_capacities(lru_capacities);
}
/// Returns a snapshot of the current state, which you can query for
@@ -170,7 +181,7 @@ impl AnalysisHost {
}
/// NB: this clears the database
- pub fn per_query_memory_usage(&mut self) -> Vec<(String, profile::Bytes)> {
+ pub fn per_query_memory_usage(&mut self) -> Vec<(String, profile::Bytes, usize)> {
self.db.per_query_memory_usage()
}
pub fn request_cancellation(&mut self) {
@@ -233,14 +244,14 @@ impl Analysis {
None,
None,
cfg_options.clone(),
- cfg_options,
+ None,
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None, name: None },
+ CrateOrigin::Local { repo: None, name: None },
Err("Analysis::from_single_file has no target layout".into()),
+ None,
);
- change.change_file(file_id, Some(Arc::new(text)));
+ change.change_file(file_id, Some(Arc::from(text)));
change.set_crate_graph(crate_graph);
host.apply_change(change);
(host.analysis(), file_id)
@@ -259,7 +270,7 @@ impl Analysis {
}
/// Gets the text of the source file.
- pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<String>> {
+ pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<str>> {
self.with_db(|db| db.file_text(file_id))
}
@@ -313,6 +324,10 @@ impl Analysis {
self.with_db(|db| view_mir::view_mir(db, position))
}
+ pub fn interpret_function(&self, position: FilePosition) -> Cancellable<String> {
+ self.with_db(|db| interpret_function::interpret_function(db, position))
+ }
+
pub fn view_item_tree(&self, file_id: FileId) -> Cancellable<String> {
self.with_db(|db| view_item_tree::view_item_tree(db, file_id))
}
@@ -322,6 +337,10 @@ impl Analysis {
self.with_db(|db| view_crate_graph::view_crate_graph(db, full))
}
+ pub fn fetch_crates(&self) -> Cancellable<FxIndexSet<CrateInfo>> {
+ self.with_db(|db| fetch_crates::fetch_crates(db))
+ }
+
pub fn expand_macro(&self, position: FilePosition) -> Cancellable<Option<ExpandedMacro>> {
self.with_db(|db| expand_macro::expand_macro(db, position))
}
@@ -452,12 +471,19 @@ impl Analysis {
self.with_db(|db| moniker::moniker(db, position))
}
- /// Return URL(s) for the documentation of the symbol under the cursor.
+ /// Returns URL(s) for the documentation of the symbol under the cursor.
+ /// # Arguments
+ /// * `position` - Position in the file.
+ /// * `target_dir` - Directory where the build output is storeda.
pub fn external_docs(
&self,
position: FilePosition,
- ) -> Cancellable<Option<doc_links::DocumentationLink>> {
- self.with_db(|db| doc_links::external_docs(db, &position))
+ target_dir: Option<&OsStr>,
+ sysroot: Option<&OsStr>,
+ ) -> Cancellable<doc_links::DocumentationLinks> {
+ self.with_db(|db| {
+ doc_links::external_docs(db, &position, target_dir, sysroot).unwrap_or_default()
+ })
}
/// Computes parameter information at the given position.
@@ -508,6 +534,11 @@ impl Analysis {
self.with_db(|db| db.crate_graph()[crate_id].edition)
}
+ /// Returns true if this crate has `no_std` or `no_core` specified.
+ pub fn is_crate_no_std(&self, crate_id: CrateId) -> Cancellable<bool> {
+ self.with_db(|db| hir::db::DefDatabase::crate_def_map(db, crate_id).is_no_std())
+ }
+
/// Returns the root file of the given crate.
pub fn crate_root(&self, crate_id: CrateId) -> Cancellable<FileId> {
self.with_db(|db| db.crate_graph()[crate_id].root_file_id)
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index 349e79ecf..0d57e63d2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -1,7 +1,7 @@
//! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports)
//! for LSIF and LSP.
-use hir::{AsAssocItem, AssocItemContainer, Crate, Name, Semantics};
+use hir::{AsAssocItem, AssocItemContainer, Crate, Semantics};
use ide_db::{
base_db::{CrateOrigin, FilePosition, LangCrateOrigin},
defs::{Definition, IdentClass},
@@ -27,7 +27,7 @@ pub enum MonikerDescriptorKind {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MonikerDescriptor {
- pub name: Name,
+ pub name: String,
pub desc: MonikerDescriptorKind,
}
@@ -41,11 +41,7 @@ impl ToString for MonikerIdentifier {
fn to_string(&self) -> String {
match self {
MonikerIdentifier { description, crate_name } => {
- format!(
- "{}::{}",
- crate_name,
- description.iter().map(|x| x.name.to_string()).join("::")
- )
+ format!("{}::{}", crate_name, description.iter().map(|x| &x.name).join("::"))
}
}
}
@@ -136,7 +132,10 @@ pub(crate) fn def_to_moniker(
let krate = module.krate();
let mut description = vec![];
description.extend(module.path_to_root(db).into_iter().filter_map(|x| {
- Some(MonikerDescriptor { name: x.name(db)?, desc: MonikerDescriptorKind::Namespace })
+ Some(MonikerDescriptor {
+ name: x.name(db)?.display(db).to_string(),
+ desc: MonikerDescriptorKind::Namespace,
+ })
}));
// Handle associated items within a trait
@@ -147,7 +146,7 @@ pub(crate) fn def_to_moniker(
// Because different traits can have functions with the same name,
// we have to include the trait name as part of the moniker for uniqueness.
description.push(MonikerDescriptor {
- name: trait_.name(db),
+ name: trait_.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
});
}
@@ -156,14 +155,14 @@ pub(crate) fn def_to_moniker(
// we add both the struct name and the trait name to the path
if let Some(adt) = impl_.self_ty(db).as_adt() {
description.push(MonikerDescriptor {
- name: adt.name(db),
+ name: adt.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
});
}
if let Some(trait_) = impl_.trait_(db) {
description.push(MonikerDescriptor {
- name: trait_.name(db),
+ name: trait_.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
});
}
@@ -173,7 +172,7 @@ pub(crate) fn def_to_moniker(
if let Definition::Field(it) = def {
description.push(MonikerDescriptor {
- name: it.parent_def(db).name(db),
+ name: it.parent_def(db).name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
});
}
@@ -191,48 +190,63 @@ pub(crate) fn def_to_moniker(
return None;
}
- MonikerDescriptor { name: local.name(db), desc: MonikerDescriptorKind::Parameter }
- }
- Definition::Macro(m) => {
- MonikerDescriptor { name: m.name(db), desc: MonikerDescriptorKind::Macro }
- }
- Definition::Function(f) => {
- MonikerDescriptor { name: f.name(db), desc: MonikerDescriptorKind::Method }
- }
- Definition::Variant(v) => {
- MonikerDescriptor { name: v.name(db), desc: MonikerDescriptorKind::Type }
- }
- Definition::Const(c) => {
- MonikerDescriptor { name: c.name(db)?, desc: MonikerDescriptorKind::Term }
- }
- Definition::Trait(trait_) => {
- MonikerDescriptor { name: trait_.name(db), desc: MonikerDescriptorKind::Type }
- }
- Definition::TraitAlias(ta) => {
- MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::Type }
- }
- Definition::TypeAlias(ta) => {
- MonikerDescriptor { name: ta.name(db), desc: MonikerDescriptorKind::TypeParameter }
- }
- Definition::Module(m) => {
- MonikerDescriptor { name: m.name(db)?, desc: MonikerDescriptorKind::Namespace }
- }
- Definition::BuiltinType(b) => {
- MonikerDescriptor { name: b.name(), desc: MonikerDescriptorKind::Type }
+ MonikerDescriptor {
+ name: local.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Parameter,
+ }
}
+ Definition::Macro(m) => MonikerDescriptor {
+ name: m.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Macro,
+ },
+ Definition::Function(f) => MonikerDescriptor {
+ name: f.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Method,
+ },
+ Definition::Variant(v) => MonikerDescriptor {
+ name: v.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Type,
+ },
+ Definition::Const(c) => MonikerDescriptor {
+ name: c.name(db)?.display(db).to_string(),
+ desc: MonikerDescriptorKind::Term,
+ },
+ Definition::Trait(trait_) => MonikerDescriptor {
+ name: trait_.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Type,
+ },
+ Definition::TraitAlias(ta) => MonikerDescriptor {
+ name: ta.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Type,
+ },
+ Definition::TypeAlias(ta) => MonikerDescriptor {
+ name: ta.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::TypeParameter,
+ },
+ Definition::Module(m) => MonikerDescriptor {
+ name: m.name(db)?.display(db).to_string(),
+ desc: MonikerDescriptorKind::Namespace,
+ },
+ Definition::BuiltinType(b) => MonikerDescriptor {
+ name: b.name().display(db).to_string(),
+ desc: MonikerDescriptorKind::Type,
+ },
Definition::SelfType(imp) => MonikerDescriptor {
- name: imp.self_ty(db).as_adt()?.name(db),
+ name: imp.self_ty(db).as_adt()?.name(db).display(db).to_string(),
desc: MonikerDescriptorKind::Type,
},
- Definition::Field(it) => {
- MonikerDescriptor { name: it.name(db), desc: MonikerDescriptorKind::Term }
- }
- Definition::Adt(adt) => {
- MonikerDescriptor { name: adt.name(db), desc: MonikerDescriptorKind::Type }
- }
- Definition::Static(s) => {
- MonikerDescriptor { name: s.name(db), desc: MonikerDescriptorKind::Meta }
- }
+ Definition::Field(it) => MonikerDescriptor {
+ name: it.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Term,
+ },
+ Definition::Adt(adt) => MonikerDescriptor {
+ name: adt.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Type,
+ },
+ Definition::Static(s) => MonikerDescriptor {
+ name: s.name(db).display(db).to_string(),
+ desc: MonikerDescriptorKind::Meta,
+ },
};
description.push(name_desc);
@@ -245,11 +259,17 @@ pub(crate) fn def_to_moniker(
kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
package_information: {
let (name, repo, version) = match krate.origin(db) {
- CrateOrigin::CratesIo { repo, name } => (
+ CrateOrigin::Library { repo, name } => (name, repo, krate.version(db)),
+ CrateOrigin::Local { repo, name } => (
name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()),
repo,
krate.version(db),
),
+ CrateOrigin::Rustc { name } => (
+ name.clone(),
+ Some("https://github.com/rust-lang/rust/".to_string()),
+ Some(format!("https://github.com/rust-lang/rust/compiler/{name}",)),
+ ),
CrateOrigin::Lang(lang) => (
krate.display_name(db)?.canonical_name().to_string(),
Some("https://github.com/rust-lang/rust/".to_string()),
diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
index 6aae82f98..385c1b0c0 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -4,8 +4,8 @@ use std::fmt;
use either::Either;
use hir::{
- symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirDisplay,
- InFile, LocalSource, ModuleSource, Semantics,
+ symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasContainer, HasSource,
+ HirDisplay, HirFileId, InFile, LocalSource, ModuleSource,
};
use ide_db::{
base_db::{FileId, FileRange},
@@ -15,7 +15,7 @@ use ide_db::{defs::Definition, RootDatabase};
use stdx::never;
use syntax::{
ast::{self, HasName},
- match_ast, AstNode, SmolStr, SyntaxNode, TextRange,
+ AstNode, SmolStr, SyntaxNode, TextRange,
};
/// `NavigationTarget` represents an element in the editor's UI which you can
@@ -45,6 +45,9 @@ pub struct NavigationTarget {
pub container_name: Option<SmolStr>,
pub description: Option<String>,
pub docs: Option<Documentation>,
+ /// In addition to a `name` field, a `NavigationTarget` may also be aliased
+ /// In such cases we want a `NavigationTarget` to be accessible by its alias
+ pub alias: Option<SmolStr>,
}
impl fmt::Debug for NavigationTarget {
@@ -89,10 +92,9 @@ impl NavigationTarget {
pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget {
let name = module.name(db).map(|it| it.to_smol_str()).unwrap_or_default();
- if let Some(src @ InFile { value, .. }) = &module.declaration_source(db) {
- let FileRange { file_id, range: full_range } = src.syntax().original_file_range(db);
- let focus_range =
- value.name().and_then(|it| orig_focus_range(db, src.file_id, it.syntax()));
+ if let Some(InFile { value, file_id }) = &module.declaration_source(db) {
+ let (file_id, full_range, focus_range) =
+ orig_range_with_focus(db, *file_id, value.syntax(), value.name());
let mut res = NavigationTarget::from_syntax(
file_id,
name,
@@ -128,14 +130,15 @@ impl NavigationTarget {
/// Allows `NavigationTarget` to be created from a `NameOwner`
pub(crate) fn from_named(
db: &RootDatabase,
- node @ InFile { file_id, value }: InFile<&dyn ast::HasName>,
+ InFile { file_id, value }: InFile<&dyn ast::HasName>,
kind: SymbolKind,
) -> NavigationTarget {
let name = value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into());
- let focus_range = value.name().and_then(|it| orig_focus_range(db, file_id, it.syntax()));
- let FileRange { file_id, range } = node.map(|it| it.syntax()).original_file_range(db);
- NavigationTarget::from_syntax(file_id, name, focus_range, range, kind)
+ let (file_id, full_range, focus_range) =
+ orig_range_with_focus(db, file_id, value.syntax(), value.name());
+
+ NavigationTarget::from_syntax(file_id, name, focus_range, full_range, kind)
}
fn from_syntax(
@@ -154,23 +157,43 @@ impl NavigationTarget {
container_name: None,
description: None,
docs: None,
+ alias: None,
}
}
}
impl TryToNav for FileSymbol {
fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
- let full_range = self.loc.original_range(db)?;
- let name_range = self.loc.original_name_range(db)?;
+ let full_range = self.loc.original_range(db);
+ let focus_range = self.loc.original_name_range(db).and_then(|it| {
+ if it.file_id == full_range.file_id {
+ Some(it.range)
+ } else {
+ None
+ }
+ });
Some(NavigationTarget {
file_id: full_range.file_id,
- name: self.name.clone(),
- kind: Some(self.kind.into()),
+ name: if self.is_alias { self.def.name(db)?.to_smol_str() } else { self.name.clone() },
+ alias: if self.is_alias { Some(self.name.clone()) } else { None },
+ kind: Some(hir::ModuleDefId::from(self.def).into()),
full_range: full_range.range,
- focus_range: Some(name_range.range),
+ focus_range,
container_name: self.container_name.clone(),
- description: description_from_symbol(db, self),
+ description: match self.def {
+ hir::ModuleDef::Module(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Function(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Adt(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Variant(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Const(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Static(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Trait(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::TraitAlias(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::TypeAlias(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::Macro(it) => Some(it.display(db).to_string()),
+ hir::ModuleDef::BuiltinType(_) => None,
+ },
docs: None,
})
}
@@ -221,38 +244,80 @@ impl TryToNav for hir::ModuleDef {
}
}
-pub(crate) trait ToNavFromAst {
+pub(crate) trait ToNavFromAst: Sized {
const KIND: SymbolKind;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ _ = db;
+ None
+ }
}
+
+fn container_name(db: &RootDatabase, t: impl HasContainer) -> Option<SmolStr> {
+ match t.container(db) {
+ hir::ItemContainer::Trait(it) => Some(it.name(db).to_smol_str()),
+ // FIXME: Handle owners of blocks correctly here
+ hir::ItemContainer::Module(it) => it.name(db).map(|name| name.to_smol_str()),
+ _ => None,
+ }
+}
+
impl ToNavFromAst for hir::Function {
const KIND: SymbolKind = SymbolKind::Function;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
+
impl ToNavFromAst for hir::Const {
const KIND: SymbolKind = SymbolKind::Const;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::Static {
const KIND: SymbolKind = SymbolKind::Static;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::Struct {
const KIND: SymbolKind = SymbolKind::Struct;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::Enum {
const KIND: SymbolKind = SymbolKind::Enum;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::Variant {
const KIND: SymbolKind = SymbolKind::Variant;
}
impl ToNavFromAst for hir::Union {
const KIND: SymbolKind = SymbolKind::Union;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::TypeAlias {
const KIND: SymbolKind = SymbolKind::TypeAlias;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::Trait {
const KIND: SymbolKind = SymbolKind::Trait;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl ToNavFromAst for hir::TraitAlias {
const KIND: SymbolKind = SymbolKind::TraitAlias;
+ fn container_name(self, db: &RootDatabase) -> Option<SmolStr> {
+ container_name(db, self)
+ }
}
impl<D> TryToNav for D
@@ -269,6 +334,7 @@ where
);
res.docs = self.docs(db);
res.description = Some(self.display(db).to_string());
+ res.container_name = self.container_name(db);
Some(res)
}
}
@@ -280,15 +346,11 @@ impl ToNav for hir::Module {
let name = self.name(db).map(|it| it.to_smol_str()).unwrap_or_default();
let (syntax, focus) = match &value {
ModuleSource::SourceFile(node) => (node.syntax(), None),
- ModuleSource::Module(node) => (
- node.syntax(),
- node.name().and_then(|it| orig_focus_range(db, file_id, it.syntax())),
- ),
+ ModuleSource::Module(node) => (node.syntax(), node.name()),
ModuleSource::BlockExpr(node) => (node.syntax(), None),
};
- let FileRange { file_id, range: full_range } =
- InFile::new(file_id, syntax).original_file_range(db);
- NavigationTarget::from_syntax(file_id, name, focus, full_range, SymbolKind::Module)
+ let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus);
+ NavigationTarget::from_syntax(file_id, name, focus_range, full_range, SymbolKind::Module)
}
}
@@ -297,17 +359,14 @@ impl TryToNav for hir::Impl {
let InFile { file_id, value } = self.source(db)?;
let derive_attr = self.is_builtin_derive(db);
- let focus_range = if derive_attr.is_some() {
- None
- } else {
- value.self_ty().and_then(|ty| orig_focus_range(db, file_id, ty.syntax()))
- };
+ let focus = if derive_attr.is_some() { None } else { value.self_ty() };
- let FileRange { file_id, range: full_range } = match &derive_attr {
- Some(attr) => attr.syntax().original_file_range(db),
- None => InFile::new(file_id, value.syntax()).original_file_range(db),
+ let syntax = match &derive_attr {
+ Some(attr) => attr.value.syntax(),
+ None => value.syntax(),
};
+ let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus);
Some(NavigationTarget::from_syntax(
file_id,
"impl".into(),
@@ -396,9 +455,8 @@ impl ToNav for LocalSource {
Either::Left(bind_pat) => (bind_pat.syntax(), bind_pat.name()),
Either::Right(it) => (it.syntax(), it.name()),
};
- let focus_range = name.and_then(|it| orig_focus_range(db, file_id, it.syntax()));
- let FileRange { file_id, range: full_range } =
- InFile::new(file_id, node).original_file_range(db);
+
+ let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, node, name);
let name = local.name(db).to_smol_str();
let kind = if local.is_self(db) {
@@ -411,6 +469,7 @@ impl ToNav for LocalSource {
NavigationTarget {
file_id,
name,
+ alias: None,
kind: Some(kind),
full_range,
focus_range,
@@ -432,13 +491,13 @@ impl ToNav for hir::Label {
let InFile { file_id, value } = self.source(db);
let name = self.name(db).to_smol_str();
- let range = |syntax: &_| InFile::new(file_id, syntax).original_file_range(db);
- let FileRange { file_id, range: full_range } = range(value.syntax());
- let focus_range = value.lifetime().map(|lt| range(lt.syntax()).range);
+ let (file_id, full_range, focus_range) =
+ orig_range_with_focus(db, file_id, value.syntax(), value.lifetime());
NavigationTarget {
file_id,
name,
+ alias: None,
kind: Some(SymbolKind::Label),
full_range,
focus_range,
@@ -463,22 +522,18 @@ impl TryToNav for hir::TypeParam {
Either::Right(x) => Either::Right(x),
};
- let range = |syntax: &_| InFile::new(file_id, syntax).original_file_range(db);
- let focus_range = |syntax: &_| InFile::new(file_id, syntax).original_file_range_opt(db);
- let FileRange { file_id, range: full_range } = match &value {
- Either::Left(type_param) => range(type_param.syntax()),
- Either::Right(trait_) => trait_
- .name()
- .and_then(|name| focus_range(name.syntax()))
- .unwrap_or_else(|| range(trait_.syntax())),
+ let syntax = match &value {
+ Either::Left(type_param) => type_param.syntax(),
+ Either::Right(trait_) => trait_.syntax(),
};
- let focus_range = value
- .either(|it| it.name(), |it| it.name())
- .and_then(|it| focus_range(it.syntax()))
- .map(|it| it.range);
+ let focus = value.as_ref().either(|it| it.name(), |it| it.name());
+
+ let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus);
+
Some(NavigationTarget {
file_id,
name,
+ alias: None,
kind: Some(SymbolKind::TypeParam),
full_range,
focus_range,
@@ -500,14 +555,15 @@ impl TryToNav for hir::LifetimeParam {
let InFile { file_id, value } = self.source(db)?;
let name = self.name(db).to_smol_str();
- let FileRange { file_id, range: full_range } =
+ let FileRange { file_id, range } =
InFile::new(file_id, value.syntax()).original_file_range(db);
Some(NavigationTarget {
file_id,
name,
+ alias: None,
kind: Some(SymbolKind::LifetimeParam),
- full_range,
- focus_range: Some(full_range),
+ full_range: range,
+ focus_range: Some(range),
container_name: None,
description: None,
docs: None,
@@ -528,12 +584,12 @@ impl TryToNav for hir::ConstParam {
}
};
- let focus_range = value.name().and_then(|it| orig_focus_range(db, file_id, it.syntax()));
- let FileRange { file_id, range: full_range } =
- InFile::new(file_id, value.syntax()).original_file_range(db);
+ let (file_id, full_range, focus_range) =
+ orig_range_with_focus(db, file_id, value.syntax(), value.name());
Some(NavigationTarget {
file_id,
name,
+ alias: None,
kind: Some(SymbolKind::ConstParam),
full_range,
focus_range,
@@ -544,38 +600,19 @@ impl TryToNav for hir::ConstParam {
}
}
-/// Get a description of a symbol.
-///
-/// e.g. `struct Name`, `enum Name`, `fn Name`
-pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<String> {
- let sema = Semantics::new(db);
- let node = symbol.loc.syntax(&sema)?;
-
- match_ast! {
- match node {
- ast::Fn(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Struct(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Enum(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Trait(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::TraitAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Module(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::TypeAlias(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Const(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Static(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::RecordField(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Variant(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- ast::Union(it) => sema.to_def(&it).map(|it| it.display(db).to_string()),
- _ => None,
- }
- }
-}
-
-fn orig_focus_range(
+fn orig_range_with_focus(
db: &RootDatabase,
- file_id: hir::HirFileId,
- syntax: &SyntaxNode,
-) -> Option<TextRange> {
- InFile::new(file_id, syntax).original_file_range_opt(db).map(|it| it.range)
+ hir_file: HirFileId,
+ value: &SyntaxNode,
+ name: Option<impl AstNode>,
+) -> (FileId, TextRange, Option<TextRange>) {
+ let FileRange { file_id, range: full_range } =
+ InFile::new(hir_file, value).original_file_range(db);
+ let focus_range = name
+ .and_then(|it| InFile::new(hir_file, it.syntax()).original_file_range_opt(db))
+ .and_then(|range| if range.file_id == file_id { Some(range.range) } else { None });
+
+ (file_id, full_range, focus_range)
}
#[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs
index 87b3ef380..d704d12a0 100644
--- a/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/prime_caches.rs
@@ -12,9 +12,8 @@ use ide_db::{
salsa::{Database, ParallelDatabase, Snapshot},
Cancelled, CrateGraph, CrateId, SourceDatabase, SourceDatabaseExt,
},
- FxIndexMap,
+ FxHashSet, FxIndexMap,
};
-use stdx::hash::NoHashHashSet;
use crate::RootDatabase;
@@ -81,7 +80,11 @@ pub(crate) fn parallel_prime_caches(
for _ in 0..num_worker_threads {
let worker = prime_caches_worker.clone();
let db = db.snapshot();
- std::thread::spawn(move || Cancelled::catch(|| worker(db)));
+
+ stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
+ .allow_leak(true)
+ .spawn(move || Cancelled::catch(|| worker(db)))
+ .expect("failed to spawn thread");
}
(work_sender, progress_receiver)
@@ -142,7 +145,7 @@ pub(crate) fn parallel_prime_caches(
}
}
-fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> NoHashHashSet<CrateId> {
+fn compute_crates_to_prime(db: &RootDatabase, graph: &CrateGraph) -> FxHashSet<CrateId> {
// We're only interested in the workspace crates and the `ImportMap`s of their direct
// dependencies, though in practice the latter also compute the `DefMap`s.
// We don't prime transitive dependencies because they're generally not visible in
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index 3684c1033..fdc5261ac 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -17,7 +17,7 @@ use ide_db::{
RootDatabase,
};
use itertools::Itertools;
-use stdx::hash::NoHashHashMap;
+use nohash_hasher::IntMap;
use syntax::{
algo::find_node_at_offset,
ast::{self, HasName},
@@ -31,7 +31,7 @@ use crate::{FilePosition, NavigationTarget, TryToNav};
#[derive(Debug, Clone)]
pub struct ReferenceSearchResult {
pub declaration: Option<Declaration>,
- pub references: NoHashHashMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
+ pub references: IntMap<FileId, Vec<(TextRange, Option<ReferenceCategory>)>>,
}
#[derive(Debug, Clone)]
@@ -715,7 +715,7 @@ fn f() {
}
"#,
expect![[r#"
- Foo Struct FileId(1) 17..51 28..31
+ Foo Struct FileId(1) 17..51 28..31 foo
FileId(0) 53..56
FileId(2) 79..82
@@ -803,7 +803,7 @@ pub(super) struct Foo$0 {
}
"#,
expect![[r#"
- Foo Struct FileId(2) 0..41 18..21
+ Foo Struct FileId(2) 0..41 18..21 some
FileId(1) 20..23 Import
FileId(1) 47..50
@@ -1289,7 +1289,7 @@ trait Foo where Self$0 {
impl Foo for () {}
"#,
expect![[r#"
- Self TypeParam FileId(0) 6..9 6..9
+ Self TypeParam FileId(0) 0..44 6..9
FileId(0) 16..20
FileId(0) 37..41
@@ -1380,7 +1380,7 @@ fn foo<T: Bar>(_: impl Bar, _: &dyn Bar) {}
trait Foo = where Self$0: ;
"#,
expect![[r#"
- Self TypeParam FileId(0) 6..9 6..9
+ Self TypeParam FileId(0) 0..25 6..9
FileId(0) 18..22
"#]],
@@ -1542,7 +1542,7 @@ fn f() {
FileId(0) 161..165
- func Function FileId(0) 137..146 140..144
+ func Function FileId(0) 137..146 140..144 module
FileId(0) 181..185
"#]],
@@ -1581,7 +1581,7 @@ trait Trait {
}
"#,
expect![[r#"
- func Function FileId(0) 48..87 51..55
+ func Function FileId(0) 48..87 51..55 Trait
FileId(0) 74..78
"#]],
@@ -1692,7 +1692,7 @@ fn f<T: Trait>() {
}
"#,
expect![[r#"
- CONST Const FileId(0) 18..37 24..29
+ CONST Const FileId(0) 18..37 24..29 Trait
FileId(0) 71..76
FileId(0) 125..130
@@ -1721,7 +1721,7 @@ fn f<T: Trait>() {
}
"#,
expect![[r#"
- TypeAlias TypeAlias FileId(0) 18..33 23..32
+ TypeAlias TypeAlias FileId(0) 18..33 23..32 Trait
FileId(0) 66..75
FileId(0) 117..126
@@ -1750,7 +1750,7 @@ fn f<T: Trait>() {
}
"#,
expect![[r#"
- function Function FileId(0) 18..34 21..29
+ function Function FileId(0) 18..34 21..29 Trait
FileId(0) 65..73
FileId(0) 112..120
@@ -1894,7 +1894,7 @@ fn f<T: Trait>() {
}
"#,
expect![[r#"
- TypeAlias TypeAlias FileId(0) 18..33 23..32
+ TypeAlias TypeAlias FileId(0) 18..33 23..32 Trait
FileId(0) 66..75
FileId(0) 117..126
@@ -1950,7 +1950,7 @@ impl Foo for Bar {
fn method() {}
"#,
expect![[r#"
- method Function FileId(0) 16..39 19..25
+ method Function FileId(0) 16..39 19..25 Foo
FileId(0) 101..107
"#]],
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 8a8a9151c..27ad63d82 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -349,8 +349,13 @@ pub(crate) fn runnable_mod(
if !has_test_function_or_multiple_test_submodules(sema, &def) {
return None;
}
- let path =
- def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
+ let path = def
+ .path_to_root(sema.db)
+ .into_iter()
+ .rev()
+ .filter_map(|it| it.name(sema.db))
+ .map(|it| it.display(sema.db).to_string())
+ .join("::");
let attrs = def.attrs(sema.db);
let cfg = attrs.cfg();
@@ -376,7 +381,7 @@ pub(crate) fn runnable_impl(
} else {
String::new()
};
- let mut test_id = format!("{adt_name}{params}");
+ let mut test_id = format!("{}{params}", adt_name.display(sema.db));
test_id.retain(|c| c != ' ');
let test_id = TestId::Path(test_id);
@@ -391,8 +396,13 @@ fn runnable_mod_outline_definition(
if !has_test_function_or_multiple_test_submodules(sema, &def) {
return None;
}
- let path =
- def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
+ let path = def
+ .path_to_root(sema.db)
+ .into_iter()
+ .rev()
+ .filter_map(|it| it.name(sema.db))
+ .map(|it| it.display(sema.db).to_string())
+ .join("::");
let attrs = def.attrs(sema.db);
let cfg = attrs.cfg();
@@ -430,7 +440,7 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
let mut path = String::new();
def.canonical_module_path(db)?
.flat_map(|it| it.name(db))
- .for_each(|name| format_to!(path, "{}::", name));
+ .for_each(|name| format_to!(path, "{}::", name.display(db)));
// This probably belongs to canonical_path?
if let Some(assoc_item) = def.as_assoc_item(db) {
if let hir::AssocItemContainer::Impl(imp) = assoc_item.container(db) {
@@ -438,17 +448,17 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
if let Some(adt) = ty.as_adt() {
let name = adt.name(db);
let mut ty_args = ty.generic_parameters(db).peekable();
- format_to!(path, "{}", name);
+ format_to!(path, "{}", name.display(db));
if ty_args.peek().is_some() {
format_to!(path, "<{}>", ty_args.format_with(",", |ty, cb| cb(&ty)));
}
- format_to!(path, "::{}", def_name);
+ format_to!(path, "::{}", def_name.display(db));
path.retain(|c| c != ' ');
return Some(path);
}
}
}
- format_to!(path, "{}", def_name);
+ format_to!(path, "{}", def_name.display(db));
Some(path)
})();
@@ -2232,14 +2242,14 @@ mod tests {
file_id: FileId(
0,
),
- full_range: 52..115,
- focus_range: 67..75,
- name: "foo_test",
+ full_range: 121..185,
+ focus_range: 136..145,
+ name: "foo2_test",
kind: Function,
},
kind: Test {
test_id: Path(
- "tests::foo_test",
+ "tests::foo2_test",
),
attr: TestAttr {
ignore: false,
@@ -2253,14 +2263,14 @@ mod tests {
file_id: FileId(
0,
),
- full_range: 121..185,
- focus_range: 136..145,
- name: "foo2_test",
+ full_range: 52..115,
+ focus_range: 67..75,
+ name: "foo_test",
kind: Function,
},
kind: Test {
test_id: Path(
- "tests::foo2_test",
+ "tests::foo_test",
),
attr: TestAttr {
ignore: false,
@@ -2579,6 +2589,7 @@ mod r#mod {
),
full_range: 47..84,
name: "r#for",
+ container_name: "r#mod",
},
kind: DocTest {
test_id: Path(
@@ -2595,6 +2606,7 @@ mod r#mod {
),
full_range: 90..146,
name: "r#struct",
+ container_name: "r#mod",
},
kind: DocTest {
test_id: Path(
diff --git a/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs
index e606072a8..f85700daf 100644
--- a/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/shuffle_crate_graph.rs
@@ -1,9 +1,8 @@
-use std::sync::Arc;
-
use ide_db::{
- base_db::{salsa::Durability, CrateGraph, SourceDatabase},
+ base_db::{salsa::Durability, CrateGraph, ProcMacros, SourceDatabase},
FxHashMap, RootDatabase,
};
+use triomphe::Arc;
// Feature: Shuffle Crate Graph
//
@@ -16,6 +15,7 @@ use ide_db::{
// |===
pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
let crate_graph = db.crate_graph();
+ let proc_macros = db.proc_macros();
let mut shuffled_ids = crate_graph.iter().collect::<Vec<_>>();
@@ -23,6 +23,7 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
stdx::rand::shuffle(&mut shuffled_ids, |i| rng.rand_range(0..i as u32) as usize);
let mut new_graph = CrateGraph::default();
+ let mut new_proc_macros = ProcMacros::default();
let mut map = FxHashMap::default();
for old_id in shuffled_ids.iter().copied() {
@@ -35,11 +36,12 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
data.cfg_options.clone(),
data.potential_cfg_options.clone(),
data.env.clone(),
- data.proc_macro.clone(),
data.is_proc_macro,
data.origin.clone(),
data.target_layout.clone(),
+ data.channel,
);
+ new_proc_macros.insert(new_id, proc_macros[&old_id].clone());
map.insert(old_id, new_id);
}
@@ -53,4 +55,5 @@ pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
}
db.set_crate_graph_with_durability(Arc::new(new_graph), Durability::HIGH);
+ db.set_proc_macros_with_durability(Arc::new(new_proc_macros), Durability::HIGH);
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index 4b2c139f6..7795be54e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -15,8 +15,9 @@ use ide_db::{
use stdx::format_to;
use syntax::{
algo,
- ast::{self, HasArgList},
- match_ast, AstNode, Direction, SyntaxElementChildren, SyntaxToken, TextRange, TextSize,
+ ast::{self, AstChildren, HasArgList},
+ match_ast, AstNode, Direction, NodeOrToken, SyntaxElementChildren, SyntaxNode, SyntaxToken,
+ TextRange, TextSize, T,
};
use crate::RootDatabase;
@@ -116,6 +117,20 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
}
return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token);
},
+ ast::TuplePat(tuple_pat) => {
+ let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
+ if cursor_outside {
+ continue;
+ }
+ return signature_help_for_tuple_pat(&sema, tuple_pat, token);
+ },
+ ast::TupleExpr(tuple_expr) => {
+ let cursor_outside = tuple_expr.r_paren_token().as_ref() == Some(&token);
+ if cursor_outside {
+ continue;
+ }
+ return signature_help_for_tuple_expr(&sema, tuple_expr, token);
+ },
_ => (),
}
}
@@ -162,7 +177,7 @@ fn signature_help_for_call(
match callable.kind() {
hir::CallableKind::Function(func) => {
res.doc = func.docs(db).map(|it| it.into());
- format_to!(res.signature, "fn {}", func.name(db));
+ format_to!(res.signature, "fn {}", func.name(db).display(db));
fn_params = Some(match callable.receiver_param(db) {
Some(_self) => func.params_without_self(db),
None => func.assoc_fn_params(db),
@@ -170,15 +185,15 @@ fn signature_help_for_call(
}
hir::CallableKind::TupleStruct(strukt) => {
res.doc = strukt.docs(db).map(|it| it.into());
- format_to!(res.signature, "struct {}", strukt.name(db));
+ format_to!(res.signature, "struct {}", strukt.name(db).display(db));
}
hir::CallableKind::TupleEnumVariant(variant) => {
res.doc = variant.docs(db).map(|it| it.into());
format_to!(
res.signature,
"enum {}::{}",
- variant.parent_enum(db).name(db),
- variant.name(db)
+ variant.parent_enum(db).name(db).display(db),
+ variant.name(db).display(db)
);
}
hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (),
@@ -248,31 +263,31 @@ fn signature_help_for_generics(
match generics_def {
hir::GenericDef::Function(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "fn {}", it.name(db));
+ format_to!(res.signature, "fn {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "enum {}", it.name(db));
+ format_to!(res.signature, "enum {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "struct {}", it.name(db));
+ format_to!(res.signature, "struct {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Union(it)) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "union {}", it.name(db));
+ format_to!(res.signature, "union {}", it.name(db).display(db));
}
hir::GenericDef::Trait(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "trait {}", it.name(db));
+ format_to!(res.signature, "trait {}", it.name(db).display(db));
}
hir::GenericDef::TraitAlias(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "trait {}", it.name(db));
+ format_to!(res.signature, "trait {}", it.name(db).display(db));
}
hir::GenericDef::TypeAlias(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "type {}", it.name(db));
+ format_to!(res.signature, "type {}", it.name(db).display(db));
}
hir::GenericDef::Variant(it) => {
// In paths, generics of an enum can be specified *after* one of its variants.
@@ -280,7 +295,7 @@ fn signature_help_for_generics(
// We'll use the signature of the enum, but include the docs of the variant.
res.doc = it.docs(db).map(|it| it.into());
let enum_ = it.parent_enum(db);
- format_to!(res.signature, "enum {}", enum_.name(db));
+ format_to!(res.signature, "enum {}", enum_.name(db).display(db));
generics_def = enum_.into();
}
// These don't have generic args that can be specified
@@ -395,24 +410,26 @@ fn signature_help_for_tuple_struct_pat(
pat: ast::TupleStructPat,
token: SyntaxToken,
) -> Option<SignatureHelp> {
- let rest_pat = pat.fields().find(|it| matches!(it, ast::Pat::RestPat(_)));
- let is_left_of_rest_pat =
- rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());
-
+ let path = pat.path()?;
+ let path_res = sema.resolve_path(&path)?;
let mut res = SignatureHelp {
doc: None,
signature: String::new(),
parameters: vec![],
active_parameter: None,
};
-
let db = sema.db;
- let path_res = sema.resolve_path(&pat.path()?)?;
+
let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res {
let en = variant.parent_enum(db);
res.doc = en.docs(db).map(|it| it.into());
- format_to!(res.signature, "enum {}::{} (", en.name(db), variant.name(db));
+ format_to!(
+ res.signature,
+ "enum {}::{} (",
+ en.name(db).display(db),
+ variant.name(db).display(db)
+ );
variant.fields(db)
} else {
let adt = match path_res {
@@ -424,36 +441,78 @@ fn signature_help_for_tuple_struct_pat(
match adt {
hir::Adt::Struct(it) => {
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "struct {} (", it.name(db));
+ format_to!(res.signature, "struct {} (", it.name(db).display(db));
it.fields(db)
}
_ => return None,
}
};
- let commas = pat
- .syntax()
- .children_with_tokens()
- .filter_map(syntax::NodeOrToken::into_token)
- .filter(|t| t.kind() == syntax::T![,]);
- res.active_parameter = Some(if is_left_of_rest_pat {
- commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
- } else {
- let n_commas = commas
- .collect::<Vec<_>>()
- .into_iter()
- .rev()
- .take_while(|t| t.text_range().start() > token.text_range().start())
- .count();
- fields.len().saturating_sub(1).saturating_sub(n_commas)
- });
+ Some(signature_help_for_tuple_pat_ish(
+ db,
+ res,
+ pat.syntax(),
+ token,
+ pat.fields(),
+ fields.into_iter().map(|it| it.ty(db)),
+ ))
+}
+
+fn signature_help_for_tuple_pat(
+ sema: &Semantics<'_, RootDatabase>,
+ pat: ast::TuplePat,
+ token: SyntaxToken,
+) -> Option<SignatureHelp> {
+ let db = sema.db;
+ let field_pats = pat.fields();
+ let pat = pat.into();
+ let ty = sema.type_of_pat(&pat)?;
+ let fields = ty.original.tuple_fields(db);
+
+ Some(signature_help_for_tuple_pat_ish(
+ db,
+ SignatureHelp {
+ doc: None,
+ signature: String::from('('),
+ parameters: vec![],
+ active_parameter: None,
+ },
+ pat.syntax(),
+ token,
+ field_pats,
+ fields.into_iter(),
+ ))
+}
+fn signature_help_for_tuple_expr(
+ sema: &Semantics<'_, RootDatabase>,
+ expr: ast::TupleExpr,
+ token: SyntaxToken,
+) -> Option<SignatureHelp> {
+ let active_parameter = Some(
+ expr.syntax()
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .filter(|t| t.kind() == T![,])
+ .take_while(|t| t.text_range().start() <= token.text_range().start())
+ .count(),
+ );
+
+ let db = sema.db;
+ let mut res = SignatureHelp {
+ doc: None,
+ signature: String::from('('),
+ parameters: vec![],
+ active_parameter,
+ };
+ let expr = sema.type_of_expr(&expr.into())?;
+ let fields = expr.original.tuple_fields(db);
let mut buf = String::new();
- for ty in fields.into_iter().map(|it| it.ty(db)) {
+ for ty in fields {
format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
res.push_call_param(&buf);
buf.clear();
}
- res.signature.push_str(")");
+ res.signature.push(')');
Some(res)
}
@@ -465,8 +524,8 @@ fn signature_help_for_record_(
token: SyntaxToken,
) -> Option<SignatureHelp> {
let active_parameter = field_list_children
- .filter_map(syntax::NodeOrToken::into_token)
- .filter(|t| t.kind() == syntax::T![,])
+ .filter_map(NodeOrToken::into_token)
+ .filter(|t| t.kind() == T![,])
.take_while(|t| t.text_range().start() <= token.text_range().start())
.count();
@@ -486,7 +545,12 @@ fn signature_help_for_record_(
let en = variant.parent_enum(db);
res.doc = en.docs(db).map(|it| it.into());
- format_to!(res.signature, "enum {}::{} {{ ", en.name(db), variant.name(db));
+ format_to!(
+ res.signature,
+ "enum {}::{} {{ ",
+ en.name(db).display(db),
+ variant.name(db).display(db)
+ );
} else {
let adt = match path_res {
PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?,
@@ -498,12 +562,12 @@ fn signature_help_for_record_(
hir::Adt::Struct(it) => {
fields = it.fields(db);
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "struct {} {{ ", it.name(db));
+ format_to!(res.signature, "struct {} {{ ", it.name(db).display(db));
}
hir::Adt::Union(it) => {
fields = it.fields(db);
res.doc = it.docs(db).map(|it| it.into());
- format_to!(res.signature, "union {} {{ ", it.name(db));
+ format_to!(res.signature, "union {} {{ ", it.name(db).display(db));
}
_ => return None,
}
@@ -514,7 +578,7 @@ fn signature_help_for_record_(
let mut buf = String::new();
for (field, ty) in fields2 {
let name = field.name(db);
- format_to!(buf, "{name}: {}", ty.display_truncated(db, Some(20)));
+ format_to!(buf, "{}: {}", name.display(db), ty.display_truncated(db, Some(20)));
res.push_record_field(&buf);
buf.clear();
@@ -524,7 +588,7 @@ fn signature_help_for_record_(
}
for (name, field) in fields {
let Some(field) = field else { continue };
- format_to!(buf, "{name}: {}", field.ty(db).display_truncated(db, Some(20)));
+ format_to!(buf, "{}: {}", name.display(db), field.ty(db).display_truncated(db, Some(20)));
res.push_record_field(&buf);
buf.clear();
}
@@ -532,6 +596,46 @@ fn signature_help_for_record_(
Some(res)
}
+fn signature_help_for_tuple_pat_ish(
+ db: &RootDatabase,
+ mut res: SignatureHelp,
+ pat: &SyntaxNode,
+ token: SyntaxToken,
+ mut field_pats: AstChildren<ast::Pat>,
+ fields: impl ExactSizeIterator<Item = hir::Type>,
+) -> SignatureHelp {
+ let rest_pat = field_pats.find(|it| matches!(it, ast::Pat::RestPat(_)));
+ let is_left_of_rest_pat =
+ rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());
+
+ let commas = pat
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .filter(|t| t.kind() == T![,]);
+
+ res.active_parameter = {
+ Some(if is_left_of_rest_pat {
+ commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
+ } else {
+ let n_commas = commas
+ .collect::<Vec<_>>()
+ .into_iter()
+ .rev()
+ .take_while(|t| t.text_range().start() > token.text_range().start())
+ .count();
+ fields.len().saturating_sub(1).saturating_sub(n_commas)
+ })
+ };
+
+ let mut buf = String::new();
+ for ty in fields {
+ format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
+ res.push_call_param(&buf);
+ buf.clear();
+ }
+ res.signature.push_str(")");
+ res
+}
#[cfg(test)]
mod tests {
use std::iter;
@@ -1228,6 +1332,24 @@ fn main() {
}
#[test]
+ fn call_info_for_fn_def_over_reference() {
+ check(
+ r#"
+struct S;
+fn foo(s: S) -> i32 { 92 }
+fn main() {
+ let bar = &&&&&foo;
+ bar($0);
+}
+ "#,
+ expect![[r#"
+ fn foo(s: S) -> i32
+ ^^^^
+ "#]],
+ )
+ }
+
+ #[test]
fn call_info_for_fn_ptr() {
check(
r#"
@@ -1823,4 +1945,290 @@ fn main() {
"#]],
);
}
+
+ #[test]
+ fn test_tuple_expr_free() {
+ check(
+ r#"
+fn main() {
+ (0$0, 1, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ ($0 1, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ ^^^ ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ (1, 3 $0);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ (1, 3 $0,);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_expr_expected() {
+ check(
+ r#"
+fn main() {
+ let _: (&str, u32, u32)= ($0, 1, 3);
+}
+"#,
+ expect![[r#"
+ (&str, u32, u32)
+ ^^^^ --- ---
+ "#]],
+ );
+ // FIXME: Should typeck report a 4-ary tuple for the expression here?
+ check(
+ r#"
+fn main() {
+ let _: (&str, u32, u32, u32) = ($0, 1, 3);
+}
+"#,
+ expect![[r#"
+ (&str, u32, u32)
+ ^^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let _: (&str, u32, u32)= ($0, 1, 3, 5);
+}
+"#,
+ expect![[r#"
+ (&str, u32, u32, i32)
+ ^^^^ --- --- ---
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_pat_free() {
+ check(
+ r#"
+fn main() {
+ let ($0, 1, 3);
+}
+"#,
+ expect![[r#"
+ ({unknown}, i32, i32)
+ ^^^^^^^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (0$0, 1, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let ($0 1, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ ^^^ ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0,);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0, ..);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3, .., $0);
+}
+"#,
+ // FIXME: This is wrong, this should not mark the last as active
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_tuple_pat_expected() {
+ check(
+ r#"
+fn main() {
+ let (0$0, 1, 3): (i32, i32, i32);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let ($0, 1, 3): (i32, i32, i32);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0): (i32,);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0, ..): (i32, i32, i32, i32);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32, i32)
+ --- ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3, .., $0): (i32, i32, i32);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ --- --- ^^^
+ "#]],
+ );
+ }
+ #[test]
+ fn test_tuple_pat_expected_inferred() {
+ check(
+ r#"
+fn main() {
+ let (0$0, 1, 3) = (1, 2 ,3);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let ($0 1, 3) = (1, 2, 3);
+}
+"#,
+ // FIXME: Should typeck report a 3-ary tuple for the pattern here?
+ expect![[r#"
+ (i32, i32)
+ ^^^ ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0) = (1,);
+}
+"#,
+ expect![[r#"
+ (i32, i32)
+ --- ^^^
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3 $0, ..) = (1, 2, 3, 4);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32, i32)
+ --- ^^^ --- ---
+ "#]],
+ );
+ check(
+ r#"
+fn main() {
+ let (1, 3, .., $0) = (1, 2, 3);
+}
+"#,
+ expect![[r#"
+ (i32, i32, i32)
+ --- --- ^^^
+ "#]],
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/ssr.rs b/src/tools/rust-analyzer/crates/ide/src/ssr.rs
index 497eb1cc1..deaf3c9c4 100644
--- a/src/tools/rust-analyzer/crates/ide/src/ssr.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/ssr.rs
@@ -56,8 +56,6 @@ pub(crate) fn ssr_assists(
#[cfg(test)]
mod tests {
- use std::sync::Arc;
-
use expect_test::expect;
use ide_assists::{Assist, AssistResolveStrategy};
use ide_db::{
@@ -65,6 +63,7 @@ mod tests {
symbol_index::SymbolsDatabase,
FxHashSet, RootDatabase,
};
+ use triomphe::Arc;
use super::ssr_assists;
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index c97691b14..3e3d9f8f8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -118,9 +118,11 @@ impl StaticIndex<'_> {
adjustment_hints_hide_outside_unsafe: false,
hide_named_constructor_hints: false,
hide_closure_initialization_hints: false,
+ closure_style: hir::ClosureStyle::ImplFn,
param_names_for_lifetime_elision_hints: false,
binding_mode_hints: false,
max_length: Some(25),
+ closure_capture_hints: false,
closing_brace_hints_min_lines: Some(25),
},
file_id,
@@ -136,10 +138,10 @@ impl StaticIndex<'_> {
});
let hover_config = HoverConfig {
links_in_hover: true,
+ memory_layout: None,
documentation: true,
keywords: true,
format: crate::HoverDocFormat::Markdown,
- interpret_tests: false,
};
let tokens = tokens.filter(|token| {
matches!(
diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs
index 7ce782f93..d2c77e2dc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/status.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/status.rs
@@ -1,9 +1,18 @@
-use std::{fmt, sync::Arc};
+use std::{fmt, marker::PhantomData};
-use hir::{ExpandResult, MacroFile};
-use ide_db::base_db::{
- salsa::debug::{DebugQueryTable, TableEntry},
- CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId,
+use hir::{
+ db::{AstIdMapQuery, AttrsQuery, BlockDefMapQuery, ParseMacroExpansionQuery},
+ Attr, Attrs, ExpandResult, MacroFile, Module,
+};
+use ide_db::{
+ base_db::{
+ salsa::{
+ debug::{DebugQueryTable, TableEntry},
+ Query, QueryTable,
+ },
+ CrateId, FileId, FileTextQuery, ParseQuery, SourceDatabase, SourceRootId,
+ },
+ symbol_index::ModuleSymbolsQuery,
};
use ide_db::{
symbol_index::{LibrarySymbolsQuery, SymbolIndex},
@@ -14,13 +23,7 @@ use profile::{memory_usage, Bytes};
use std::env;
use stdx::format_to;
use syntax::{ast, Parse, SyntaxNode};
-
-fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
- ide_db::base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>()
-}
-fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
- hir::db::ParseMacroExpansionQuery.in_db(db).entries::<SyntaxTreeStats>()
-}
+use triomphe::Arc;
// Feature: Status
//
@@ -34,15 +37,22 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
// image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[]
pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
let mut buf = String::new();
- format_to!(buf, "{}\n", FileTextQuery.in_db(db).entries::<FilesStats>());
- format_to!(buf, "{}\n", LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>());
- format_to!(buf, "{}\n", syntax_tree_stats(db));
- format_to!(buf, "{} (Macros)\n", macro_syntax_tree_stats(db));
+
+ format_to!(buf, "{}\n", collect_query(FileTextQuery.in_db(db)));
+ format_to!(buf, "{}\n", collect_query(ParseQuery.in_db(db)));
+ format_to!(buf, "{}\n", collect_query(ParseMacroExpansionQuery.in_db(db)));
+ format_to!(buf, "{}\n", collect_query(LibrarySymbolsQuery.in_db(db)));
+ format_to!(buf, "{}\n", collect_query(ModuleSymbolsQuery.in_db(db)));
format_to!(buf, "{} in total\n", memory_usage());
if env::var("RA_COUNT").is_ok() {
format_to!(buf, "\nCounts:\n{}", profile::countme::get_all());
}
+ format_to!(buf, "\nDebug info:\n");
+ format_to!(buf, "{}\n", collect_query(AttrsQuery.in_db(db)));
+ format_to!(buf, "{} ast id maps\n", collect_query_count(AstIdMapQuery.in_db(db)));
+ format_to!(buf, "{} block def maps\n", collect_query_count(BlockDefMapQuery.in_db(db)));
+
if let Some(file_id) = file_id {
format_to!(buf, "\nFile info:\n");
let crates = crate::parent_module::crates_for(db, file_id);
@@ -52,8 +62,8 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
let crate_graph = db.crate_graph();
for krate in crates {
let display_crate = |krate: CrateId| match &crate_graph[krate].display_name {
- Some(it) => format!("{it}({krate:?})"),
- None => format!("{krate:?}"),
+ Some(it) => format!("{it}({})", krate.into_raw()),
+ None => format!("{}", krate.into_raw()),
};
format_to!(buf, "Crate: {}\n", display_crate(krate));
let deps = crate_graph[krate]
@@ -68,6 +78,82 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
buf.trim().to_string()
}
+fn collect_query<'q, Q>(table: QueryTable<'q, Q>) -> <Q as QueryCollect>::Collector
+where
+ QueryTable<'q, Q>: DebugQueryTable,
+ Q: QueryCollect,
+ <Q as Query>::Storage: 'q,
+ <Q as QueryCollect>::Collector: StatCollect<
+ <QueryTable<'q, Q> as DebugQueryTable>::Key,
+ <QueryTable<'q, Q> as DebugQueryTable>::Value,
+ >,
+{
+ struct StatCollectorWrapper<C>(C);
+ impl<C: StatCollect<K, V>, K, V> FromIterator<TableEntry<K, V>> for StatCollectorWrapper<C> {
+ fn from_iter<T>(iter: T) -> StatCollectorWrapper<C>
+ where
+ T: IntoIterator<Item = TableEntry<K, V>>,
+ {
+ let mut res = C::default();
+ for entry in iter {
+ res.collect_entry(entry.key, entry.value);
+ }
+ StatCollectorWrapper(res)
+ }
+ }
+ table.entries::<StatCollectorWrapper<<Q as QueryCollect>::Collector>>().0
+}
+
+fn collect_query_count<'q, Q>(table: QueryTable<'q, Q>) -> usize
+where
+ QueryTable<'q, Q>: DebugQueryTable,
+ Q: Query,
+ <Q as Query>::Storage: 'q,
+{
+ struct EntryCounter(usize);
+ impl<K, V> FromIterator<TableEntry<K, V>> for EntryCounter {
+ fn from_iter<T>(iter: T) -> EntryCounter
+ where
+ T: IntoIterator<Item = TableEntry<K, V>>,
+ {
+ EntryCounter(iter.into_iter().count())
+ }
+ }
+ table.entries::<EntryCounter>().0
+}
+
+trait QueryCollect: Query {
+ type Collector;
+}
+
+impl QueryCollect for LibrarySymbolsQuery {
+ type Collector = SymbolsStats<SourceRootId>;
+}
+
+impl QueryCollect for ParseQuery {
+ type Collector = SyntaxTreeStats<false>;
+}
+
+impl QueryCollect for ParseMacroExpansionQuery {
+ type Collector = SyntaxTreeStats<true>;
+}
+
+impl QueryCollect for FileTextQuery {
+ type Collector = FilesStats;
+}
+
+impl QueryCollect for ModuleSymbolsQuery {
+ type Collector = SymbolsStats<Module>;
+}
+
+impl QueryCollect for AttrsQuery {
+ type Collector = AttrsStats;
+}
+
+trait StatCollect<K, V>: Default {
+ fn collect_entry(&mut self, key: K, value: Option<V>);
+}
+
#[derive(Default)]
struct FilesStats {
total: usize,
@@ -80,85 +166,98 @@ impl fmt::Display for FilesStats {
}
}
-impl FromIterator<TableEntry<FileId, Arc<String>>> for FilesStats {
- fn from_iter<T>(iter: T) -> FilesStats
- where
- T: IntoIterator<Item = TableEntry<FileId, Arc<String>>>,
- {
- let mut res = FilesStats::default();
- for entry in iter {
- res.total += 1;
- res.size += entry.value.unwrap().len();
- }
- res
+impl StatCollect<FileId, Arc<str>> for FilesStats {
+ fn collect_entry(&mut self, _: FileId, value: Option<Arc<str>>) {
+ self.total += 1;
+ self.size += value.unwrap().len();
}
}
#[derive(Default)]
-pub(crate) struct SyntaxTreeStats {
+pub(crate) struct SyntaxTreeStats<const MACROS: bool> {
total: usize,
pub(crate) retained: usize,
}
-impl fmt::Display for SyntaxTreeStats {
+impl<const MACROS: bool> fmt::Display for SyntaxTreeStats<MACROS> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(fmt, "{} trees, {} preserved", self.total, self.retained)
+ write!(
+ fmt,
+ "{} trees, {} preserved{}",
+ self.total,
+ self.retained,
+ if MACROS { " (macros)" } else { "" }
+ )
}
}
-impl FromIterator<TableEntry<FileId, Parse<ast::SourceFile>>> for SyntaxTreeStats {
- fn from_iter<T>(iter: T) -> SyntaxTreeStats
- where
- T: IntoIterator<Item = TableEntry<FileId, Parse<ast::SourceFile>>>,
- {
- let mut res = SyntaxTreeStats::default();
- for entry in iter {
- res.total += 1;
- res.retained += entry.value.is_some() as usize;
- }
- res
+impl StatCollect<FileId, Parse<ast::SourceFile>> for SyntaxTreeStats<false> {
+ fn collect_entry(&mut self, _: FileId, value: Option<Parse<ast::SourceFile>>) {
+ self.total += 1;
+ self.retained += value.is_some() as usize;
}
}
-impl<M> FromIterator<TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>
- for SyntaxTreeStats
-{
- fn from_iter<T>(iter: T) -> SyntaxTreeStats
- where
- T: IntoIterator<Item = TableEntry<MacroFile, ExpandResult<Option<(Parse<SyntaxNode>, M)>>>>,
- {
- let mut res = SyntaxTreeStats::default();
- for entry in iter {
- res.total += 1;
- res.retained += entry.value.is_some() as usize;
+impl<M> StatCollect<MacroFile, ExpandResult<(Parse<SyntaxNode>, M)>> for SyntaxTreeStats<true> {
+ fn collect_entry(&mut self, _: MacroFile, value: Option<ExpandResult<(Parse<SyntaxNode>, M)>>) {
+ self.total += 1;
+ self.retained += value.is_some() as usize;
+ }
+}
+
+struct SymbolsStats<Key> {
+ total: usize,
+ size: Bytes,
+ phantom: PhantomData<Key>,
+}
+
+impl<Key> Default for SymbolsStats<Key> {
+ fn default() -> Self {
+ Self { total: Default::default(), size: Default::default(), phantom: PhantomData }
+ }
+}
+
+impl fmt::Display for SymbolsStats<Module> {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(fmt, "{} of module index symbols ({})", self.size, self.total)
+ }
+}
+impl fmt::Display for SymbolsStats<SourceRootId> {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(fmt, "{} of library index symbols ({})", self.size, self.total)
+ }
+}
+impl<Key> StatCollect<Key, Arc<SymbolIndex>> for SymbolsStats<Key> {
+ fn collect_entry(&mut self, _: Key, value: Option<Arc<SymbolIndex>>) {
+ if let Some(symbols) = value {
+ self.total += symbols.len();
+ self.size += symbols.memory_size();
}
- res
}
}
#[derive(Default)]
-struct LibrarySymbolsStats {
+struct AttrsStats {
+ entries: usize,
total: usize,
- size: Bytes,
}
-impl fmt::Display for LibrarySymbolsStats {
+impl fmt::Display for AttrsStats {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(fmt, "{} of index symbols ({})", self.size, self.total)
+ let size =
+ self.entries * std::mem::size_of::<Attrs>() + self.total * std::mem::size_of::<Attr>();
+ let size = Bytes::new(size as _);
+ write!(
+ fmt,
+ "{} attribute query entries, {} total attributes ({} for storing entries)",
+ self.entries, self.total, size
+ )
}
}
-impl FromIterator<TableEntry<SourceRootId, Arc<SymbolIndex>>> for LibrarySymbolsStats {
- fn from_iter<T>(iter: T) -> LibrarySymbolsStats
- where
- T: IntoIterator<Item = TableEntry<SourceRootId, Arc<SymbolIndex>>>,
- {
- let mut res = LibrarySymbolsStats::default();
- for entry in iter {
- let symbols = entry.value.unwrap();
- res.total += symbols.len();
- res.size += symbols.memory_size();
- }
- res
+impl<Key> StatCollect<Key, Attrs> for AttrsStats {
+ fn collect_entry(&mut self, _: Key, value: Option<Attrs>) {
+ self.entries += 1;
+ self.total += value.map_or(0, |it| it.len());
}
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
index 454a250f3..dc06591ff 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
@@ -16,13 +16,19 @@ mod tests;
use hir::{Name, Semantics};
use ide_db::{FxHashMap, RootDatabase, SymbolKind};
use syntax::{
- ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T,
+ ast::{self, IsString},
+ AstNode, AstToken, NodeOrToken,
+ SyntaxKind::*,
+ SyntaxNode, TextRange, WalkEvent, T,
};
use crate::{
syntax_highlighting::{
- escape::highlight_escape_string, format::highlight_format_string, highlights::Highlights,
- macro_::MacroHighlighter, tags::Highlight,
+ escape::{highlight_escape_char, highlight_escape_string},
+ format::highlight_format_string,
+ highlights::Highlights,
+ macro_::MacroHighlighter,
+ tags::Highlight,
},
FileId, HlMod, HlOperator, HlPunct, HlTag,
};
@@ -163,6 +169,7 @@ pub struct HighlightConfig {
// injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation.
// intraDocLink:: Emitted for intra doc links in doc-strings.
// library:: Emitted for items that are defined outside of the current crate.
+// macro:: Emitted for tokens inside macro calls.
// mutable:: Emitted for mutable locals and statics as well as functions taking `&mut self`.
// public:: Emitted for items that are from the current crate and are `pub`.
// reference:: Emitted for locals behind a reference and functions taking `self` by reference.
@@ -236,7 +243,11 @@ fn traverse(
let mut attr_or_derive_item = None;
let mut current_macro: Option<ast::Macro> = None;
let mut macro_highlighter = MacroHighlighter::default();
+
+ // FIXME: these are not perfectly accurate, we determine them by the real file's syntax tree
+ // an an attribute nested in a macro call will not emit `inside_attribute`
let mut inside_attribute = false;
+ let mut inside_macro_call = false;
// Walk all nodes, keeping track of whether we are inside a macro or not.
// If in macro, expand it first and highlight the expanded code.
@@ -267,46 +278,50 @@ fn traverse(
inside_attribute = false
}
- Enter(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => {
- match ast::Item::cast(node.clone()) {
- Some(ast::Item::MacroRules(mac)) => {
- macro_highlighter.init();
- current_macro = Some(mac.into());
- continue;
- }
- Some(ast::Item::MacroDef(mac)) => {
- macro_highlighter.init();
- current_macro = Some(mac.into());
- continue;
- }
- Some(item) => {
- if matches!(node.kind(), FN | CONST | STATIC) {
- bindings_shadow_count.clear();
+ Enter(NodeOrToken::Node(node)) => match ast::Item::cast(node.clone()) {
+ Some(item) => {
+ match item {
+ ast::Item::MacroRules(mac) => {
+ macro_highlighter.init();
+ current_macro = Some(mac.into());
+ continue;
+ }
+ ast::Item::MacroDef(mac) => {
+ macro_highlighter.init();
+ current_macro = Some(mac.into());
+ continue;
+ }
+ ast::Item::Fn(_) | ast::Item::Const(_) | ast::Item::Static(_) => {
+ bindings_shadow_count.clear()
}
+ ast::Item::MacroCall(_) => {
+ inside_macro_call = true;
+ }
+ _ => (),
+ }
- if attr_or_derive_item.is_none() {
- if sema.is_attr_macro_call(&item) {
- attr_or_derive_item = Some(AttrOrDerive::Attr(item));
- } else {
- let adt = match item {
- ast::Item::Enum(it) => Some(ast::Adt::Enum(it)),
- ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
- ast::Item::Union(it) => Some(ast::Adt::Union(it)),
- _ => None,
- };
- match adt {
- Some(adt) if sema.is_derive_annotated(&adt) => {
- attr_or_derive_item =
- Some(AttrOrDerive::Derive(ast::Item::from(adt)));
- }
- _ => (),
+ if attr_or_derive_item.is_none() {
+ if sema.is_attr_macro_call(&item) {
+ attr_or_derive_item = Some(AttrOrDerive::Attr(item));
+ } else {
+ let adt = match item {
+ ast::Item::Enum(it) => Some(ast::Adt::Enum(it)),
+ ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
+ ast::Item::Union(it) => Some(ast::Adt::Union(it)),
+ _ => None,
+ };
+ match adt {
+ Some(adt) if sema.is_derive_annotated(&adt) => {
+ attr_or_derive_item =
+ Some(AttrOrDerive::Derive(ast::Item::from(adt)));
}
+ _ => (),
}
}
}
- _ => (),
}
- }
+ _ => (),
+ },
Leave(NodeOrToken::Node(node)) if ast::Item::can_cast(node.kind()) => {
match ast::Item::cast(node.clone()) {
Some(ast::Item::MacroRules(mac)) => {
@@ -324,6 +339,9 @@ fn traverse(
{
attr_or_derive_item = None;
}
+ Some(ast::Item::MacroCall(_)) => {
+ inside_macro_call = false;
+ }
_ => (),
}
}
@@ -419,14 +437,35 @@ fn traverse(
continue;
}
highlight_format_string(hl, &string, &expanded_string, range);
- highlight_escape_string(hl, &string, range.start());
+
+ if !string.is_raw() {
+ highlight_escape_string(hl, &string, range.start());
+ }
}
} else if ast::ByteString::can_cast(token.kind())
&& ast::ByteString::can_cast(descended_token.kind())
{
if let Some(byte_string) = ast::ByteString::cast(token) {
- highlight_escape_string(hl, &byte_string, range.start());
+ if !byte_string.is_raw() {
+ highlight_escape_string(hl, &byte_string, range.start());
+ }
}
+ } else if ast::CString::can_cast(token.kind())
+ && ast::CString::can_cast(descended_token.kind())
+ {
+ if let Some(c_string) = ast::CString::cast(token) {
+ if !c_string.is_raw() {
+ highlight_escape_string(hl, &c_string, range.start());
+ }
+ }
+ } else if ast::Char::can_cast(token.kind())
+ && ast::Char::can_cast(descended_token.kind())
+ {
+ let Some(char) = ast::Char::cast(token) else {
+ continue;
+ };
+
+ highlight_escape_char(hl, &char, range.start())
}
}
@@ -455,32 +494,42 @@ fn traverse(
}
// apply config filtering
- match &mut highlight.tag {
- HlTag::StringLiteral if !config.strings => continue,
- // If punctuation is disabled, make the macro bang part of the macro call again.
- tag @ HlTag::Punctuation(HlPunct::MacroBang) => {
- if !config.macro_bang {
- *tag = HlTag::Symbol(SymbolKind::Macro);
- } else if !config.specialize_punctuation {
- *tag = HlTag::Punctuation(HlPunct::Other);
- }
- }
- HlTag::Punctuation(_) if !config.punctuation => continue,
- tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => {
- *tag = HlTag::Punctuation(HlPunct::Other);
- }
- HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => continue,
- tag @ HlTag::Operator(_) if !config.specialize_operator => {
- *tag = HlTag::Operator(HlOperator::Other);
- }
- _ => (),
+ if !filter_by_config(&mut highlight, config) {
+ continue;
}
if inside_attribute {
highlight |= HlMod::Attribute
}
+ if inside_macro_call && tt_level > 0 {
+ highlight |= HlMod::Macro
+ }
hl.add(HlRange { range, highlight, binding_hash });
}
}
}
+
+fn filter_by_config(highlight: &mut Highlight, config: HighlightConfig) -> bool {
+ match &mut highlight.tag {
+ HlTag::StringLiteral if !config.strings => return false,
+ // If punctuation is disabled, make the macro bang part of the macro call again.
+ tag @ HlTag::Punctuation(HlPunct::MacroBang) => {
+ if !config.macro_bang {
+ *tag = HlTag::Symbol(SymbolKind::Macro);
+ } else if !config.specialize_punctuation {
+ *tag = HlTag::Punctuation(HlPunct::Other);
+ }
+ }
+ HlTag::Punctuation(_) if !config.punctuation => return false,
+ tag @ HlTag::Punctuation(_) if !config.specialize_punctuation => {
+ *tag = HlTag::Punctuation(HlPunct::Other);
+ }
+ HlTag::Operator(_) if !config.operator && highlight.mods.is_empty() => return false,
+ tag @ HlTag::Operator(_) if !config.specialize_operator => {
+ *tag = HlTag::Operator(HlOperator::Other);
+ }
+ _ => (),
+ }
+ true
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs
index 6a1236c79..211e35880 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs
@@ -1,8 +1,8 @@
//! Syntax highlighting for escape sequences
use crate::syntax_highlighting::highlights::Highlights;
use crate::{HlRange, HlTag};
-use syntax::ast::IsString;
-use syntax::TextSize;
+use syntax::ast::{Char, IsString};
+use syntax::{AstToken, TextRange, TextSize};
pub(super) fn highlight_escape_string<T: IsString>(
stack: &mut Highlights,
@@ -23,3 +23,23 @@ pub(super) fn highlight_escape_string<T: IsString>(
}
});
}
+
+pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char, start: TextSize) {
+ if char.value().is_none() {
+ return;
+ }
+
+ let text = char.text();
+ if !text.starts_with('\'') || !text.ends_with('\'') {
+ return;
+ }
+
+ let text = &text[1..text.len() - 1];
+ if !text.starts_with('\\') {
+ return;
+ }
+
+ let range =
+ TextRange::new(start + TextSize::from(1), start + TextSize::from(text.len() as u32 + 1));
+ stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None })
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index 2111baad7..3c40246a6 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -26,7 +26,7 @@ pub(super) fn token(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> O
}
let highlight: Highlight = match token.kind() {
- STRING | BYTE_STRING => HlTag::StringLiteral.into(),
+ STRING | BYTE_STRING | C_STRING => HlTag::StringLiteral.into(),
INT_NUMBER if token.parent_ancestors().nth(1).map(|it| it.kind()) == Some(FIELD_EXPR) => {
SymbolKind::Field.into()
}
@@ -340,7 +340,7 @@ fn highlight_def(
Definition::Field(_) => Highlight::new(HlTag::Symbol(SymbolKind::Field)),
Definition::Module(module) => {
let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module));
- if module.is_crate_root(db) {
+ if module.is_crate_root() {
h |= HlMod::CrateRoot;
}
h
@@ -675,14 +675,12 @@ fn is_consumed_lvalue(node: &SyntaxNode, local: &hir::Local, db: &RootDatabase)
/// Returns true if the parent nodes of `node` all match the `SyntaxKind`s in `kinds` exactly.
fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[SyntaxKind]) -> bool {
- while let (Some(parent), [kind, rest @ ..]) = (&node.parent(), kinds) {
+ while let (Some(parent), [kind, rest @ ..]) = (node.parent(), kinds) {
if parent.kind() != *kind {
return false;
}
- // FIXME: Would be nice to get parent out of the match, but binding by-move and by-value
- // in the same pattern is unstable: rust-lang/rust#68354.
- node = node.parent().unwrap().into();
+ node = parent.into();
kinds = rest;
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
index 3c4cfc781..901df147d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
@@ -52,7 +52,11 @@ pub(super) fn ra_fixture(
if let Some(next) = text.strip_prefix(marker) {
if let Some(range) = literal.map_range_up(TextRange::at(offset, TextSize::of(marker))) {
- hl.add(HlRange { range, highlight: HlTag::Keyword.into(), binding_hash: None });
+ hl.add(HlRange {
+ range,
+ highlight: HlTag::Keyword | HlMod::Injected,
+ binding_hash: None,
+ });
}
text = next;
@@ -66,7 +70,16 @@ pub(super) fn ra_fixture(
for mut hl_range in analysis
.highlight(
- HighlightConfig { syntactic_name_ref_highlighting: false, ..config },
+ HighlightConfig {
+ syntactic_name_ref_highlighting: false,
+ punctuation: true,
+ operator: true,
+ strings: true,
+ specialize_punctuation: config.specialize_punctuation,
+ specialize_operator: config.operator,
+ inject_doc_comment: config.inject_doc_comment,
+ macro_bang: config.macro_bang,
+ },
tmp_file_id,
)
.unwrap()
@@ -74,6 +87,7 @@ pub(super) fn ra_fixture(
for range in inj.map_range_up(hl_range.range) {
if let Some(range) = literal.map_range_up(range) {
hl_range.range = range;
+ hl_range.highlight |= HlMod::Injected;
hl.add(hl_range);
}
}
@@ -217,7 +231,16 @@ pub(super) fn doc_comment(
if let Ok(ranges) = analysis.with_db(|db| {
super::highlight(
db,
- HighlightConfig { syntactic_name_ref_highlighting: true, ..config },
+ HighlightConfig {
+ syntactic_name_ref_highlighting: true,
+ punctuation: true,
+ operator: true,
+ strings: true,
+ specialize_punctuation: config.specialize_punctuation,
+ specialize_operator: config.operator,
+ inject_doc_comment: config.inject_doc_comment,
+ macro_bang: config.macro_bang,
+ },
tmp_file_id,
None,
)
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
index a81c4ee0c..f98310911 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs
@@ -49,7 +49,7 @@ pub enum HlMod {
Associated = 0,
/// Used with keywords like `async` and `await`.
Async,
- /// Used to differentiate individual elements within attributes.
+ /// Used to differentiate individual elements within attribute calls.
Attribute,
/// Callable item or value.
Callable,
@@ -72,6 +72,8 @@ pub enum HlMod {
IntraDocLink,
/// Used for items from other crates.
Library,
+ /// Used to differentiate individual elements within macro calls.
+ Macro,
/// Mutable binding.
Mutable,
/// Used for public items.
@@ -200,7 +202,7 @@ impl fmt::Display for HlTag {
}
impl HlMod {
- const ALL: &'static [HlMod; 19] = &[
+ const ALL: &'static [HlMod; HlMod::Unsafe as usize + 1] = &[
HlMod::Associated,
HlMod::Async,
HlMod::Attribute,
@@ -214,6 +216,7 @@ impl HlMod {
HlMod::Injected,
HlMod::IntraDocLink,
HlMod::Library,
+ HlMod::Macro,
HlMod::Mutable,
HlMod::Public,
HlMod::Reference,
@@ -237,6 +240,7 @@ impl HlMod {
HlMod::Injected => "injected",
HlMod::IntraDocLink => "intra_doc_link",
HlMod::Library => "library",
+ HlMod::Macro => "macro",
HlMod::Mutable => "mutable",
HlMod::Public => "public",
HlMod::Reference => "reference",
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index 18045f1f5..35f240d42 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -93,7 +93,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">foo</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="function injected">new</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
<span class="comment documentation">///</span>
<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="comment injected">// calls bar on foo</span>
- <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">assert</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
+ <span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">assert</span><span class="macro_bang injected">!</span><span class="parenthesis injected macro">(</span><span class="none injected macro">foo</span><span class="operator injected macro">.</span><span class="none injected macro">bar</span><span class="parenthesis injected macro">(</span><span class="parenthesis injected macro">)</span><span class="parenthesis injected macro">)</span><span class="semicolon injected">;</span>
<span class="comment documentation">///</span>
<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="logical injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span>
<span class="comment documentation">///</span>
@@ -145,7 +145,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="comment documentation">/// ```</span>
<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">macro_rules</span><span class="macro_bang injected">!</span><span class="none injected"> </span><span class="macro declaration injected">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="angle injected">&gt;</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr </span><span class="brace injected">}</span><span class="brace injected">}</span>
-<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
+<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="macro injected">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected macro">(</span><span class="numeric_literal injected macro">1</span><span class="parenthesis injected macro">)</span><span class="semicolon injected">;</span>
<span class="comment documentation">/// ```</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
@@ -165,7 +165,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="comment documentation">///</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
-<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="module injected">alloc</span><span class="operator injected">::</span><span class="macro injected">vec</span><span class="macro_bang injected">!</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
+<span class="comment documentation">///</span><span class="comment documentation"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="module injected">alloc</span><span class="operator injected">::</span><span class="macro injected">vec</span><span class="macro_bang injected">!</span><span class="bracket injected macro">[</span><span class="numeric_literal injected macro">1</span><span class="comma injected macro">,</span><span class="none injected"> </span><span class="numeric_literal injected macro">2</span><span class="comma injected macro">,</span><span class="none injected"> </span><span class="numeric_literal injected macro">3</span><span class="bracket injected macro">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
<span class="comment documentation">/// ```</span>
<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration public">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index af41796e2..87b9da46e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -43,5 +43,5 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
<pre><code><span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">std</span><span class="semicolon">;</span>
-<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root library">alloc</span> <span class="keyword">as</span> <span class="module crate_root declaration library">abc</span><span class="semicolon">;</span>
+<span class="keyword">extern</span> <span class="keyword">crate</span> <span class="module crate_root default_library library">alloc</span> <span class="keyword">as</span> <span class="module crate_root default_library declaration library">abc</span><span class="semicolon">;</span>
</code></pre> \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
index 9f2b1926b..6b049f379 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -178,7 +178,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">impl</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="angle">&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">T</span><span class="angle">&gt;</span> <span class="brace">{</span>
<span class="keyword">fn</span> <span class="function associated consuming declaration">and</span><span class="angle">&lt;</span><span class="type_param declaration">U</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="self_keyword declaration">self</span><span class="comma">,</span> <span class="value_param declaration">other</span><span class="colon">:</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="type_param">U</span><span class="angle">&gt;</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="enum">Option</span><span class="angle">&lt;</span><span class="parenthesis">(</span><span class="type_param">T</span><span class="comma">,</span> <span class="type_param">U</span><span class="parenthesis">)</span><span class="angle">&gt;</span> <span class="brace">{</span>
<span class="keyword control">match</span> <span class="value_param">other</span> <span class="brace">{</span>
- <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="unresolved_reference">unimplemented</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
+ <span class="enum_variant">None</span> <span class="operator">=&gt;</span> <span class="unresolved_reference">unimplemented</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma">,</span>
<span class="variable declaration">Nope</span> <span class="operator">=&gt;</span> <span class="variable">Nope</span><span class="comma">,</span>
<span class="brace">}</span>
<span class="brace">}</span>
@@ -192,7 +192,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword async">async</span> <span class="keyword">fn</span> <span class="function async declaration">async_main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="keyword">let</span> <span class="variable declaration">f1</span> <span class="operator">=</span> <span class="function async">learn_and_sing</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">f2</span> <span class="operator">=</span> <span class="unresolved_reference">dance</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="unresolved_reference">futures</span><span class="operator">::</span><span class="unresolved_reference">join</span><span class="macro_bang">!</span><span class="parenthesis">(</span>f1<span class="comma">,</span> f2<span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="unresolved_reference">futures</span><span class="operator">::</span><span class="unresolved_reference">join</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">f1</span><span class="comma macro">,</span> <span class="none macro">f2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">use_foo_items</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
@@ -204,7 +204,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">let</span> <span class="variable declaration">control_flow</span> <span class="operator">=</span> <span class="module crate_root library">foo</span><span class="operator">::</span><span class="function library">identity</span><span class="parenthesis">(</span><span class="module crate_root library">foo</span><span class="operator">::</span><span class="enum library">ControlFlow</span><span class="operator">::</span><span class="enum_variant library">Continue</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="keyword control">if</span> <span class="variable">control_flow</span><span class="operator">.</span><span class="function associated consuming library">should_die</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="module crate_root library">foo</span><span class="operator">::</span><span class="unresolved_reference">die</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="module crate_root library">foo</span><span class="operator">::</span><span class="unresolved_reference">die</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="brace">}</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index abcd80c28..d9c3db6fb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -45,18 +45,18 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="punctuation">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
-<span class="keyword">trait</span> <span class="trait declaration">Foo</span> <span class="brace">{</span>
- <span class="keyword">fn</span> <span class="function associated declaration static trait">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="unresolved_reference">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"2 + 2 = {}"</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="brace">}</span>
-<span class="brace">}</span><span class="string_literal">"#</span>
+ <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span><span class="none injected">
+</span><span class="keyword injected">trait</span><span class="none injected"> </span><span class="trait declaration injected">Foo</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+ </span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function associated declaration injected static trait">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+ </span><span class="unresolved_reference injected">println</span><span class="macro_bang injected">!</span><span class="parenthesis injected macro">(</span><span class="string_literal injected macro">"2 + 2 = {}"</span><span class="comma injected macro">,</span><span class="none injected"> </span><span class="numeric_literal injected macro">4</span><span class="parenthesis injected macro">)</span><span class="semicolon injected">;</span><span class="none injected">
+ </span><span class="brace injected">}</span><span class="none injected">
+</span><span class="brace injected">}</span><span class="string_literal">"#</span>
<span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r"</span>
-<span class="keyword">fn</span> <span class="function declaration">foo</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="function">foo</span><span class="parenthesis">(</span><span class="keyword">$0</span><span class="brace">{</span>
- <span class="numeric_literal">92</span>
- <span class="brace">}</span><span class="keyword">$0</span><span class="parenthesis">)</span>
-<span class="brace">}</span><span class="string_literal">"</span>
+ <span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r"</span><span class="none injected">
+</span><span class="keyword injected">fn</span><span class="none injected"> </span><span class="function declaration injected">foo</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected">
+ </span><span class="function injected">foo</span><span class="parenthesis injected">(</span><span class="keyword injected">$0</span><span class="brace injected">{</span><span class="none injected">
+ </span><span class="numeric_literal injected">92</span><span class="none injected">
+ </span><span class="brace injected">}</span><span class="keyword injected">$0</span><span class="parenthesis injected">)</span><span class="none injected">
+</span><span class="brace injected">}</span><span class="string_literal">"</span>
<span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
index 66f9ede96..3900959be 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords.html
@@ -53,6 +53,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span>
-<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="keyword macro">Self</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span>
<span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
index 54d427952..2cbbf6964 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -42,21 +42,21 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
-<pre><code><span class="module crate_root library">proc_macros</span><span class="operator">::</span><span class="macro library">mirror</span><span class="macro_bang">!</span> <span class="brace">{</span>
- <span class="brace">{</span>
- <span class="comma">,</span><span class="builtin_type">i32</span> <span class="colon">:</span><span class="field declaration public">x</span> <span class="keyword">pub</span>
- <span class="comma">,</span><span class="builtin_type">i32</span> <span class="colon">:</span><span class="field declaration public">y</span> <span class="keyword">pub</span>
- <span class="brace">}</span> <span class="struct declaration">Foo</span> <span class="keyword">struct</span>
-<span class="brace">}</span>
+<pre><code><span class="module crate_root library">proc_macros</span><span class="operator">::</span><span class="macro library">mirror</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
+ <span class="brace macro">{</span>
+ <span class="comma macro">,</span><span class="builtin_type macro">i32</span> <span class="colon macro">:</span><span class="field declaration macro public">x</span> <span class="keyword macro">pub</span>
+ <span class="comma macro">,</span><span class="builtin_type macro">i32</span> <span class="colon macro">:</span><span class="field declaration macro public">y</span> <span class="keyword macro">pub</span>
+ <span class="brace macro">}</span> <span class="struct declaration macro">Foo</span> <span class="keyword macro">struct</span>
+<span class="brace macro">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">def_fn</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="brace">}</span>
<span class="brace">}</span>
-<span class="macro">def_fn</span><span class="macro_bang">!</span> <span class="brace">{</span>
- <span class="keyword">fn</span> <span class="function declaration">bar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-</span><span class="operator">&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
- <span class="numeric_literal">100</span>
- <span class="brace">}</span>
-<span class="brace">}</span>
+<span class="macro">def_fn</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
+ <span class="keyword macro">fn</span> <span class="function declaration macro">bar</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="operator macro">-</span><span class="operator macro">&gt;</span> <span class="builtin_type macro">u32</span> <span class="brace macro">{</span>
+ <span class="numeric_literal macro">100</span>
+ <span class="brace macro">}</span>
+<span class="brace macro">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">dont_color_me_braces</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="numeric_literal">0</span><span class="brace">}</span>
@@ -90,7 +90,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="unresolved_reference">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, {}!"</span><span class="comma">,</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="unresolved_reference">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, {}!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro">noop</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index a626cda3f..fa374b04f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -86,6 +86,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">assert</span> <span class="brace">{</span><span class="brace">}</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">asm</span> <span class="brace">{</span><span class="brace">}</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
+<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">concat</span> <span class="brace">{</span><span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
@@ -93,72 +95,84 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal">Hello</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\n</span><span class="char_literal">'</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\t</span><span class="char_literal">'</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'\e'</span><span class="semicolon">;</span> <span class="comment">// invalid escape</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'e'</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">' '</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\u{48}</span><span class="char_literal">'</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\u{4823}</span><span class="char_literal">'</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\x65</span><span class="char_literal">'</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\x00</span><span class="char_literal">'</span><span class="semicolon">;</span>
+
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello, world!"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "The number is 1"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="parenthesis">(</span><span class="numeric_literal">3</span><span class="comma">,</span> <span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "(3, 4)"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> value<span class="operator">=</span><span class="numeric_literal">4</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "4"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "1 2"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">42</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1 1 2"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> argument <span class="operator">=</span> <span class="string_literal">"test"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "test"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="comma">,</span> name <span class="operator">=</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> a<span class="operator">=</span><span class="string_literal">"a"</span><span class="comma">,</span> b<span class="operator">=</span><span class="char_literal">'b'</span><span class="comma">,</span> c<span class="operator">=</span><span class="numeric_literal">3</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "a 3 b"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "{2}"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> width <span class="operator">=</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">-</span><span class="numeric_literal">5</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="numeric_literal">27</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> <span class="numeric_literal">5</span><span class="comma">,</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="string_literal">"x"</span><span class="comma">,</span> prec <span class="operator">=</span> <span class="numeric_literal">5</span><span class="comma">,</span> number <span class="operator">=</span> <span class="numeric_literal">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 fractional digits"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="numeric_literal">1234.56</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal">` has 3 right-aligned characters"</span><span class="comma">,</span> <span class="string_literal">"Hello"</span><span class="comma">,</span> <span class="numeric_literal">3</span><span class="comma">,</span> name<span class="operator">=</span><span class="string_literal">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello, world!"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "The number is 1"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "(3, 4)"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "4"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "1 2"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1 1 2"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "test"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="none macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="none macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="none macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "a 3 b"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "{2}"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="none macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal"> Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal">Hello</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal">Hello </span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">{{</span><span class="string_literal"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span><span class="comma">,</span> <span class="string_literal">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="comment">// escape sequences</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal">World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal"> World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal macro">World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal macro"> World"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">b"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">r"\\"</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> A <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> "</span><span class="comma">,</span> <span class="unresolved_reference">thingy</span><span class="comma">,</span> <span class="unresolved_reference">n2</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="bool_literal">true</span><span class="comma">,</span> <span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="bool_literal">true</span><span class="comma">,</span> <span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal"> asdasd"</span><span class="comma">,</span> <span class="numeric_literal">1</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">fmt"</span><span class="comma">,</span> <span class="numeric_literal">0</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"mov eax, </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span>concat<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="parenthesis">)</span><span class="comma">,</span> <span class="string_literal">"{}"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"mov eax, </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">concat</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">format_args</span><span class="operator macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 1992bdc6a..654d51b8a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -89,13 +89,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">let</span> <span class="variable declaration">x</span> <span class="operator">=</span> <span class="operator">&</span><span class="numeric_literal">5</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="punctuation">_</span> <span class="keyword">as</span> <span class="keyword">*</span><span class="keyword">const</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">u</span> <span class="operator">=</span> <span class="union">Union</span> <span class="brace">{</span> <span class="field">b</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
- <span class="macro">id</span><span class="macro_bang">!</span> <span class="brace">{</span>
- <span class="keyword unsafe">unsafe</span> <span class="brace">{</span> <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">}</span>
- <span class="brace">}</span><span class="semicolon">;</span>
+ <span class="macro">id</span><span class="macro_bang">!</span> <span class="brace macro">{</span>
+ <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span>
+ <span class="brace macro">}</span><span class="semicolon">;</span>
<span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
- <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="macro unsafe">id</span><span class="macro_bang">!</span> <span class="brace">{</span> <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">}</span><span class="semicolon">;</span>
+ <span class="macro unsafe">unsafe_deref</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro unsafe">id</span><span class="macro_bang">!</span> <span class="brace macro">{</span> <span class="macro macro unsafe">unsafe_deref</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="brace macro">}</span><span class="semicolon">;</span>
<span class="comment">// unsafe fn and method calls</span>
<span class="function unsafe">unsafe_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index ac9bd8e39..497992f68 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -432,6 +432,8 @@ macro_rules! panic {}
macro_rules! assert {}
#[rustc_builtin_macro]
macro_rules! asm {}
+#[rustc_builtin_macro]
+macro_rules! concat {}
macro_rules! toho {
() => ($crate::panic!("not yet implemented"));
@@ -439,6 +441,16 @@ macro_rules! toho {
}
fn main() {
+ let a = '\n';
+ let a = '\t';
+ let a = '\e'; // invalid escape
+ let a = 'e';
+ let a = ' ';
+ let a = '\u{48}';
+ let a = '\u{4823}';
+ let a = '\x65';
+ let a = '\x00';
+
println!("Hello {{Hello}}");
// from https://doc.rust-lang.org/std/fmt/index.html
println!("Hello"); // => "Hello"
@@ -495,6 +507,7 @@ fn main() {
let _ = "\x28\x28\x00\x63\n";
let _ = b"\x28\x28\x00\x63\n";
+ let _ = r"\\";
println!("{\x41}", A = 92);
println!("{ничоси}", ничоси = 92);
@@ -507,6 +520,7 @@ fn main() {
toho!("{}fmt", 0);
asm!("mov eax, {0}");
format_args!(concat!("{}"), "{}");
+ format_args!("{}", format_args!("{}", 0));
}"#,
expect_file!["./test_data/highlight_strings.html"],
false,
@@ -1126,5 +1140,5 @@ fn benchmark_syntax_highlighting_parser() {
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
.count()
};
- assert_eq!(hash, 1608);
+ assert_eq!(hash, 1169);
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
index bb6827e8a..df1971242 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_tree.rs
@@ -1,5 +1,7 @@
-use ide_db::base_db::{FileId, SourceDatabase};
-use ide_db::RootDatabase;
+use ide_db::{
+ base_db::{FileId, SourceDatabase},
+ RootDatabase,
+};
use syntax::{
AstNode, NodeOrToken, SourceFile, SyntaxKind::STRING, SyntaxToken, TextRange, TextSize,
};
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
index 17a1e385b..8c84461f6 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
@@ -1,11 +1,9 @@
-use std::sync::Arc;
-
use dot::{Id, LabelText};
use ide_db::{
base_db::{CrateGraph, CrateId, Dependency, SourceDatabase, SourceDatabaseExt},
- RootDatabase,
+ FxHashSet, RootDatabase,
};
-use stdx::hash::NoHashHashSet;
+use triomphe::Arc;
// Feature: View Crate Graph
//
@@ -42,7 +40,7 @@ pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String,
struct DotCrateGraph {
graph: Arc<CrateGraph>,
- crates_to_render: NoHashHashSet<CrateId>,
+ crates_to_render: FxHashSet<CrateId>,
}
type Edge<'a> = (CrateId, &'a Dependency);
@@ -80,7 +78,7 @@ impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph {
}
fn node_id(&'a self, n: &CrateId) -> Id<'a> {
- Id::new(format!("_{}", n.0)).unwrap()
+ Id::new(format!("_{}", u32::from(n.into_raw()))).unwrap()
}
fn node_shape(&'a self, _node: &CrateId) -> Option<LabelText<'a>> {
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
index 9c1f93356..e072df430 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs
@@ -12,5 +12,5 @@ use ide_db::RootDatabase;
// | VS Code | **rust-analyzer: Debug ItemTree**
// |===
pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String {
- db.file_item_tree(file_id.into()).pretty_print()
+ db.file_item_tree(file_id.into()).pretty_print(db)
}
diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml
index c73c368a1..dcd0d7881 100644
--- a/src/tools/rust-analyzer/crates/intern/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml
@@ -18,3 +18,4 @@ dashmap = { version = "=5.4.0", features = ["raw-api"] }
hashbrown = { version = "0.12.1", default-features = false }
once_cell = "1.17.0"
rustc-hash = "1.1.0"
+triomphe.workspace = true
diff --git a/src/tools/rust-analyzer/crates/intern/src/lib.rs b/src/tools/rust-analyzer/crates/intern/src/lib.rs
index fb2903696..dabbf3a38 100644
--- a/src/tools/rust-analyzer/crates/intern/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/lib.rs
@@ -6,13 +6,13 @@ use std::{
fmt::{self, Debug, Display},
hash::{BuildHasherDefault, Hash, Hasher},
ops::Deref,
- sync::Arc,
};
use dashmap::{DashMap, SharedValue};
-use hashbrown::HashMap;
+use hashbrown::{hash_map::RawEntryMut, HashMap};
use once_cell::sync::OnceCell;
use rustc_hash::FxHasher;
+use triomphe::Arc;
type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>;
type Guard<T> = dashmap::RwLockWriteGuard<
@@ -26,56 +26,58 @@ pub struct Interned<T: Internable + ?Sized> {
impl<T: Internable> Interned<T> {
pub fn new(obj: T) -> Self {
- match Interned::lookup(&obj) {
- Ok(this) => this,
- Err(shard) => {
- let arc = Arc::new(obj);
- Self::alloc(arc, shard)
- }
+ let (mut shard, hash) = Self::select(&obj);
+ // Atomically,
+ // - check if `obj` is already in the map
+ // - if so, clone its `Arc` and return it
+ // - if not, box it up, insert it, and return a clone
+ // This needs to be atomic (locking the shard) to avoid races with other thread, which could
+ // insert the same object between us looking it up and inserting it.
+ match shard.raw_entry_mut().from_key_hashed_nocheck(hash as u64, &obj) {
+ RawEntryMut::Occupied(occ) => Self { arc: occ.key().clone() },
+ RawEntryMut::Vacant(vac) => Self {
+ arc: vac
+ .insert_hashed_nocheck(hash as u64, Arc::new(obj), SharedValue::new(()))
+ .0
+ .clone(),
+ },
}
}
}
-impl<T: Internable + ?Sized> Interned<T> {
- fn lookup(obj: &T) -> Result<Self, Guard<T>> {
- let storage = T::storage().get();
- let shard_idx = storage.determine_map(obj);
- let shard = &storage.shards()[shard_idx];
- let shard = shard.write();
-
+impl Interned<str> {
+ pub fn new_str(s: &str) -> Self {
+ let (mut shard, hash) = Self::select(s);
// Atomically,
// - check if `obj` is already in the map
// - if so, clone its `Arc` and return it
// - if not, box it up, insert it, and return a clone
// This needs to be atomic (locking the shard) to avoid races with other thread, which could
// insert the same object between us looking it up and inserting it.
-
- // FIXME: avoid double lookup/hashing by using raw entry API (once stable, or when
- // hashbrown can be plugged into dashmap)
- match shard.get_key_value(obj) {
- Some((arc, _)) => Ok(Self { arc: arc.clone() }),
- None => Err(shard),
+ match shard.raw_entry_mut().from_key_hashed_nocheck(hash as u64, s) {
+ RawEntryMut::Occupied(occ) => Self { arc: occ.key().clone() },
+ RawEntryMut::Vacant(vac) => Self {
+ arc: vac
+ .insert_hashed_nocheck(hash as u64, Arc::from(s), SharedValue::new(()))
+ .0
+ .clone(),
+ },
}
}
-
- fn alloc(arc: Arc<T>, mut shard: Guard<T>) -> Self {
- let arc2 = arc.clone();
-
- shard.insert(arc2, SharedValue::new(()));
-
- Self { arc }
- }
}
-impl Interned<str> {
- pub fn new_str(s: &str) -> Self {
- match Interned::lookup(s) {
- Ok(this) => this,
- Err(shard) => {
- let arc = Arc::<str>::from(s);
- Self::alloc(arc, shard)
- }
- }
+impl<T: Internable + ?Sized> Interned<T> {
+ #[inline]
+ fn select(obj: &T) -> (Guard<T>, u64) {
+ let storage = T::storage().get();
+ let hash = {
+ let mut hasher = std::hash::BuildHasher::build_hasher(storage.hasher());
+ obj.hash(&mut hasher);
+ hasher.finish()
+ };
+ let shard_idx = storage.determine_shard(hash as usize);
+ let shard = &storage.shards()[shard_idx];
+ (shard.write(), hash)
}
}
@@ -83,7 +85,7 @@ impl<T: Internable + ?Sized> Drop for Interned<T> {
#[inline]
fn drop(&mut self) {
// When the last `Ref` is dropped, remove the object from the global map.
- if Arc::strong_count(&self.arc) == 2 {
+ if Arc::count(&self.arc) == 2 {
// Only `self` and the global map point to the object.
self.drop_slow();
@@ -94,20 +96,17 @@ impl<T: Internable + ?Sized> Drop for Interned<T> {
impl<T: Internable + ?Sized> Interned<T> {
#[cold]
fn drop_slow(&mut self) {
- let storage = T::storage().get();
- let shard_idx = storage.determine_map(&self.arc);
- let shard = &storage.shards()[shard_idx];
- let mut shard = shard.write();
-
- // FIXME: avoid double lookup
- let (arc, _) = shard.get_key_value(&self.arc).expect("interned value removed prematurely");
+ let (mut shard, hash) = Self::select(&self.arc);
- if Arc::strong_count(arc) != 2 {
+ if Arc::count(&self.arc) != 2 {
// Another thread has interned another copy
return;
}
- shard.remove(&self.arc);
+ match shard.raw_entry_mut().from_key_hashed_nocheck(hash, &self.arc) {
+ RawEntryMut::Occupied(occ) => occ.remove(),
+ RawEntryMut::Vacant(_) => unreachable!(),
+ };
// Shrink the backing storage if the shard is less than 50% occupied.
if shard.len() * 2 < shard.capacity() {
diff --git a/src/tools/rust-analyzer/crates/limit/src/lib.rs b/src/tools/rust-analyzer/crates/limit/src/lib.rs
index 6b2534aa4..7fb4b513a 100644
--- a/src/tools/rust-analyzer/crates/limit/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/limit/src/lib.rs
@@ -6,6 +6,7 @@
use std::sync::atomic::AtomicUsize;
/// Represents a struct used to enforce a numerical limit.
+#[derive(Debug)]
pub struct Limit {
upper_bound: usize,
#[cfg(feature = "tracking")]
diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
index 894355fcb..d28dd17de 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
@@ -20,7 +20,10 @@ fn benchmark_parse_macro_rules() {
let rules = macro_rules_fixtures_tt();
let hash: usize = {
let _pt = bench("mbe parse macro rules");
- rules.values().map(|it| DeclarativeMacro::parse_macro_rules(it).unwrap().rules.len()).sum()
+ rules
+ .values()
+ .map(|it| DeclarativeMacro::parse_macro_rules(it, true).unwrap().rules.len())
+ .sum()
};
assert_eq!(hash, 1144);
}
@@ -50,7 +53,7 @@ fn benchmark_expand_macro_rules() {
fn macro_rules_fixtures() -> FxHashMap<String, DeclarativeMacro> {
macro_rules_fixtures_tt()
.into_iter()
- .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt).unwrap()))
+ .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true).unwrap()))
.collect()
}
@@ -76,7 +79,7 @@ fn invocation_fixtures(rules: &FxHashMap<String, DeclarativeMacro>) -> Vec<(Stri
let mut res = Vec::new();
for (name, it) in rules {
- for rule in &it.rules {
+ for rule in it.rules.iter() {
// Generate twice
for _ in 0..2 {
// The input are generated by filling the `Op` randomly.
@@ -108,7 +111,7 @@ fn invocation_fixtures(rules: &FxHashMap<String, DeclarativeMacro>) -> Vec<(Stri
}
try_cnt += 1;
if try_cnt > 100 {
- panic!("invocaton fixture {name} cannot be generated.\n");
+ panic!("invocation fixture {name} cannot be generated.\n");
}
}
}
@@ -192,10 +195,10 @@ fn invocation_fixtures(rules: &FxHashMap<String, DeclarativeMacro>) -> Vec<(Stri
});
parent.token_trees.push(subtree.into());
}
- Op::Ignore { .. } | Op::Index { .. } => {}
+ Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {}
};
- // Simple linear congruential generator for determistic result
+ // Simple linear congruential generator for deterministic result
fn rand(seed: &mut usize) -> usize {
let a = 1664525;
let c = 1013904223;
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs
index 7537dc322..8e2181e97 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs
@@ -13,10 +13,11 @@ use crate::{parser::MetaVarKind, tt, ExpandError, ExpandResult};
pub(crate) fn expand_rules(
rules: &[crate::Rule],
input: &tt::Subtree,
+ is_2021: bool,
) -> ExpandResult<tt::Subtree> {
let mut match_: Option<(matcher::Match, &crate::Rule)> = None;
for rule in rules {
- let new_match = matcher::match_(&rule.lhs, input);
+ let new_match = matcher::match_(&rule.lhs, input, is_2021);
if new_match.err.is_none() {
// If we find a rule that applies without errors, we're done.
@@ -45,7 +46,7 @@ pub(crate) fn expand_rules(
transcriber::transcribe(&rule.rhs, &match_.bindings);
ExpandResult { value, err: match_.err.or(transcribe_err) }
} else {
- ExpandResult::with_err(
+ ExpandResult::new(
tt::Subtree { delimiter: tt::Delimiter::unspecified(), token_trees: vec![] },
ExpandError::NoMatchingRule,
)
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
index f4ea9e5c8..474826079 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
@@ -111,8 +111,8 @@ impl Match {
}
/// Matching errors are added to the `Match`.
-pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match {
- let mut res = match_loop(pattern, input);
+pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree, is_2021: bool) -> Match {
+ let mut res = match_loop(pattern, input, is_2021);
res.bound_count = count(res.bindings.bindings());
return res;
@@ -332,7 +332,7 @@ struct MatchState<'t> {
/// Cached result of meta variable parsing
meta_result: Option<(TtIter<'t>, ExpandResult<Option<Fragment>>)>,
- /// Is error occuried in this state, will `poised` to "parent"
+ /// Is error occurred in this state, will `poised` to "parent"
is_error: bool,
}
@@ -354,6 +354,7 @@ struct MatchState<'t> {
/// - `eof_items`: the set of items that would be valid if this was the EOF.
/// - `bb_items`: the set of items that are waiting for the black-box parser.
/// - `error_items`: the set of items in errors, used for error-resilient parsing
+#[inline]
fn match_loop_inner<'t>(
src: TtIter<'t>,
stack: &[TtIter<'t>],
@@ -364,6 +365,7 @@ fn match_loop_inner<'t>(
next_items: &mut Vec<MatchState<'t>>,
eof_items: &mut SmallVec<[MatchState<'t>; 1]>,
error_items: &mut SmallVec<[MatchState<'t>; 1]>,
+ is_2021: bool,
) {
macro_rules! try_push {
($items: expr, $it:expr) => {
@@ -474,7 +476,7 @@ fn match_loop_inner<'t>(
OpDelimited::Op(Op::Var { kind, name, .. }) => {
if let &Some(kind) = kind {
let mut fork = src.clone();
- let match_res = match_meta_var(kind, &mut fork);
+ let match_res = match_meta_var(kind, &mut fork, is_2021);
match match_res.err {
None => {
// Some meta variables are optional (e.g. vis)
@@ -565,7 +567,9 @@ fn match_loop_inner<'t>(
item.is_error = true;
error_items.push(item);
}
- OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. }) => {}
+ OpDelimited::Op(Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. }) => {
+ stdx::never!("metavariable expression in lhs found");
+ }
OpDelimited::Open => {
if matches!(src.peek_n(0), Some(tt::TokenTree::Subtree(..))) {
item.dot.next();
@@ -583,7 +587,7 @@ fn match_loop_inner<'t>(
}
}
-fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
+fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: bool) -> Match {
let mut src = TtIter::new(src);
let mut stack: SmallVec<[TtIter<'_>; 1]> = SmallVec::new();
let mut res = Match::default();
@@ -622,6 +626,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
&mut next_items,
&mut eof_items,
&mut error_items,
+ is_2021,
);
stdx::always!(cur_items.is_empty());
@@ -731,14 +736,17 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match {
}
}
-fn match_meta_var(kind: MetaVarKind, input: &mut TtIter<'_>) -> ExpandResult<Option<Fragment>> {
+fn match_meta_var(
+ kind: MetaVarKind,
+ input: &mut TtIter<'_>,
+ is_2021: bool,
+) -> ExpandResult<Option<Fragment>> {
let fragment = match kind {
MetaVarKind::Path => parser::PrefixEntryPoint::Path,
MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
- // FIXME: These two should actually behave differently depending on the edition.
- //
- // https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html
- MetaVarKind::Pat | MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
+ MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop,
+ MetaVarKind::Pat => parser::PrefixEntryPoint::Pat,
+ MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt,
MetaVarKind::Block => parser::PrefixEntryPoint::Block,
MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem,
@@ -805,7 +813,9 @@ fn collect_vars(collector_fun: &mut impl FnMut(SmolStr), pattern: &MetaTemplate)
Op::Var { name, .. } => collector_fun(name.clone()),
Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens),
Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens),
- Op::Ignore { .. } | Op::Index { .. } | Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {
+ Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {}
+ Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } => {
+ stdx::never!("metavariable expression in lhs found");
}
}
}
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
index dffb40d4b..6161af185 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
@@ -7,7 +7,7 @@ use crate::{
expander::{Binding, Bindings, Fragment},
parser::{MetaVarKind, Op, RepeatKind, Separator},
tt::{self, Delimiter},
- ExpandError, ExpandResult, MetaTemplate,
+ CountError, ExpandError, ExpandResult, MetaTemplate,
};
impl Bindings {
@@ -15,13 +15,23 @@ impl Bindings {
self.inner.contains_key(name)
}
- fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<Fragment, ExpandError> {
+ fn get(&self, name: &str) -> Result<&Binding, ExpandError> {
+ match self.inner.get(name) {
+ Some(binding) => Ok(binding),
+ None => Err(ExpandError::binding_error(format!("could not find binding `{name}`"))),
+ }
+ }
+
+ fn get_fragment(
+ &self,
+ name: &str,
+ nesting: &mut [NestingState],
+ ) -> Result<Fragment, ExpandError> {
macro_rules! binding_err {
($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) };
}
- let mut b: &Binding =
- self.inner.get(name).ok_or_else(|| binding_err!("could not find binding `{name}`"))?;
+ let mut b = self.get(name)?;
for nesting_state in nesting.iter_mut() {
nesting_state.hit = true;
b = match b {
@@ -133,7 +143,7 @@ fn expand_subtree(
// remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation
let start_elements = arena.len();
let mut err = None;
- for op in template.iter() {
+ 'ops: for op in template.iter() {
match op {
Op::Literal(it) => arena.push(tt::Leaf::from(it.clone()).into()),
Op::Ident(it) => arena.push(tt::Leaf::from(it.clone()).into()),
@@ -161,13 +171,12 @@ fn expand_subtree(
}
Op::Ignore { name, id } => {
// Expand the variable, but ignore the result. This registers the repetition count.
+ // FIXME: Any emitted errors are dropped.
expand_var(ctx, name, *id);
}
Op::Index { depth } => {
- let index = ctx
- .nesting
- .get(ctx.nesting.len() - 1 - (*depth as usize))
- .map_or(0, |nest| nest.idx);
+ let index =
+ ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx);
arena.push(
tt::Leaf::Literal(tt::Literal {
text: index.to_string().into(),
@@ -176,6 +185,65 @@ fn expand_subtree(
.into(),
);
}
+ Op::Count { name, depth } => {
+ let mut binding = match ctx.bindings.get(name.as_str()) {
+ Ok(b) => b,
+ Err(e) => {
+ if err.is_none() {
+ err = Some(e);
+ }
+ continue;
+ }
+ };
+ for state in ctx.nesting.iter_mut() {
+ state.hit = true;
+ match binding {
+ Binding::Fragment(_) | Binding::Missing(_) => {
+ // `count()` will report an error.
+ break;
+ }
+ Binding::Nested(bs) => {
+ if let Some(b) = bs.get(state.idx) {
+ binding = b;
+ } else {
+ state.at_end = true;
+ continue 'ops;
+ }
+ }
+ Binding::Empty => {
+ state.at_end = true;
+ // FIXME: Breaking here and proceeding to `count()` isn't the most
+ // correct thing to do here. This could be a binding of some named
+ // fragment which we don't know the depth of, so `count()` will just
+ // return 0 for this no matter what `depth` is. See test
+ // `count_interaction_with_empty_binding` for example.
+ break;
+ }
+ }
+ }
+
+ let c = match count(ctx, binding, 0, *depth) {
+ Ok(c) => c,
+ Err(e) => {
+ // XXX: It *might* make sense to emit a dummy integer value like `0` here.
+ // That would type inference a bit more robust in cases like
+ // `v[${count(t)}]` where index doesn't matter, but also coult also lead to
+ // wrong infefrence for cases like `tup.${count(t)}` where index itself
+ // does matter.
+ if err.is_none() {
+ err = Some(e.into());
+ }
+ continue;
+ }
+ };
+ arena.push(
+ tt::Leaf::Literal(tt::Literal {
+ text: c.to_string().into(),
+ span: tt::TokenId::unspecified(),
+ })
+ .into(),
+ );
+ }
}
}
// drain the elements added in this instance of expand_subtree
@@ -218,12 +286,9 @@ fn expand_var(ctx: &mut ExpandCtx<'_>, v: &SmolStr, id: tt::TokenId) -> ExpandRe
.into();
ExpandResult::ok(Fragment::Tokens(tt))
} else {
- ctx.bindings.get(v, &mut ctx.nesting).map_or_else(
+ ctx.bindings.get_fragment(v, &mut ctx.nesting).map_or_else(
|e| ExpandResult {
- value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree {
- delimiter: tt::Delimiter::unspecified(),
- token_trees: vec![],
- })),
+ value: Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree::empty())),
err: Some(e),
},
ExpandResult::ok,
@@ -245,6 +310,7 @@ fn expand_repeat(
let limit = 65536;
let mut has_seps = 0;
let mut counter = 0;
+ let mut err = None;
loop {
let ExpandResult { value: mut t, err: e } = expand_subtree(ctx, template, None, arena);
@@ -272,6 +338,7 @@ fn expand_repeat(
}
if e.is_some() {
+ err = err.or(e);
continue;
}
@@ -317,7 +384,7 @@ fn expand_repeat(
err: Some(ExpandError::UnexpectedToken),
};
}
- ExpandResult::ok(Fragment::Tokens(tt))
+ ExpandResult { value: Fragment::Tokens(tt), err }
}
fn push_fragment(buf: &mut Vec<tt::TokenTree>, fragment: Fragment) {
@@ -343,3 +410,34 @@ fn push_subtree(buf: &mut Vec<tt::TokenTree>, tt: tt::Subtree) {
_ => buf.push(tt.into()),
}
}
+
+/// Handles `${count(t, depth)}`. `our_depth` is the recursion depth and `count_depth` is the depth
+/// defined by the metavar expression.
+fn count(
+ ctx: &ExpandCtx<'_>,
+ binding: &Binding,
+ our_depth: usize,
+ count_depth: Option<usize>,
+) -> Result<usize, CountError> {
+ match binding {
+ Binding::Nested(bs) => match count_depth {
+ None => bs.iter().map(|b| count(ctx, b, our_depth + 1, None)).sum(),
+ Some(0) => Ok(bs.len()),
+ Some(d) => bs.iter().map(|b| count(ctx, b, our_depth + 1, Some(d - 1))).sum(),
+ },
+ Binding::Empty => Ok(0),
+ Binding::Fragment(_) | Binding::Missing(_) => {
+ if our_depth == 0 {
+ // `${count(t)}` is placed inside the innermost repetition. This includes cases
+ // where `t` is not a repeated fragment.
+ Err(CountError::Misplaced)
+ } else if count_depth.is_none() {
+ Ok(1)
+ } else {
+ // We've reached at the innermost repeated fragment, but the user wants us to go
+ // further!
+ Err(CountError::OutOfBounds)
+ }
+ }
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
index ac107a0d6..5ef20ff8a 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
@@ -19,6 +19,7 @@ mod benchmark;
mod token_map;
use ::tt::token_id as tt;
+use stdx::impl_from;
use std::fmt;
@@ -69,7 +70,7 @@ impl fmt::Display for ParseError {
}
}
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub enum ExpandError {
BindingError(Box<Box<str>>),
LeftoverTokens,
@@ -77,8 +78,11 @@ pub enum ExpandError {
LimitExceeded,
NoMatchingRule,
UnexpectedToken,
+ CountError(CountError),
}
+impl_from!(CountError for ExpandError);
+
impl ExpandError {
fn binding_error(e: impl Into<Box<str>>) -> ExpandError {
ExpandError::BindingError(Box::new(e.into()))
@@ -94,6 +98,23 @@ impl fmt::Display for ExpandError {
ExpandError::ConversionError => f.write_str("could not convert tokens"),
ExpandError::LimitExceeded => f.write_str("Expand exceed limit"),
ExpandError::LeftoverTokens => f.write_str("leftover tokens"),
+ ExpandError::CountError(e) => e.fmt(f),
+ }
+ }
+}
+
+// FIXME: Showing these errors could be nicer.
+#[derive(Debug, PartialEq, Eq, Clone, Hash)]
+pub enum CountError {
+ OutOfBounds,
+ Misplaced,
+}
+
+impl fmt::Display for CountError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ CountError::OutOfBounds => f.write_str("${count} out of bounds"),
+ CountError::Misplaced => f.write_str("${count} misplaced"),
}
}
}
@@ -104,9 +125,12 @@ impl fmt::Display for ExpandError {
/// and `$()*` have special meaning (see `Var` and `Repeat` data structures)
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DeclarativeMacro {
- rules: Vec<Rule>,
+ rules: Box<[Rule]>,
/// Highest id of the token we have in TokenMap
shift: Shift,
+ // This is used for correctly determining the behavior of the pat fragment
+ // FIXME: This should be tracked by hygiene of the fragment identifier!
+ is_2021: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -190,7 +214,10 @@ pub enum Origin {
impl DeclarativeMacro {
/// The old, `macro_rules! m {}` flavor.
- pub fn parse_macro_rules(tt: &tt::Subtree) -> Result<DeclarativeMacro, ParseError> {
+ pub fn parse_macro_rules(
+ tt: &tt::Subtree,
+ is_2021: bool,
+ ) -> Result<DeclarativeMacro, ParseError> {
// Note: this parsing can be implemented using mbe machinery itself, by
// matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing
// manually seems easier.
@@ -211,11 +238,11 @@ impl DeclarativeMacro {
validate(lhs)?;
}
- Ok(DeclarativeMacro { rules, shift: Shift::new(tt) })
+ Ok(DeclarativeMacro { rules: rules.into_boxed_slice(), shift: Shift::new(tt), is_2021 })
}
/// The new, unstable `macro m {}` flavor.
- pub fn parse_macro2(tt: &tt::Subtree) -> Result<DeclarativeMacro, ParseError> {
+ pub fn parse_macro2(tt: &tt::Subtree, is_2021: bool) -> Result<DeclarativeMacro, ParseError> {
let mut src = TtIter::new(tt);
let mut rules = Vec::new();
@@ -244,14 +271,14 @@ impl DeclarativeMacro {
validate(lhs)?;
}
- Ok(DeclarativeMacro { rules, shift: Shift::new(tt) })
+ Ok(DeclarativeMacro { rules: rules.into_boxed_slice(), shift: Shift::new(tt), is_2021 })
}
pub fn expand(&self, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> {
// apply shift
let mut tt = tt.clone();
self.shift.shift_all(&mut tt);
- expander::expand_rules(&self.rules, &tt)
+ expander::expand_rules(&self.rules, &tt, self.is_2021)
}
pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
@@ -324,12 +351,12 @@ pub struct ValueResult<T, E> {
}
impl<T, E> ValueResult<T, E> {
- pub fn ok(value: T) -> Self {
- Self { value, err: None }
+ pub fn new(value: T, err: E) -> Self {
+ Self { value, err: Some(err) }
}
- pub fn with_err(value: T, err: E) -> Self {
- Self { value, err: Some(err) }
+ pub fn ok(value: T) -> Self {
+ Self { value, err: None }
}
pub fn only_err(err: E) -> Self
diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs
index fd3d64719..7a143e746 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs
@@ -20,7 +20,7 @@ use crate::{tt, tt_iter::TtIter, ParseError};
/// Stuff to the right is a [`MetaTemplate`] template which is used to produce
/// output.
#[derive(Clone, Debug, PartialEq, Eq)]
-pub(crate) struct MetaTemplate(pub(crate) Vec<Op>);
+pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>);
impl MetaTemplate {
pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Result<MetaTemplate, ParseError> {
@@ -44,7 +44,7 @@ impl MetaTemplate {
res.push(op);
}
- Ok(MetaTemplate(res))
+ Ok(MetaTemplate(res.into_boxed_slice()))
}
}
@@ -52,7 +52,8 @@ impl MetaTemplate {
pub(crate) enum Op {
Var { name: SmolStr, kind: Option<MetaVarKind>, id: tt::TokenId },
Ignore { name: SmolStr, id: tt::TokenId },
- Index { depth: u32 },
+ Index { depth: usize },
+ Count { name: SmolStr, depth: Option<usize> },
Repeat { tokens: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
Subtree { tokens: MetaTemplate, delimiter: tt::Delimiter },
Literal(tt::Literal),
@@ -295,9 +296,13 @@ fn parse_metavar_expr(src: &mut TtIter<'_>) -> Result<Op, ()> {
let ident = args.expect_ident()?;
Op::Ignore { name: ident.text.clone(), id: ident.span }
}
- "index" => {
- let depth = if args.len() == 0 { 0 } else { args.expect_u32_literal()? };
- Op::Index { depth }
+ "index" => Op::Index { depth: parse_depth(&mut args)? },
+ "count" => {
+ let ident = args.expect_ident()?;
+ // `${count(t)}` and `${count(t,)}` have different meanings. Not sure if this is a bug
+ // but that's how it's implemented in rustc as of this writing. See rust-lang/rust#111904.
+ let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None };
+ Op::Count { name: ident.text.clone(), depth }
}
_ => return Err(()),
};
@@ -308,3 +313,22 @@ fn parse_metavar_expr(src: &mut TtIter<'_>) -> Result<Op, ()> {
Ok(op)
}
+
+fn parse_depth(src: &mut TtIter<'_>) -> Result<usize, ()> {
+ if src.len() == 0 {
+ Ok(0)
+ } else if let tt::Leaf::Literal(lit) = src.expect_literal()? {
+ // Suffixes are not allowed.
+ lit.text.parse().map_err(|_| ())
+ } else {
+ Err(())
+ }
+}
+
+fn try_eat_comma(src: &mut TtIter<'_>) -> bool {
+ if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))) = src.peek_n(0) {
+ let _ = src.next();
+ return true;
+ }
+ false
+}
diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
index fb5313401..8cbf0f8fc 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
@@ -190,20 +190,13 @@ fn convert_tokens<C: TokenConverter>(conv: &mut C) -> tt::Subtree {
let kind = token.kind(conv);
if kind == COMMENT {
- if let Some(tokens) = conv.convert_doc_comment(&token) {
- // FIXME: There has to be a better way to do this
- // Add the comments token id to the converted doc string
+ // Since `convert_doc_comment` can fail, we need to peek the next id, so that we can
+ // figure out which token id to use for the doc comment, if it is converted successfully.
+ let next_id = conv.id_alloc().peek_next_id();
+ if let Some(tokens) = conv.convert_doc_comment(&token, next_id) {
let id = conv.id_alloc().alloc(range, synth_id);
- result.extend(tokens.into_iter().map(|mut tt| {
- if let tt::TokenTree::Subtree(sub) = &mut tt {
- if let Some(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) =
- sub.token_trees.get_mut(2)
- {
- lit.span = id
- }
- }
- tt
- }));
+ debug_assert_eq!(id, next_id);
+ result.extend(tokens);
}
continue;
}
@@ -382,49 +375,46 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr {
text.into()
}
-fn convert_doc_comment(token: &syntax::SyntaxToken) -> Option<Vec<tt::TokenTree>> {
+fn convert_doc_comment(
+ token: &syntax::SyntaxToken,
+ span: tt::TokenId,
+) -> Option<Vec<tt::TokenTree>> {
cov_mark::hit!(test_meta_doc_comments);
let comment = ast::Comment::cast(token.clone())?;
let doc = comment.kind().doc?;
// Make `doc="\" Comments\""
- let meta_tkns = vec![mk_ident("doc"), mk_punct('='), mk_doc_literal(&comment)];
+ let meta_tkns =
+ vec![mk_ident("doc", span), mk_punct('=', span), mk_doc_literal(&comment, span)];
// Make `#![]`
let mut token_trees = Vec::with_capacity(3);
- token_trees.push(mk_punct('#'));
+ token_trees.push(mk_punct('#', span));
if let ast::CommentPlacement::Inner = doc {
- token_trees.push(mk_punct('!'));
+ token_trees.push(mk_punct('!', span));
}
token_trees.push(tt::TokenTree::from(tt::Subtree {
- delimiter: tt::Delimiter {
- open: tt::TokenId::UNSPECIFIED,
- close: tt::TokenId::UNSPECIFIED,
- kind: tt::DelimiterKind::Bracket,
- },
+ delimiter: tt::Delimiter { open: span, close: span, kind: tt::DelimiterKind::Bracket },
token_trees: meta_tkns,
}));
return Some(token_trees);
// Helper functions
- fn mk_ident(s: &str) -> tt::TokenTree {
- tt::TokenTree::from(tt::Leaf::from(tt::Ident {
- text: s.into(),
- span: tt::TokenId::unspecified(),
- }))
+ fn mk_ident(s: &str, span: tt::TokenId) -> tt::TokenTree {
+ tt::TokenTree::from(tt::Leaf::from(tt::Ident { text: s.into(), span }))
}
- fn mk_punct(c: char) -> tt::TokenTree {
+ fn mk_punct(c: char, span: tt::TokenId) -> tt::TokenTree {
tt::TokenTree::from(tt::Leaf::from(tt::Punct {
char: c,
spacing: tt::Spacing::Alone,
- span: tt::TokenId::unspecified(),
+ span,
}))
}
- fn mk_doc_literal(comment: &ast::Comment) -> tt::TokenTree {
- let lit = tt::Literal { text: doc_comment_text(comment), span: tt::TokenId::unspecified() };
+ fn mk_doc_literal(comment: &ast::Comment, span: tt::TokenId) -> tt::TokenTree {
+ let lit = tt::Literal { text: doc_comment_text(comment), span };
tt::TokenTree::from(tt::Leaf::from(lit))
}
@@ -480,6 +470,10 @@ impl TokenIdAlloc {
}
}
}
+
+ fn peek_next_id(&self) -> tt::TokenId {
+ tt::TokenId(self.next_id)
+ }
}
/// A raw token (straight from lexer) converter
@@ -502,7 +496,11 @@ trait SrcToken<Ctx>: std::fmt::Debug {
trait TokenConverter: Sized {
type Token: SrcToken<Self>;
- fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>>;
+ fn convert_doc_comment(
+ &self,
+ token: &Self::Token,
+ span: tt::TokenId,
+ ) -> Option<Vec<tt::TokenTree>>;
fn bump(&mut self) -> Option<(Self::Token, TextRange)>;
@@ -532,9 +530,9 @@ impl<'a> SrcToken<RawConverter<'a>> for usize {
impl<'a> TokenConverter for RawConverter<'a> {
type Token = usize;
- fn convert_doc_comment(&self, &token: &usize) -> Option<Vec<tt::TokenTree>> {
+ fn convert_doc_comment(&self, &token: &usize, span: tt::TokenId) -> Option<Vec<tt::TokenTree>> {
let text = self.lexed.text(token);
- convert_doc_comment(&doc_comment(text))
+ convert_doc_comment(&doc_comment(text), span)
}
fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
@@ -681,8 +679,12 @@ impl SrcToken<Converter> for SynToken {
impl TokenConverter for Converter {
type Token = SynToken;
- fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> {
- convert_doc_comment(token.token()?)
+ fn convert_doc_comment(
+ &self,
+ token: &Self::Token,
+ span: tt::TokenId,
+ ) -> Option<Vec<tt::TokenTree>> {
+ convert_doc_comment(token.token()?, span)
}
fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
diff --git a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs
index f744481f3..59dbf1568 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/tt_iter.rs
@@ -73,13 +73,6 @@ impl<'a> TtIter<'a> {
}
}
- pub(crate) fn expect_u32_literal(&mut self) -> Result<u32, ()> {
- match self.expect_literal()? {
- tt::Leaf::Literal(lit) => lit.text.parse().map_err(drop),
- _ => Err(()),
- }
- }
-
pub(crate) fn expect_single_punct(&mut self) -> Result<&'a tt::Punct, ()> {
match self.expect_leaf()? {
tt::Leaf::Punct(it) => Ok(it),
diff --git a/src/tools/rust-analyzer/crates/parser/Cargo.toml b/src/tools/rust-analyzer/crates/parser/Cargo.toml
index 6e962abd7..09e62c352 100644
--- a/src/tools/rust-analyzer/crates/parser/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/parser/Cargo.toml
@@ -13,7 +13,7 @@ doctest = false
[dependencies]
drop_bomb = "0.1.5"
-rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" }
+rustc_lexer.workspace = true
limit.workspace = true
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
index 15435a26c..1814e0e54 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
@@ -66,6 +66,10 @@ pub(crate) mod entry {
patterns::pattern_single(p);
}
+ pub(crate) fn pat_top(p: &mut Parser<'_>) {
+ patterns::pattern_top(p);
+ }
+
pub(crate) fn ty(p: &mut Parser<'_>) {
types::type_(p);
}
@@ -218,17 +222,22 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
// pub(self) struct S;
// pub(super) struct S;
+ // test_err crate_visibility_empty_recover
+ // pub() struct S;
+
// test pub_parens_typepath
// struct B(pub (super::A));
// struct B(pub (crate::A,));
- T![crate] | T![self] | T![super] | T![ident] if p.nth(2) != T![:] => {
+ T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => {
// If we are in a tuple struct, then the parens following `pub`
// might be an tuple field, not part of the visibility. So in that
// case we don't want to consume an identifier.
// test pub_tuple_field
// struct MyStruct(pub (u32, u32));
- if !(in_tuple_field && matches!(p.nth(1), T![ident])) {
+ // struct MyStruct(pub (u32));
+ // struct MyStruct(pub ());
+ if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
p.bump(T!['(']);
paths::use_path(p);
p.expect(T![')']);
@@ -243,7 +252,7 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
paths::use_path(p);
p.expect(T![')']);
}
- _ => (),
+ _ => {}
}
}
m.complete(p, VISIBILITY);
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
index a884d8b6e..1cbd16632 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
@@ -4,8 +4,8 @@ use crate::grammar::attributes::ATTRIBUTE_FIRST;
use super::*;
-pub(crate) use self::atom::{block_expr, match_arm_list};
-pub(super) use self::atom::{literal, LITERAL_FIRST};
+pub(crate) use atom::{block_expr, match_arm_list};
+pub(super) use atom::{literal, LITERAL_FIRST};
#[derive(PartialEq, Eq)]
pub(super) enum Semicolon {
@@ -188,47 +188,56 @@ struct Restrictions {
prefer_stmt: bool,
}
+enum Associativity {
+ Left,
+ Right,
+}
+
/// Binding powers of operators for a Pratt parser.
///
/// See <https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html>
+///
+/// Note that Rust doesn't define associativity for some infix operators (e.g. `==` and `..`) and
+/// requires parentheses to disambiguate. We just treat them as left associative.
#[rustfmt::skip]
-fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind) {
- const NOT_AN_OP: (u8, SyntaxKind) = (0, T![@]);
+fn current_op(p: &Parser<'_>) -> (u8, SyntaxKind, Associativity) {
+ use Associativity::*;
+ const NOT_AN_OP: (u8, SyntaxKind, Associativity) = (0, T![@], Left);
match p.current() {
- T![|] if p.at(T![||]) => (3, T![||]),
- T![|] if p.at(T![|=]) => (1, T![|=]),
- T![|] => (6, T![|]),
- T![>] if p.at(T![>>=]) => (1, T![>>=]),
- T![>] if p.at(T![>>]) => (9, T![>>]),
- T![>] if p.at(T![>=]) => (5, T![>=]),
- T![>] => (5, T![>]),
+ T![|] if p.at(T![||]) => (3, T![||], Left),
+ T![|] if p.at(T![|=]) => (1, T![|=], Right),
+ T![|] => (6, T![|], Left),
+ T![>] if p.at(T![>>=]) => (1, T![>>=], Right),
+ T![>] if p.at(T![>>]) => (9, T![>>], Left),
+ T![>] if p.at(T![>=]) => (5, T![>=], Left),
+ T![>] => (5, T![>], Left),
T![=] if p.at(T![=>]) => NOT_AN_OP,
- T![=] if p.at(T![==]) => (5, T![==]),
- T![=] => (1, T![=]),
- T![<] if p.at(T![<=]) => (5, T![<=]),
- T![<] if p.at(T![<<=]) => (1, T![<<=]),
- T![<] if p.at(T![<<]) => (9, T![<<]),
- T![<] => (5, T![<]),
- T![+] if p.at(T![+=]) => (1, T![+=]),
- T![+] => (10, T![+]),
- T![^] if p.at(T![^=]) => (1, T![^=]),
- T![^] => (7, T![^]),
- T![%] if p.at(T![%=]) => (1, T![%=]),
- T![%] => (11, T![%]),
- T![&] if p.at(T![&=]) => (1, T![&=]),
+ T![=] if p.at(T![==]) => (5, T![==], Left),
+ T![=] => (1, T![=], Right),
+ T![<] if p.at(T![<=]) => (5, T![<=], Left),
+ T![<] if p.at(T![<<=]) => (1, T![<<=], Right),
+ T![<] if p.at(T![<<]) => (9, T![<<], Left),
+ T![<] => (5, T![<], Left),
+ T![+] if p.at(T![+=]) => (1, T![+=], Right),
+ T![+] => (10, T![+], Left),
+ T![^] if p.at(T![^=]) => (1, T![^=], Right),
+ T![^] => (7, T![^], Left),
+ T![%] if p.at(T![%=]) => (1, T![%=], Right),
+ T![%] => (11, T![%], Left),
+ T![&] if p.at(T![&=]) => (1, T![&=], Right),
// If you update this, remember to update `expr_let()` too.
- T![&] if p.at(T![&&]) => (4, T![&&]),
- T![&] => (8, T![&]),
- T![/] if p.at(T![/=]) => (1, T![/=]),
- T![/] => (11, T![/]),
- T![*] if p.at(T![*=]) => (1, T![*=]),
- T![*] => (11, T![*]),
- T![.] if p.at(T![..=]) => (2, T![..=]),
- T![.] if p.at(T![..]) => (2, T![..]),
- T![!] if p.at(T![!=]) => (5, T![!=]),
- T![-] if p.at(T![-=]) => (1, T![-=]),
- T![-] => (10, T![-]),
- T![as] => (12, T![as]),
+ T![&] if p.at(T![&&]) => (4, T![&&], Left),
+ T![&] => (8, T![&], Left),
+ T![/] if p.at(T![/=]) => (1, T![/=], Right),
+ T![/] => (11, T![/], Left),
+ T![*] if p.at(T![*=]) => (1, T![*=], Right),
+ T![*] => (11, T![*], Left),
+ T![.] if p.at(T![..=]) => (2, T![..=], Left),
+ T![.] if p.at(T![..]) => (2, T![..], Left),
+ T![!] if p.at(T![!=]) => (5, T![!=], Left),
+ T![-] if p.at(T![-=]) => (1, T![-=], Right),
+ T![-] => (10, T![-], Left),
+ T![as] => (12, T![as], Left),
_ => NOT_AN_OP
}
@@ -273,7 +282,7 @@ fn expr_bp(
loop {
let is_range = p.at(T![..]) || p.at(T![..=]);
- let (op_bp, op) = current_op(p);
+ let (op_bp, op, associativity) = current_op(p);
if op_bp < bp {
break;
}
@@ -306,7 +315,11 @@ fn expr_bp(
}
}
- expr_bp(p, None, Restrictions { prefer_stmt: false, ..r }, op_bp + 1);
+ let op_bp = match associativity {
+ Associativity::Left => op_bp + 1,
+ Associativity::Right => op_bp,
+ };
+ expr_bp(p, None, Restrictions { prefer_stmt: false, ..r }, op_bp);
lhs = m.complete(p, if is_range { RANGE_EXPR } else { BIN_EXPR });
}
Some((lhs, BlockLike::NotBlock))
@@ -417,7 +430,7 @@ fn postfix_expr(
allow_calls = true;
block_like = BlockLike::NotBlock;
}
- return (lhs, block_like);
+ (lhs, block_like)
}
fn postfix_dot_expr<const FLOAT_RECOVERY: bool>(
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
index d051dd268..d8553d3f9 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
@@ -12,6 +12,8 @@ use super::*;
// let _ = r"d";
// let _ = b"e";
// let _ = br"f";
+// let _ = c"g";
+// let _ = cr"h";
// }
pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[
T![true],
@@ -22,6 +24,7 @@ pub(crate) const LITERAL_FIRST: TokenSet = TokenSet::new(&[
CHAR,
STRING,
BYTE_STRING,
+ C_STRING,
]);
pub(crate) fn literal(p: &mut Parser<'_>) -> Option<CompletedMarker> {
@@ -181,6 +184,16 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
let mut saw_comma = false;
let mut saw_expr = false;
+
+ // test_err tuple_expr_leading_comma
+ // fn foo() {
+ // (,);
+ // }
+ if p.eat(T![,]) {
+ p.error("expected expression");
+ saw_comma = true;
+ }
+
while !p.at(EOF) && !p.at(T![')']) {
saw_expr = true;
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs
index 919d9b91e..e589b6993 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_args.rs
@@ -28,6 +28,7 @@ const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[
BYTE,
STRING,
BYTE_STRING,
+ C_STRING,
])
.union(types::TYPE_FIRST);
@@ -35,7 +36,7 @@ const GENERIC_ARG_FIRST: TokenSet = TokenSet::new(&[
// type T = S<i32>;
fn generic_arg(p: &mut Parser<'_>) -> bool {
match p.current() {
- LIFETIME_IDENT => lifetime_arg(p),
+ LIFETIME_IDENT if !p.nth_at(1, T![+]) => lifetime_arg(p),
T!['{'] | T![true] | T![false] | T![-] => const_arg(p),
k if k.is_literal() => const_arg(p),
// test associated_type_bounds
@@ -76,6 +77,29 @@ fn generic_arg(p: &mut Parser<'_>) -> bool {
}
}
}
+ IDENT if p.nth_at(1, T!['(']) => {
+ let m = p.start();
+ name_ref(p);
+ params::param_list_fn_trait(p);
+ if p.at(T![:]) && !p.at(T![::]) {
+ // test associated_return_type_bounds
+ // fn foo<T: Foo<foo(): Send, bar(i32): Send, baz(i32, i32): Send>>() {}
+ generic_params::bounds(p);
+ m.complete(p, ASSOC_TYPE_ARG);
+ } else {
+ // test bare_dyn_types_with_paren_as_generic_args
+ // type A = S<Fn(i32)>;
+ // type A = S<Fn(i32) + Send>;
+ // type B = S<Fn(i32) -> i32>;
+ // type C = S<Fn(i32) -> i32 + Send>;
+ opt_ret_type(p);
+ let m = m.complete(p, PATH_SEGMENT).precede(p).complete(p, PATH);
+ let m = paths::type_path_for_qualifier(p, m);
+ let m = m.precede(p).complete(p, PATH_TYPE);
+ let m = types::opt_type_bounds_as_dyn_trait_type(p, m);
+ m.precede(p).complete(p, TYPE_ARG);
+ }
+ }
_ if p.at_ts(types::TYPE_FIRST) => type_arg(p),
_ => return false,
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
index 5e0951bf8..1c056819f 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
@@ -19,7 +19,7 @@ use super::*;
// struct S;
pub(super) fn mod_contents(p: &mut Parser<'_>, stop_on_r_curly: bool) {
attributes::inner_attrs(p);
- while !p.at(EOF) && !(p.at(T!['}']) && stop_on_r_curly) {
+ while !(p.at(EOF) || (p.at(T!['}']) && stop_on_r_curly)) {
item_or_macro(p, stop_on_r_curly);
}
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
index 26490aa97..01b8f9e91 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/paths.rs
@@ -136,6 +136,7 @@ fn opt_path_type_args(p: &mut Parser<'_>, mode: Mode) {
Mode::Type => {
// test typepathfn_with_coloncolon
// type F = Start::(Middle) -> (Middle)::End;
+ // type GenericArg = S<Start(Middle)::End>;
if p.at(T![::]) && p.nth_at(2, T!['(']) {
p.bump(T![::]);
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs
index 5f4977886..39ded41bb 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/patterns.rs
@@ -5,6 +5,7 @@ pub(super) const PATTERN_FIRST: TokenSet =
T![box],
T![ref],
T![mut],
+ T![const],
T!['('],
T!['['],
T![&],
@@ -15,6 +16,10 @@ pub(super) const PATTERN_FIRST: TokenSet =
const PAT_TOP_FIRST: TokenSet = PATTERN_FIRST.union(TokenSet::new(&[T![|]]));
+/// Set of possible tokens at the start of a range pattern's end bound.
+const RANGE_PAT_END_FIRST: TokenSet =
+ expressions::LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[T![-], T![const]]));
+
pub(crate) fn pattern(p: &mut Parser<'_>) {
pattern_r(p, PAT_RECOVERY_SET);
}
@@ -105,6 +110,52 @@ fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
return;
}
+ // test exclusive_range_pat
+ // fn main() {
+ // match 42 {
+ // ..0 => {}
+ // 1..2 => {}
+ // }
+ // }
+
+ // test dot_dot_pat
+ // fn main() {
+ // let .. = ();
+ // //
+ // // Tuples
+ // //
+ // let (a, ..) = ();
+ // let (a, ..,) = ();
+ // let Tuple(a, ..) = ();
+ // let Tuple(a, ..,) = ();
+ // let (.., ..) = ();
+ // let Tuple(.., ..) = ();
+ // let (.., a, ..) = ();
+ // let Tuple(.., a, ..) = ();
+ // //
+ // // Slices
+ // //
+ // let [..] = ();
+ // let [head, ..] = ();
+ // let [head, tail @ ..] = ();
+ // let [head, .., cons] = ();
+ // let [head, mid @ .., cons] = ();
+ // let [head, .., .., cons] = ();
+ // let [head, .., mid, tail @ ..] = ();
+ // let [head, .., mid, .., cons] = ();
+ // }
+ if p.at(T![..]) {
+ let m = p.start();
+ p.bump(T![..]);
+ if p.at_ts(RANGE_PAT_END_FIRST) {
+ atom_pat(p, recovery_set);
+ m.complete(p, RANGE_PAT);
+ } else {
+ m.complete(p, REST_PAT);
+ }
+ return;
+ }
+
if let Some(lhs) = atom_pat(p, recovery_set) {
for range_op in [T![...], T![..=], T![..]] {
if p.at(range_op) {
@@ -173,7 +224,6 @@ fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option<CompletedMarke
_ if paths::is_path_start(p) => path_or_macro_pat(p),
_ if is_literal_pat_start(p) => literal_pat(p),
- T![.] if p.at(T![..]) => rest_pat(p),
T![_] => wildcard_pat(p),
T![&] => ref_pat(p),
T!['('] => tuple_pat(p),
@@ -334,39 +384,6 @@ fn wildcard_pat(p: &mut Parser<'_>) -> CompletedMarker {
m.complete(p, WILDCARD_PAT)
}
-// test dot_dot_pat
-// fn main() {
-// let .. = ();
-// //
-// // Tuples
-// //
-// let (a, ..) = ();
-// let (a, ..,) = ();
-// let Tuple(a, ..) = ();
-// let Tuple(a, ..,) = ();
-// let (.., ..) = ();
-// let Tuple(.., ..) = ();
-// let (.., a, ..) = ();
-// let Tuple(.., a, ..) = ();
-// //
-// // Slices
-// //
-// let [..] = ();
-// let [head, ..] = ();
-// let [head, tail @ ..] = ();
-// let [head, .., cons] = ();
-// let [head, mid @ .., cons] = ();
-// let [head, .., .., cons] = ();
-// let [head, .., mid, tail @ ..] = ();
-// let [head, .., mid, .., cons] = ();
-// }
-fn rest_pat(p: &mut Parser<'_>) -> CompletedMarker {
- assert!(p.at(T![..]));
- let m = p.start();
- p.bump(T![..]);
- m.complete(p, REST_PAT)
-}
-
// test ref_pat
// fn main() {
// let &a = ();
@@ -396,6 +413,16 @@ fn tuple_pat(p: &mut Parser<'_>) -> CompletedMarker {
let mut has_comma = false;
let mut has_pat = false;
let mut has_rest = false;
+
+ // test_err tuple_pat_leading_comma
+ // fn foo() {
+ // let (,);
+ // }
+ if p.eat(T![,]) {
+ p.error("expected pattern");
+ has_comma = true;
+ }
+
while !p.at(EOF) && !p.at(T![')']) {
has_pat = true;
if !p.at_ts(PAT_TOP_FIRST) {
@@ -483,6 +510,14 @@ fn box_pat(p: &mut Parser<'_>) -> CompletedMarker {
// fn main() {
// let const { 15 } = ();
// let const { foo(); bar() } = ();
+//
+// match 42 {
+// const { 0 } .. const { 1 } => (),
+// .. const { 0 } => (),
+// const { 2 } .. => (),
+// }
+//
+// let (const { () },) = ();
// }
fn const_block_pat(p: &mut Parser<'_>) -> CompletedMarker {
assert!(p.at(T![const]));
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
index 7d0b156c5..96a6cdeaa 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs
@@ -15,6 +15,7 @@ pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[
T![impl],
T![dyn],
T![Self],
+ LIFETIME_IDENT,
]));
pub(super) const TYPE_RECOVERY_SET: TokenSet = TokenSet::new(&[
@@ -49,6 +50,7 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) {
// Some path types are not allowed to have bounds (no plus)
T![<] => path_type_(p, allow_bounds),
_ if paths::is_path_start(p) => path_or_macro_type_(p, allow_bounds),
+ LIFETIME_IDENT if p.nth_at(1, T![+]) => bare_dyn_trait_type(p),
_ => {
p.err_recover("expected type", TYPE_RECOVERY_SET);
}
@@ -59,7 +61,7 @@ pub(super) fn ascription(p: &mut Parser<'_>) {
assert!(p.at(T![:]));
p.bump(T![:]);
if p.at(T![=]) {
- // recover from `let x: = expr;`, `const X: = expr;` and similars
+ // recover from `let x: = expr;`, `const X: = expr;` and similar
// hopefully no type starts with `=`
p.error("missing type");
return;
@@ -151,7 +153,9 @@ fn array_or_slice_type(p: &mut Parser<'_>) {
// type T = [(); 92];
T![;] => {
p.bump(T![;]);
+ let m = p.start();
expressions::expr(p);
+ m.complete(p, CONST_ARG);
p.expect(T![']']);
ARRAY_TYPE
}
@@ -275,6 +279,15 @@ fn dyn_trait_type(p: &mut Parser<'_>) {
m.complete(p, DYN_TRAIT_TYPE);
}
+// test bare_dyn_types_with_leading_lifetime
+// type A = 'static + Trait;
+// type B = S<'static + Trait>;
+fn bare_dyn_trait_type(p: &mut Parser<'_>) {
+ let m = p.start();
+ generic_params::bounds_without_colon(p);
+ m.complete(p, DYN_TRAIT_TYPE);
+}
+
// test path_type
// type A = Foo;
// type B = ::Foo;
@@ -326,13 +339,16 @@ pub(super) fn path_type_(p: &mut Parser<'_>, allow_bounds: bool) {
/// This turns a parsed PATH_TYPE or FOR_TYPE optionally into a DYN_TRAIT_TYPE
/// with a TYPE_BOUND_LIST
-fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser<'_>, type_marker: CompletedMarker) {
+pub(super) fn opt_type_bounds_as_dyn_trait_type(
+ p: &mut Parser<'_>,
+ type_marker: CompletedMarker,
+) -> CompletedMarker {
assert!(matches!(
type_marker.kind(),
SyntaxKind::PATH_TYPE | SyntaxKind::FOR_TYPE | SyntaxKind::MACRO_TYPE
));
if !p.at(T![+]) {
- return;
+ return type_marker;
}
// First create a TYPE_BOUND from the completed PATH_TYPE
@@ -349,5 +365,5 @@ fn opt_type_bounds_as_dyn_trait_type(p: &mut Parser<'_>, type_marker: CompletedM
let m = generic_params::bounds_without_colon_m(p, m);
// Finally precede everything with DYN_TRAIT_TYPE
- m.precede(p).complete(p, DYN_TRAIT_TYPE);
+ m.precede(p).complete(p, DYN_TRAIT_TYPE)
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
index 100deff46..e4dce21f3 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -36,7 +36,7 @@ impl<'a> LexedStr<'a> {
};
for token in rustc_lexer::tokenize(&text[conv.offset..]) {
- let token_text = &text[conv.offset..][..token.len];
+ let token_text = &text[conv.offset..][..token.len as usize];
conv.extend_token(&token.kind, token_text);
}
@@ -49,8 +49,8 @@ impl<'a> LexedStr<'a> {
return None;
}
- let token = rustc_lexer::first_token(text);
- if token.len != text.len() {
+ let token = rustc_lexer::tokenize(text).next()?;
+ if token.len as usize != text.len() {
return None;
}
@@ -175,6 +175,10 @@ impl<'a> Converter<'a> {
rustc_lexer::TokenKind::Ident => {
SyntaxKind::from_keyword(token_text).unwrap_or(IDENT)
}
+ rustc_lexer::TokenKind::InvalidIdent => {
+ err = "Ident contains invalid characters";
+ IDENT
+ }
rustc_lexer::TokenKind::RawIdent => IDENT,
rustc_lexer::TokenKind::Literal { kind, .. } => {
@@ -221,6 +225,7 @@ impl<'a> Converter<'a> {
err = "unknown literal prefix";
IDENT
}
+ rustc_lexer::TokenKind::Eof => EOF,
}
};
@@ -268,35 +273,30 @@ impl<'a> Converter<'a> {
}
BYTE_STRING
}
- rustc_lexer::LiteralKind::RawStr { err: raw_str_err, .. } => {
- if let Some(raw_str_err) = raw_str_err {
- err = match raw_str_err {
- rustc_lexer::RawStrError::InvalidStarter { .. } => "Missing `\"` symbol after `#` symbols to begin the raw string literal",
- rustc_lexer::RawStrError::NoTerminator { expected, found, .. } => if expected == found {
- "Missing trailing `\"` to terminate the raw string literal"
- } else {
- "Missing trailing `\"` with `#` symbols to terminate the raw string literal"
- },
- rustc_lexer::RawStrError::TooManyDelimiters { .. } => "Too many `#` symbols: raw strings may be delimited by up to 65535 `#` symbols",
- };
- };
+ rustc_lexer::LiteralKind::CStr { terminated } => {
+ if !terminated {
+ err = "Missing trailing `\"` symbol to terminate the string literal";
+ }
+ C_STRING
+ }
+ rustc_lexer::LiteralKind::RawStr { n_hashes } => {
+ if n_hashes.is_none() {
+ err = "Invalid raw string literal";
+ }
STRING
}
- rustc_lexer::LiteralKind::RawByteStr { err: raw_str_err, .. } => {
- if let Some(raw_str_err) = raw_str_err {
- err = match raw_str_err {
- rustc_lexer::RawStrError::InvalidStarter { .. } => "Missing `\"` symbol after `#` symbols to begin the raw byte string literal",
- rustc_lexer::RawStrError::NoTerminator { expected, found, .. } => if expected == found {
- "Missing trailing `\"` to terminate the raw byte string literal"
- } else {
- "Missing trailing `\"` with `#` symbols to terminate the raw byte string literal"
- },
- rustc_lexer::RawStrError::TooManyDelimiters { .. } => "Too many `#` symbols: raw byte strings may be delimited by up to 65535 `#` symbols",
- };
- };
-
+ rustc_lexer::LiteralKind::RawByteStr { n_hashes } => {
+ if n_hashes.is_none() {
+ err = "Invalid raw string literal";
+ }
BYTE_STRING
}
+ rustc_lexer::LiteralKind::RawCStr { n_hashes } => {
+ if n_hashes.is_none() {
+ err = "Invalid raw string literal";
+ }
+ C_STRING
+ }
};
let err = if err.is_empty() { None } else { Some(err) };
diff --git a/src/tools/rust-analyzer/crates/parser/src/lib.rs b/src/tools/rust-analyzer/crates/parser/src/lib.rs
index 8c5aed023..1aba1f767 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lib.rs
@@ -131,6 +131,7 @@ pub enum PrefixEntryPoint {
Block,
Stmt,
Pat,
+ PatTop,
Ty,
Expr,
Path,
@@ -145,6 +146,7 @@ impl PrefixEntryPoint {
PrefixEntryPoint::Block => grammar::entry::prefix::block,
PrefixEntryPoint::Stmt => grammar::entry::prefix::stmt,
PrefixEntryPoint::Pat => grammar::entry::prefix::pat,
+ PrefixEntryPoint::PatTop => grammar::entry::prefix::pat_top,
PrefixEntryPoint::Ty => grammar::entry::prefix::ty,
PrefixEntryPoint::Expr => grammar::entry::prefix::expr,
PrefixEntryPoint::Path => grammar::entry::prefix::path,
diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs
index 280416ae7..ef413c637 100644
--- a/src/tools/rust-analyzer/crates/parser/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs
@@ -205,7 +205,7 @@ impl<'t> Parser<'t> {
marker.bomb.defuse();
marker = new_marker;
};
- self.pos += 1 as usize;
+ self.pos += 1;
self.push_event(Event::FloatSplitHack { ends_in_dot });
(ends_in_dot, marker)
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
index 47e4adcbb..5cdb39700 100644
--- a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
@@ -46,10 +46,8 @@ impl<'a> LexedStr<'a> {
// Tag the token as joint if it is float with a fractional part
// we use this jointness to inform the parser about what token split
// event to emit when we encounter a float literal in a field access
- if kind == SyntaxKind::FLOAT_NUMBER {
- if !self.text(i).ends_with('.') {
- res.was_joint();
- }
+ if kind == SyntaxKind::FLOAT_NUMBER && !self.text(i).ends_with('.') {
+ res.was_joint();
}
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index cd87b304a..a8fbcfacf 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -117,6 +117,7 @@ pub enum SyntaxKind {
BYTE,
STRING,
BYTE_STRING,
+ C_STRING,
ERROR,
IDENT,
WHITESPACE,
@@ -245,6 +246,7 @@ pub enum SyntaxKind {
GENERIC_PARAM,
LIFETIME_PARAM,
TYPE_PARAM,
+ RETURN_TYPE_ARG,
CONST_PARAM,
GENERIC_ARG_LIST,
LIFETIME,
@@ -378,7 +380,7 @@ impl SyntaxKind {
)
}
pub fn is_literal(self) -> bool {
- matches!(self, INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE | STRING | BYTE_STRING)
+ matches!(self, INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE | STRING | BYTE_STRING | C_STRING)
}
pub fn from_keyword(ident: &str) -> Option<SyntaxKind> {
let kw = match ident {
diff --git a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
index 40f92e588..11f9c34ab 100644
--- a/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/tests/prefix_entries.rs
@@ -33,8 +33,7 @@ fn stmt() {
fn pat() {
check(PrefixEntryPoint::Pat, "x y", "x");
check(PrefixEntryPoint::Pat, "fn f() {}", "fn");
- // FIXME: This one is wrong, we should consume only one pattern.
- check(PrefixEntryPoint::Pat, ".. ..", ".. ..");
+ check(PrefixEntryPoint::Pat, ".. ..", "..");
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast
index 6ec1780c3..cab02d38a 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_at_eof.rast
@@ -1 +1 @@
-BYTE_STRING "br##\"" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\"" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast
index d65f1bb2f..0486a1e8e 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ascii_escape.rast
@@ -1 +1 @@
-BYTE_STRING "br##\"\\x7f" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\"\\x7f" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast
index 0f9e0a165..41e3455c1 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_ferris.rast
@@ -1 +1 @@
-BYTE_STRING "br##\"🦀" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\"🦀" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast
index 202dcd2d4..a11208a81 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash.rast
@@ -1 +1 @@
-BYTE_STRING "br##\"\\" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\"\\" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast
index d45485b52..10a47ab84 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_slash_n.rast
@@ -1 +1 @@
-BYTE_STRING "br##\"\\n" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\"\\n" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast
index 1bfabbc3a..b41ea3a17 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_space.rast
@@ -1 +1 @@
-BYTE_STRING "br##\" " error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\" " error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast
index 104ab8aae..63b8a5af8 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_byte_string_with_unicode_escape.rast
@@ -1 +1 @@
-BYTE_STRING "br##\"\\u{20AA}" error: Missing trailing `"` with `#` symbols to terminate the raw byte string literal
+BYTE_STRING "br##\"\\u{20AA}" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast
index 71b20fd19..096bb9403 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_at_eof.rast
@@ -1 +1 @@
-STRING "r##\"" error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\"" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast
index dc106dd24..f0ad200fe 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ascii_escape.rast
@@ -1 +1 @@
-STRING "r##\"\\x7f" error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\"\\x7f" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast
index 30ee029f6..bc5996d1e 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_ferris.rast
@@ -1 +1 @@
-STRING "r##\"🦀" error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\"🦀" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast
index 8a6f6cc43..b48ec5dda 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash.rast
@@ -1 +1 @@
-STRING "r##\"\\" error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\"\\" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast
index f46eff251..9f32f6777 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_slash_n.rast
@@ -1 +1 @@
-STRING "r##\"\\n" error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\"\\n" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast
index 49b6afea4..2804a43cf 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_space.rast
@@ -1 +1 @@
-STRING "r##\" " error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\" " error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast
index d10d6d8e8..eb0a2d2da 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_raw_string_with_unicode_escape.rast
@@ -1 +1 @@
-STRING "r##\"\\u{20AA}" error: Missing trailing `"` with `#` symbols to terminate the raw string literal
+STRING "r##\"\\u{20AA}" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast
index cf942c92f..52a7f03b6 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_at_eof.rast
@@ -1 +1 @@
-BYTE_STRING "br##" error: Missing `"` symbol after `#` symbols to begin the raw byte string literal
+BYTE_STRING "br##" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast
index 042769c27..da5550d4c 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_byte_string_with_ascii.rast
@@ -1,4 +1,4 @@
-BYTE_STRING "br## " error: Missing `"` symbol after `#` symbols to begin the raw byte string literal
+BYTE_STRING "br## " error: Invalid raw string literal
IDENT "I"
WHITESPACE " "
IDENT "lack"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast
index 2f7c7529a..50b962e77 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_at_eof.rast
@@ -1 +1 @@
-STRING "r##" error: Missing `"` symbol after `#` symbols to begin the raw string literal
+STRING "r##" error: Invalid raw string literal
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast
index 4a06b0abe..1f484299a 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unstarted_raw_string_with_ascii.rast
@@ -1,4 +1,4 @@
-STRING "r## " error: Missing `"` symbol after `#` symbols to begin the raw string literal
+STRING "r## " error: Invalid raw string literal
IDENT "I"
WHITESPACE " "
IDENT "lack"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast
index 674c8d536..674c8d536 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rs
index 2792c2084..2792c2084 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplere_where_for.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast
index 4b2a74036..4b2a74036 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rast
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rs
index db32b98df..db32b98df 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repated_extern_modifier.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0047_repeated_extern_modifier.rs
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast
new file mode 100644
index 000000000..0fe4ca42d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rast
@@ -0,0 +1,18 @@
+SOURCE_FILE
+ STRUCT
+ VISIBILITY
+ PUB_KW "pub"
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ ERROR
+ R_PAREN ")"
+ WHITESPACE " "
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "S"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+error 4: expected identifier
+error 5: expected R_PAREN
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs
new file mode 100644
index 000000000..e8cf9e669
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0018_crate_visibility_empty_recover.rs
@@ -0,0 +1 @@
+pub() struct S;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast
new file mode 100644
index 000000000..3fbc0da40
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rast
@@ -0,0 +1,24 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ TUPLE_EXPR
+ L_PAREN "("
+ COMMA ","
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
+error 17: expected expression
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs
new file mode 100644
index 000000000..12fab59a7
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0019_tuple_expr_leading_comma.rs
@@ -0,0 +1,3 @@
+fn foo() {
+ (,);
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast
new file mode 100644
index 000000000..9c8837292
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rast
@@ -0,0 +1,26 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ TUPLE_PAT
+ L_PAREN "("
+ COMMA ","
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
+error 21: expected pattern
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs
new file mode 100644
index 000000000..de168521e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0020_tuple_pat_leading_comma.rs
@@ -0,0 +1,3 @@
+fn foo() {
+ let (,);
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast
index 2a5c644d4..0d50144b7 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0017_array_type.rast
@@ -14,8 +14,9 @@ SOURCE_FILE
R_PAREN ")"
SEMICOLON ";"
WHITESPACE " "
- LITERAL
- INT_NUMBER "92"
+ CONST_ARG
+ LITERAL
+ INT_NUMBER "92"
R_BRACK "]"
SEMICOLON ";"
WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast
index 403c265ea..fe73d9dfe 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rast
@@ -131,6 +131,30 @@ SOURCE_FILE
LITERAL
BYTE_STRING "br\"f\""
SEMICOLON ";"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ C_STRING "c\"g\""
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ C_STRING "cr\"h\""
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs
index 2e11a5a6e..e7f235a83 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0085_expr_literals.rs
@@ -9,4 +9,6 @@ fn foo() {
let _ = r"d";
let _ = b"e";
let _ = br"f";
+ let _ = c"g";
+ let _ = cr"h";
}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast
index 59de2b9f1..593867a7b 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rast
@@ -74,6 +74,126 @@ SOURCE_FILE
L_PAREN "("
R_PAREN ")"
SEMICOLON ";"
+ WHITESPACE "\n\n "
+ EXPR_STMT
+ MATCH_EXPR
+ MATCH_KW "match"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "42"
+ WHITESPACE " "
+ MATCH_ARM_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ MATCH_ARM
+ RANGE_PAT
+ CONST_BLOCK_PAT
+ CONST_KW "const"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "0"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE " "
+ DOT2 ".."
+ WHITESPACE " "
+ CONST_BLOCK_PAT
+ CONST_KW "const"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE " "
+ FAT_ARROW "=>"
+ WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE "\n "
+ MATCH_ARM
+ RANGE_PAT
+ DOT2 ".."
+ WHITESPACE " "
+ CONST_BLOCK_PAT
+ CONST_KW "const"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "0"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE " "
+ FAT_ARROW "=>"
+ WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE "\n "
+ MATCH_ARM
+ RANGE_PAT
+ CONST_BLOCK_PAT
+ CONST_KW "const"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "2"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE " "
+ DOT2 ".."
+ WHITESPACE " "
+ FAT_ARROW "=>"
+ WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE "\n "
+ R_CURLY "}"
+ WHITESPACE "\n\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ TUPLE_PAT
+ L_PAREN "("
+ CONST_BLOCK_PAT
+ CONST_KW "const"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ R_CURLY "}"
+ COMMA ","
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ R_PAREN ")"
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs
index dce9defac..6ecdee849 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0156_const_block_pat.rs
@@ -1,4 +1,12 @@
fn main() {
let const { 15 } = ();
let const { foo(); bar() } = ();
+
+ match 42 {
+ const { 0 } .. const { 1 } => (),
+ .. const { 0 } => (),
+ const { 2 } .. => (),
+ }
+
+ let (const { () },) = ();
}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast
index a23ddf69f..c78d16f06 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rast
@@ -28,3 +28,42 @@ SOURCE_FILE
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n"
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "MyStruct"
+ TUPLE_FIELD_LIST
+ L_PAREN "("
+ TUPLE_FIELD
+ VISIBILITY
+ PUB_KW "pub"
+ WHITESPACE " "
+ PAREN_TYPE
+ L_PAREN "("
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "u32"
+ R_PAREN ")"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "MyStruct"
+ TUPLE_FIELD_LIST
+ L_PAREN "("
+ TUPLE_FIELD
+ VISIBILITY
+ PUB_KW "pub"
+ WHITESPACE " "
+ TUPLE_TYPE
+ L_PAREN "("
+ R_PAREN ")"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs
index 00d8feba9..6f725fb7b 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0196_pub_tuple_field.rs
@@ -1 +1,3 @@
struct MyStruct(pub (u32, u32));
+struct MyStruct(pub (u32));
+struct MyStruct(pub ());
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast
index b47a5a5c1..67277d063 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rast
@@ -41,3 +41,41 @@ SOURCE_FILE
IDENT "End"
SEMICOLON ";"
WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "GenericArg"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Start"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Middle"
+ R_PAREN ")"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "End"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs
index 8efd93a7f..8c54f6704 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0202_typepathfn_with_coloncolon.rs
@@ -1 +1,2 @@
type F = Start::(Middle) -> (Middle)::End;
+type GenericArg = S<Start(Middle)::End>;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast
new file mode 100644
index 000000000..fd2c422d0
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rast
@@ -0,0 +1,58 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "main"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ MATCH_EXPR
+ MATCH_KW "match"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "42"
+ WHITESPACE " "
+ MATCH_ARM_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ MATCH_ARM
+ RANGE_PAT
+ DOT2 ".."
+ LITERAL_PAT
+ LITERAL
+ INT_NUMBER "0"
+ WHITESPACE " "
+ FAT_ARROW "=>"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n "
+ MATCH_ARM
+ RANGE_PAT
+ LITERAL_PAT
+ LITERAL
+ INT_NUMBER "1"
+ DOT2 ".."
+ LITERAL_PAT
+ LITERAL
+ INT_NUMBER "2"
+ WHITESPACE " "
+ FAT_ARROW "=>"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n "
+ R_CURLY "}"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs
new file mode 100644
index 000000000..e80505d8b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_exclusive_range_pat.rs
@@ -0,0 +1,6 @@
+fn main() {
+ match 42 {
+ ..0 => {}
+ 1..2 => {}
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast
new file mode 100644
index 000000000..2fa52068c
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rast
@@ -0,0 +1,102 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ GENERIC_PARAM_LIST
+ L_ANGLE "<"
+ TYPE_PARAM
+ NAME
+ IDENT "T"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Foo"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Send"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "bar"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Send"
+ COMMA ","
+ WHITESPACE " "
+ ASSOC_TYPE_ARG
+ NAME_REF
+ IDENT "baz"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ COMMA ","
+ WHITESPACE " "
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ COLON ":"
+ WHITESPACE " "
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Send"
+ R_ANGLE ">"
+ R_ANGLE ">"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs
new file mode 100644
index 000000000..42029ac59
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_associated_return_type_bounds.rs
@@ -0,0 +1 @@
+fn foo<T: Foo<foo(): Send, bar(i32): Send, baz(i32, i32): Send>>() {}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast
new file mode 100644
index 000000000..d7e67fbcd
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rast
@@ -0,0 +1,58 @@
+SOURCE_FILE
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "A"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ DYN_TRAIT_TYPE
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'static"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Trait"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "B"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ DYN_TRAIT_TYPE
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ LIFETIME
+ LIFETIME_IDENT "'static"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Trait"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs
new file mode 100644
index 000000000..3e9a9a29d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0208_bare_dyn_types_with_leading_lifetime.rs
@@ -0,0 +1,2 @@
+type A = 'static + Trait;
+type B = S<'static + Trait>;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast
new file mode 100644
index 000000000..d5f97bad8
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rast
@@ -0,0 +1,175 @@
+SOURCE_FILE
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "A"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Fn"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "A"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ DYN_TRAIT_TYPE
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Fn"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Send"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "B"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Fn"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ WHITESPACE " "
+ RET_TYPE
+ THIN_ARROW "->"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ TYPE_ALIAS
+ TYPE_KW "type"
+ WHITESPACE " "
+ NAME
+ IDENT "C"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ DYN_TRAIT_TYPE
+ TYPE_BOUND_LIST
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Fn"
+ PARAM_LIST
+ L_PAREN "("
+ PARAM
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ WHITESPACE " "
+ RET_TYPE
+ THIN_ARROW "->"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ TYPE_BOUND
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Send"
+ R_ANGLE ">"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs
new file mode 100644
index 000000000..800002b1b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0209_bare_dyn_types_with_paren_as_generic_args.rs
@@ -0,0 +1,4 @@
+type A = S<Fn(i32)>;
+type A = S<Fn(i32) + Send>;
+type B = S<Fn(i32) -> i32>;
+type C = S<Fn(i32) -> i32 + Send>;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast
index ae08c0756..438025728 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rast
@@ -183,4 +183,273 @@ SOURCE_FILE
COMMENT "//---&*1 - --2 * 9;"
WHITESPACE "\n"
R_CURLY "}"
+ WHITESPACE "\n\n"
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "right_associative"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "c"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ WHITESPACE " "
+ PLUSEQ "+="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "c"
+ WHITESPACE " "
+ MINUSEQ "-="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "d"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ WHITESPACE " "
+ STAREQ "*="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "c"
+ WHITESPACE " "
+ SLASHEQ "/="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "d"
+ WHITESPACE " "
+ PERCENTEQ "%="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "e"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ WHITESPACE " "
+ AMPEQ "&="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "c"
+ WHITESPACE " "
+ PIPEEQ "|="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "d"
+ WHITESPACE " "
+ CARETEQ "^="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "e"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ WHITESPACE " "
+ SHLEQ "<<="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "c"
+ WHITESPACE " "
+ SHREQ ">>="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "d"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n\n"
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "mixed_associativity"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ COMMENT "// (a + b) = (c += ((d * e) = f))"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "c"
+ WHITESPACE " "
+ PLUSEQ "+="
+ WHITESPACE " "
+ BIN_EXPR
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "d"
+ WHITESPACE " "
+ STAR "*"
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "e"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "f"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs
index cc9598470..7ee3013a0 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0028_operator_binding_power.rs
@@ -12,3 +12,16 @@ fn binding_power() {
//1 = 2 .. 3;
//---&*1 - --2 * 9;
}
+
+fn right_associative() {
+ a = b = c;
+ a = b += c -= d;
+ a = b *= c /= d %= e;
+ a = b &= c |= d ^= e;
+ a = b <<= c >>= d;
+}
+
+fn mixed_associativity() {
+ // (a + b) = (c += ((d * e) = f))
+ a + b = c += d * e = f;
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast
index 44423581e..3965ae959 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0030_traits.rast
@@ -51,8 +51,9 @@ SOURCE_FILE
IDENT "i32"
SEMICOLON ";"
WHITESPACE " "
- LITERAL
- INT_NUMBER "1"
+ CONST_ARG
+ LITERAL
+ INT_NUMBER "1"
R_BRACK "]"
R_PAREN ")"
SEMICOLON ";"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast
index 3b02c3f96..f3c85b45b 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0043_complex_assignment.rast
@@ -24,8 +24,9 @@ SOURCE_FILE
IDENT "u8"
SEMICOLON ";"
WHITESPACE " "
- LITERAL
- INT_NUMBER "1"
+ CONST_ARG
+ LITERAL
+ INT_NUMBER "1"
R_BRACK "]"
WHITESPACE " "
R_CURLY "}"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast
index bef138071..fad574a47 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast
@@ -60,7 +60,7 @@ SOURCE_FILE
IDENT "doc"
TOKEN_TREE
L_PAREN "("
- STRING "\"Being validated is not affected by duplcates\""
+ STRING "\"Being validated is not affected by duplicates\""
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs
index f16c4566e..0969ea165 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rs
@@ -3,7 +3,7 @@ fn inner() {
//! As are ModuleDoc style comments
{
#![doc("Inner attributes are allowed in blocks used as statements")]
- #![doc("Being validated is not affected by duplcates")]
+ #![doc("Being validated is not affected by duplicates")]
//! As are ModuleDoc style comments
};
{
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast
index e8b836dfb..ce75c5518 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rast
@@ -168,42 +168,46 @@ SOURCE_FILE
WHITESPACE "\n "
EXPR_STMT
BIN_EXPR
- BIN_EXPR
- CALL_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Some"
- ARG_LIST
- L_PAREN "("
- RANGE_EXPR
- DOT2 ".."
- R_PAREN ")"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- METHOD_CALL_EXPR
- CALL_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Some"
- ARG_LIST
- L_PAREN "("
- LITERAL
- INT_NUMBER "0"
- R_PAREN ")"
- DOT "."
- WHITESPACE "\n "
- NAME_REF
- IDENT "Ok"
- ARG_LIST
- L_PAREN "("
- UNDERSCORE_EXPR
- UNDERSCORE "_"
- R_PAREN ")"
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Some"
+ ARG_LIST
+ L_PAREN "("
+ RANGE_EXPR
+ DOT2 ".."
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Some"
+ ARG_LIST
+ L_PAREN "("
+ LITERAL
+ INT_NUMBER "0"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ BIN_EXPR
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Ok"
+ ARG_LIST
+ L_PAREN "("
+ UNDERSCORE_EXPR
+ UNDERSCORE "_"
+ R_PAREN ")"
WHITESPACE " "
EQ "="
WHITESPACE " "
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs
index 9d3e86603..d223b11f2 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0072_destructuring_assignment.rs
@@ -4,7 +4,7 @@ fn foo() {
(_) = ..;
struct S { a: i32 }
S { .. } = S { ..S::default() };
- Some(..) = Some(0).
+ Some(..) = Some(0);
Ok(_) = 0;
let (a, b);
[a, .., b] = [1, .., 2];
diff --git a/src/tools/rust-analyzer/crates/paths/Cargo.toml b/src/tools/rust-analyzer/crates/paths/Cargo.toml
index e24e6ecef..28b54be52 100644
--- a/src/tools/rust-analyzer/crates/paths/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/paths/Cargo.toml
@@ -15,4 +15,4 @@ doctest = false
# Adding this dep sadly puts a lot of rust-analyzer crates after the
# serde-derive crate. Even though we don't activate the derive feature here,
# someone else in the crate graph certainly does!
-# serde = "1"
+# serde.workspace = true
diff --git a/src/tools/rust-analyzer/crates/paths/src/lib.rs b/src/tools/rust-analyzer/crates/paths/src/lib.rs
index 6ae23ac84..e0c20a414 100644
--- a/src/tools/rust-analyzer/crates/paths/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/paths/src/lib.rs
@@ -140,6 +140,11 @@ impl AbsPath {
self.0.parent().map(AbsPath::assert)
}
+ /// Equivalent of [`Path::join`] for `AbsPath` with an additional normalize step afterwards.
+ pub fn absolutize(&self, path: impl AsRef<Path>) -> AbsPathBuf {
+ self.join(path).normalize()
+ }
+
/// Equivalent of [`Path::join`] for `AbsPath`.
pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
self.as_ref().join(path).try_into().unwrap()
@@ -166,6 +171,10 @@ impl AbsPath {
AbsPathBuf::try_from(self.0.to_path_buf()).unwrap()
}
+ pub fn canonicalize(&self) -> ! {
+ panic!("We explicitly do not provide canonicalization API, as that is almost always a wrong solution, see #14430")
+ }
+
/// Equivalent of [`Path::strip_prefix`] for `AbsPath`.
///
/// Returns a relative path.
@@ -179,6 +188,13 @@ impl AbsPath {
self.0.ends_with(&suffix.0)
}
+ pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> {
+ Some((
+ self.file_stem()?.to_str()?,
+ self.extension().and_then(|extension| extension.to_str()),
+ ))
+ }
+
// region:delegate-methods
// Note that we deliberately don't implement `Deref<Target = Path>` here.
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
index 28469b832..d3486e755 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
@@ -19,9 +19,10 @@ object = { version = "0.30.2", default-features = false, features = [
"macho",
"pe",
] }
-serde = { version = "1.0.137", features = ["derive"] }
-serde_json = { version = "1.0.81", features = ["unbounded_depth"] }
+serde.workspace = true
+serde_json = { workspace = true, features = ["unbounded_depth"] }
tracing = "0.1.37"
+triomphe.workspace = true
memmap2 = "0.5.4"
snap = "1.1.0"
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
index 90d06967e..1603458f7 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs
@@ -12,11 +12,8 @@ mod process;
mod version;
use paths::AbsPathBuf;
-use std::{
- ffi::OsStr,
- fmt, io,
- sync::{Arc, Mutex},
-};
+use std::{fmt, io, sync::Mutex};
+use triomphe::Arc;
use serde::{Deserialize, Serialize};
@@ -54,18 +51,8 @@ pub struct MacroDylib {
}
impl MacroDylib {
- // FIXME: this is buggy due to TOCTOU, we should check the version in the
- // macro process instead.
- pub fn new(path: AbsPathBuf) -> io::Result<MacroDylib> {
- let _p = profile::span("MacroDylib::new");
-
- let info = version::read_dylib_info(&path)?;
- if info.version.0 < 1 || info.version.1 < 47 {
- let msg = format!("proc-macro {} built by {info:#?} is not supported by rust-analyzer, please update your Rust version.", path.display());
- return Err(io::Error::new(io::ErrorKind::InvalidData, msg));
- }
-
- Ok(MacroDylib { path })
+ pub fn new(path: AbsPathBuf) -> MacroDylib {
+ MacroDylib { path }
}
}
@@ -113,11 +100,8 @@ pub struct MacroPanic {
impl ProcMacroServer {
/// Spawns an external process as the proc macro server and returns a client connected to it.
- pub fn spawn(
- process_path: AbsPathBuf,
- args: impl IntoIterator<Item = impl AsRef<OsStr>> + Clone,
- ) -> io::Result<ProcMacroServer> {
- let process = ProcMacroProcessSrv::run(process_path, args)?;
+ pub fn spawn(process_path: AbsPathBuf) -> io::Result<ProcMacroServer> {
+ let process = ProcMacroProcessSrv::run(process_path)?;
Ok(ProcMacroServer { process: Arc::new(Mutex::new(process)) })
}
@@ -156,15 +140,16 @@ impl ProcMacro {
attr: Option<&tt::Subtree>,
env: Vec<(String, String)>,
) -> Result<Result<tt::Subtree, PanicMessage>, ServerError> {
+ let version = self.process.lock().unwrap_or_else(|e| e.into_inner()).version();
let current_dir = env
.iter()
.find(|(name, _)| name == "CARGO_MANIFEST_DIR")
.map(|(_, value)| value.clone());
let task = ExpandMacro {
- macro_body: FlatTree::new(subtree),
+ macro_body: FlatTree::new(subtree, version),
macro_name: self.name.to_string(),
- attributes: attr.map(FlatTree::new),
+ attributes: attr.map(|subtree| FlatTree::new(subtree, version)),
lib: self.dylib_path.to_path_buf().into(),
env,
current_dir,
@@ -173,7 +158,9 @@ impl ProcMacro {
let request = msg::Request::ExpandMacro(task);
let response = self.process.lock().unwrap_or_else(|e| e.into_inner()).send_task(request)?;
match response {
- msg::Response::ExpandMacro(it) => Ok(it.map(FlatTree::to_subtree)),
+ msg::Response::ExpandMacro(it) => {
+ Ok(it.map(|tree| FlatTree::to_subtree(tree, version)))
+ }
msg::Response::ListMacros(..) | msg::Response::ApiVersionCheck(..) => {
Err(ServerError { message: "unexpected response".to_string(), io: None })
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs
index 4040efe93..4b01643c2 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs
@@ -12,8 +12,12 @@ use crate::ProcMacroKind;
pub use crate::msg::flat::FlatTree;
+// The versions of the server protocol
pub const NO_VERSION_CHECK_VERSION: u32 = 0;
-pub const CURRENT_API_VERSION: u32 = 1;
+pub const VERSION_CHECK_VERSION: u32 = 1;
+pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2;
+
+pub const CURRENT_API_VERSION: u32 = ENCODE_CLOSE_SPAN_VERSION;
#[derive(Debug, Serialize, Deserialize)]
pub enum Request {
@@ -146,7 +150,7 @@ mod tests {
fn test_proc_macro_rpc_works() {
let tt = fixture_token_tree();
let task = ExpandMacro {
- macro_body: FlatTree::new(&tt),
+ macro_body: FlatTree::new(&tt, CURRENT_API_VERSION),
macro_name: Default::default(),
attributes: None,
lib: std::env::current_dir().unwrap(),
@@ -158,6 +162,6 @@ mod tests {
// println!("{}", json);
let back: ExpandMacro = serde_json::from_str(&json).unwrap();
- assert_eq!(tt, back.macro_body.to_subtree());
+ assert_eq!(tt, back.macro_body.to_subtree(CURRENT_API_VERSION));
}
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs
index fd3202e0b..44245336f 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/msg/flat.rs
@@ -39,7 +39,10 @@ use std::collections::{HashMap, VecDeque};
use serde::{Deserialize, Serialize};
-use crate::tt::{self, TokenId};
+use crate::{
+ msg::ENCODE_CLOSE_SPAN_VERSION,
+ tt::{self, TokenId},
+};
#[derive(Serialize, Deserialize, Debug)]
pub struct FlatTree {
@@ -52,7 +55,8 @@ pub struct FlatTree {
}
struct SubtreeRepr {
- id: tt::TokenId,
+ open: tt::TokenId,
+ close: tt::TokenId,
kind: tt::DelimiterKind,
tt: [u32; 2],
}
@@ -74,7 +78,7 @@ struct IdentRepr {
}
impl FlatTree {
- pub fn new(subtree: &tt::Subtree) -> FlatTree {
+ pub fn new(subtree: &tt::Subtree, version: u32) -> FlatTree {
let mut w = Writer {
string_table: HashMap::new(),
work: VecDeque::new(),
@@ -89,7 +93,11 @@ impl FlatTree {
w.write(subtree);
return FlatTree {
- subtree: write_vec(w.subtree, SubtreeRepr::write),
+ subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
+ write_vec(w.subtree, SubtreeRepr::write_with_close_span)
+ } else {
+ write_vec(w.subtree, SubtreeRepr::write)
+ },
literal: write_vec(w.literal, LiteralRepr::write),
punct: write_vec(w.punct, PunctRepr::write),
ident: write_vec(w.ident, IdentRepr::write),
@@ -102,9 +110,13 @@ impl FlatTree {
}
}
- pub fn to_subtree(self) -> tt::Subtree {
+ pub fn to_subtree(self, version: u32) -> tt::Subtree {
return Reader {
- subtree: read_vec(self.subtree, SubtreeRepr::read),
+ subtree: if version >= ENCODE_CLOSE_SPAN_VERSION {
+ read_vec(self.subtree, SubtreeRepr::read_with_close_span)
+ } else {
+ read_vec(self.subtree, SubtreeRepr::read)
+ },
literal: read_vec(self.literal, LiteralRepr::read),
punct: read_vec(self.punct, PunctRepr::read),
ident: read_vec(self.ident, IdentRepr::read),
@@ -130,9 +142,9 @@ impl SubtreeRepr {
tt::DelimiterKind::Brace => 2,
tt::DelimiterKind::Bracket => 3,
};
- [self.id.0, kind, self.tt[0], self.tt[1]]
+ [self.open.0, kind, self.tt[0], self.tt[1]]
}
- fn read([id, kind, lo, len]: [u32; 4]) -> SubtreeRepr {
+ fn read([open, kind, lo, len]: [u32; 4]) -> SubtreeRepr {
let kind = match kind {
0 => tt::DelimiterKind::Invisible,
1 => tt::DelimiterKind::Parenthesis,
@@ -140,7 +152,26 @@ impl SubtreeRepr {
3 => tt::DelimiterKind::Bracket,
other => panic!("bad kind {other}"),
};
- SubtreeRepr { id: TokenId(id), kind, tt: [lo, len] }
+ SubtreeRepr { open: TokenId(open), close: TokenId::UNSPECIFIED, kind, tt: [lo, len] }
+ }
+ fn write_with_close_span(self) -> [u32; 5] {
+ let kind = match self.kind {
+ tt::DelimiterKind::Invisible => 0,
+ tt::DelimiterKind::Parenthesis => 1,
+ tt::DelimiterKind::Brace => 2,
+ tt::DelimiterKind::Bracket => 3,
+ };
+ [self.open.0, self.close.0, kind, self.tt[0], self.tt[1]]
+ }
+ fn read_with_close_span([open, close, kind, lo, len]: [u32; 5]) -> SubtreeRepr {
+ let kind = match kind {
+ 0 => tt::DelimiterKind::Invisible,
+ 1 => tt::DelimiterKind::Parenthesis,
+ 2 => tt::DelimiterKind::Brace,
+ 3 => tt::DelimiterKind::Bracket,
+ other => panic!("bad kind {other}"),
+ };
+ SubtreeRepr { open: TokenId(open), close: TokenId(close), kind, tt: [lo, len] }
}
}
@@ -244,9 +275,10 @@ impl<'a> Writer<'a> {
fn enqueue(&mut self, subtree: &'a tt::Subtree) -> u32 {
let idx = self.subtree.len();
- let delimiter_id = subtree.delimiter.open;
+ let open = subtree.delimiter.open;
+ let close = subtree.delimiter.close;
let delimiter_kind = subtree.delimiter.kind;
- self.subtree.push(SubtreeRepr { id: delimiter_id, kind: delimiter_kind, tt: [!0, !0] });
+ self.subtree.push(SubtreeRepr { open, close, kind: delimiter_kind, tt: [!0, !0] });
self.work.push_back((idx, subtree));
idx as u32
}
@@ -277,11 +309,7 @@ impl Reader {
let repr = &self.subtree[i];
let token_trees = &self.token_tree[repr.tt[0] as usize..repr.tt[1] as usize];
let s = tt::Subtree {
- delimiter: tt::Delimiter {
- open: repr.id,
- close: TokenId::UNSPECIFIED,
- kind: repr.kind,
- },
+ delimiter: tt::Delimiter { open: repr.open, close: repr.close, kind: repr.kind },
token_trees: token_trees
.iter()
.copied()
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
index 1ccbd780f..9a20fa63e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/process.rs
@@ -1,7 +1,6 @@
//! Handle process life-time and message passing for proc-macro client
use std::{
- ffi::{OsStr, OsString},
io::{self, BufRead, BufReader, Write},
process::{Child, ChildStdin, ChildStdout, Command, Stdio},
};
@@ -23,12 +22,9 @@ pub(crate) struct ProcMacroProcessSrv {
}
impl ProcMacroProcessSrv {
- pub(crate) fn run(
- process_path: AbsPathBuf,
- args: impl IntoIterator<Item = impl AsRef<OsStr>> + Clone,
- ) -> io::Result<ProcMacroProcessSrv> {
+ pub(crate) fn run(process_path: AbsPathBuf) -> io::Result<ProcMacroProcessSrv> {
let create_srv = |null_stderr| {
- let mut process = Process::run(process_path.clone(), args.clone(), null_stderr)?;
+ let mut process = Process::run(process_path.clone(), null_stderr)?;
let (stdin, stdout) = process.stdio().expect("couldn't access child stdio");
io::Result::Ok(ProcMacroProcessSrv { _process: process, stdin, stdout, version: 0 })
@@ -56,6 +52,10 @@ impl ProcMacroProcessSrv {
}
}
+ pub(crate) fn version(&self) -> u32 {
+ self.version
+ }
+
pub(crate) fn version_check(&mut self) -> Result<u32, ServerError> {
let request = Request::ApiVersionCheck {};
let response = self.send_task(request)?;
@@ -96,13 +96,8 @@ struct Process {
}
impl Process {
- fn run(
- path: AbsPathBuf,
- args: impl IntoIterator<Item = impl AsRef<OsStr>>,
- null_stderr: bool,
- ) -> io::Result<Process> {
- let args: Vec<OsString> = args.into_iter().map(|s| s.as_ref().into()).collect();
- let child = JodChild(mk_child(&path, args, null_stderr)?);
+ fn run(path: AbsPathBuf, null_stderr: bool) -> io::Result<Process> {
+ let child = JodChild(mk_child(&path, null_stderr)?);
Ok(Process { child })
}
@@ -115,13 +110,8 @@ impl Process {
}
}
-fn mk_child(
- path: &AbsPath,
- args: impl IntoIterator<Item = impl AsRef<OsStr>>,
- null_stderr: bool,
-) -> io::Result<Child> {
+fn mk_child(path: &AbsPath, null_stderr: bool) -> io::Result<Child> {
Command::new(path.as_os_str())
- .args(args)
.env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs
index cf637ec35..13f67a012 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/version.rs
@@ -122,7 +122,7 @@ pub fn read_version(dylib_path: &AbsPath) -> io::Result<String> {
// https://github.com/rust-lang/rust/commit/0696e79f2740ad89309269b460579e548a5cd632
let snappy_portion = match version {
5 | 6 => &dot_rustc[8..],
- 7 => {
+ 7 | 8 => {
let len_bytes = &dot_rustc[8..12];
let data_len = u32::from_be_bytes(len_bytes.try_into().unwrap()) as usize;
&dot_rustc[12..data_len + 12]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml
index c402bc022..8f03c6ec7 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml
@@ -10,6 +10,7 @@ rust-version.workspace = true
[dependencies]
proc-macro-srv.workspace = true
+proc-macro-api.workspace = true
[features]
sysroot-abi = ["proc-macro-srv/sysroot-abi"]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
index ac9fa9f5a..bece19518 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
@@ -1,6 +1,6 @@
//! A standalone binary for `proc-macro-srv`.
-
-use proc_macro_srv::cli;
+//! Driver for proc macro server
+use std::io;
fn main() -> std::io::Result<()> {
let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE");
@@ -15,5 +15,37 @@ fn main() -> std::io::Result<()> {
}
}
- cli::run()
+ run()
+}
+
+#[cfg(not(feature = "sysroot-abi"))]
+fn run() -> io::Result<()> {
+ panic!("proc-macro-srv-cli requires the `sysroot-abi` feature to be enabled");
+}
+
+#[cfg(feature = "sysroot-abi")]
+fn run() -> io::Result<()> {
+ use proc_macro_api::msg::{self, Message};
+
+ let read_request = |buf: &mut String| msg::Request::read(&mut io::stdin().lock(), buf);
+
+ let write_response = |msg: msg::Response| msg.write(&mut io::stdout().lock());
+
+ let mut srv = proc_macro_srv::ProcMacroSrv::default();
+ let mut buf = String::new();
+
+ while let Some(req) = read_request(&mut buf)? {
+ let res = match req {
+ msg::Request::ListMacros { dylib_path } => {
+ msg::Response::ListMacros(srv.list_macros(&dylib_path))
+ }
+ msg::Request::ExpandMacro(task) => msg::Response::ExpandMacro(srv.expand(task)),
+ msg::Request::ApiVersionCheck {} => {
+ msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION)
+ }
+ };
+ write_response(res)?
+ }
+
+ Ok(())
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
index f7f07cfcb..d5eb157bf 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml
@@ -22,6 +22,7 @@ object = { version = "0.30.2", default-features = false, features = [
libloading = "0.7.3"
memmap2 = "0.5.4"
+stdx.workspace = true
tt.workspace = true
mbe.workspace = true
paths.workspace = true
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs
deleted file mode 100644
index 93805c893..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-//! Macro ABI for version 1.63 of rustc
-
-#[allow(dead_code)]
-#[doc(hidden)]
-mod proc_macro;
-
-#[allow(dead_code)]
-#[doc(hidden)]
-mod ra_server;
-
-use libloading::Library;
-use proc_macro_api::ProcMacroKind;
-
-use super::tt;
-use super::PanicMessage;
-
-pub use ra_server::TokenStream;
-
-pub(crate) struct Abi {
- exported_macros: Vec<proc_macro::bridge::client::ProcMacro>,
-}
-
-impl From<proc_macro::bridge::PanicMessage> for PanicMessage {
- fn from(p: proc_macro::bridge::PanicMessage) -> Self {
- Self { message: p.as_str().map(|s| s.to_string()) }
- }
-}
-
-impl Abi {
- pub unsafe fn from_lib(lib: &Library, symbol_name: String) -> Result<Abi, libloading::Error> {
- let macros: libloading::Symbol<'_, &&[proc_macro::bridge::client::ProcMacro]> =
- lib.get(symbol_name.as_bytes())?;
- Ok(Self { exported_macros: macros.to_vec() })
- }
-
- pub fn expand(
- &self,
- macro_name: &str,
- macro_body: &tt::Subtree,
- attributes: Option<&tt::Subtree>,
- ) -> Result<tt::Subtree, PanicMessage> {
- let parsed_body = TokenStream::with_subtree(macro_body.clone());
-
- let parsed_attributes =
- attributes.map_or(TokenStream::new(), |attr| TokenStream::with_subtree(attr.clone()));
-
- for proc_macro in &self.exported_macros {
- match proc_macro {
- proc_macro::bridge::client::ProcMacro::CustomDerive {
- trait_name, client, ..
- } if *trait_name == macro_name => {
- let res = client.run(
- &proc_macro::bridge::server::SameThread,
- ra_server::RustAnalyzer::default(),
- parsed_body,
- true,
- );
- return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
- }
- proc_macro::bridge::client::ProcMacro::Bang { name, client }
- if *name == macro_name =>
- {
- let res = client.run(
- &proc_macro::bridge::server::SameThread,
- ra_server::RustAnalyzer::default(),
- parsed_body,
- true,
- );
- return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
- }
- proc_macro::bridge::client::ProcMacro::Attr { name, client }
- if *name == macro_name =>
- {
- let res = client.run(
- &proc_macro::bridge::server::SameThread,
- ra_server::RustAnalyzer::default(),
- parsed_attributes,
- parsed_body,
- true,
- );
- return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
- }
- _ => continue,
- }
- }
-
- Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into())
- }
-
- pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
- self.exported_macros
- .iter()
- .map(|proc_macro| match proc_macro {
- proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
- (trait_name.to_string(), ProcMacroKind::CustomDerive)
- }
- proc_macro::bridge::client::ProcMacro::Bang { name, .. } => {
- (name.to_string(), ProcMacroKind::FuncLike)
- }
- proc_macro::bridge::client::ProcMacro::Attr { name, .. } => {
- (name.to_string(), ProcMacroKind::Attr)
- }
- })
- .collect()
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs
deleted file mode 100644
index 48030f8d8..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-//! Buffer management for same-process client<->server communication.
-
-use std::io::{self, Write};
-use std::mem;
-use std::ops::{Deref, DerefMut};
-use std::slice;
-
-#[repr(C)]
-pub struct Buffer {
- data: *mut u8,
- len: usize,
- capacity: usize,
- reserve: extern "C" fn(Buffer, usize) -> Buffer,
- drop: extern "C" fn(Buffer),
-}
-
-unsafe impl Sync for Buffer {}
-unsafe impl Send for Buffer {}
-
-impl Default for Buffer {
- #[inline]
- fn default() -> Self {
- Self::from(vec![])
- }
-}
-
-impl Deref for Buffer {
- type Target = [u8];
- #[inline]
- fn deref(&self) -> &[u8] {
- unsafe { slice::from_raw_parts(self.data as *const u8, self.len) }
- }
-}
-
-impl DerefMut for Buffer {
- #[inline]
- fn deref_mut(&mut self) -> &mut [u8] {
- unsafe { slice::from_raw_parts_mut(self.data, self.len) }
- }
-}
-
-impl Buffer {
- #[inline]
- pub(super) fn new() -> Self {
- Self::default()
- }
-
- #[inline]
- pub(super) fn clear(&mut self) {
- self.len = 0;
- }
-
- #[inline]
- pub(super) fn take(&mut self) -> Self {
- mem::take(self)
- }
-
- // We have the array method separate from extending from a slice. This is
- // because in the case of small arrays, codegen can be more efficient
- // (avoiding a memmove call). With extend_from_slice, LLVM at least
- // currently is not able to make that optimization.
- #[inline]
- pub(super) fn extend_from_array<const N: usize>(&mut self, xs: &[u8; N]) {
- if xs.len() > (self.capacity - self.len) {
- let b = self.take();
- *self = (b.reserve)(b, xs.len());
- }
- unsafe {
- xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len());
- self.len += xs.len();
- }
- }
-
- #[inline]
- pub(super) fn extend_from_slice(&mut self, xs: &[u8]) {
- if xs.len() > (self.capacity - self.len) {
- let b = self.take();
- *self = (b.reserve)(b, xs.len());
- }
- unsafe {
- xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len());
- self.len += xs.len();
- }
- }
-
- #[inline]
- pub(super) fn push(&mut self, v: u8) {
- // The code here is taken from Vec::push, and we know that reserve()
- // will panic if we're exceeding isize::MAX bytes and so there's no need
- // to check for overflow.
- if self.len == self.capacity {
- let b = self.take();
- *self = (b.reserve)(b, 1);
- }
- unsafe {
- *self.data.add(self.len) = v;
- self.len += 1;
- }
- }
-}
-
-impl Write for Buffer {
- #[inline]
- fn write(&mut self, xs: &[u8]) -> io::Result<usize> {
- self.extend_from_slice(xs);
- Ok(xs.len())
- }
-
- #[inline]
- fn write_all(&mut self, xs: &[u8]) -> io::Result<()> {
- self.extend_from_slice(xs);
- Ok(())
- }
-
- #[inline]
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
-}
-
-impl Drop for Buffer {
- #[inline]
- fn drop(&mut self) {
- let b = self.take();
- (b.drop)(b);
- }
-}
-
-impl From<Vec<u8>> for Buffer {
- fn from(mut v: Vec<u8>) -> Self {
- let (data, len, capacity) = (v.as_mut_ptr(), v.len(), v.capacity());
- mem::forget(v);
-
- // This utility function is nested in here because it can *only*
- // be safely called on `Buffer`s created by *this* `proc_macro`.
- fn to_vec(b: Buffer) -> Vec<u8> {
- unsafe {
- let Buffer { data, len, capacity, .. } = b;
- mem::forget(b);
- Vec::from_raw_parts(data, len, capacity)
- }
- }
-
- extern "C" fn reserve(b: Buffer, additional: usize) -> Buffer {
- let mut v = to_vec(b);
- v.reserve(additional);
- Buffer::from(v)
- }
-
- extern "C" fn drop(b: Buffer) {
- mem::drop(to_vec(b));
- }
-
- Buffer { data, len, capacity, reserve, drop }
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs
deleted file mode 100644
index b346c2c18..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs
+++ /dev/null
@@ -1,510 +0,0 @@
-//! Client-side types.
-
-use super::*;
-
-use std::marker::PhantomData;
-
-macro_rules! define_handles {
- (
- 'owned: $($oty:ident,)*
- 'interned: $($ity:ident,)*
- ) => {
- #[repr(C)]
- #[allow(non_snake_case)]
- pub struct HandleCounters {
- $($oty: AtomicUsize,)*
- $($ity: AtomicUsize,)*
- }
-
- impl HandleCounters {
- // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of
- // a wrapper `fn` pointer, once `const fn` can reference `static`s.
- extern "C" fn get() -> &'static Self {
- static COUNTERS: HandleCounters = HandleCounters {
- $($oty: AtomicUsize::new(1),)*
- $($ity: AtomicUsize::new(1),)*
- };
- &COUNTERS
- }
- }
-
- // FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
- #[repr(C)]
- #[allow(non_snake_case)]
- pub(super) struct HandleStore<S: server::Types> {
- $($oty: handle::OwnedStore<S::$oty>,)*
- $($ity: handle::InternedStore<S::$ity>,)*
- }
-
- impl<S: server::Types> HandleStore<S> {
- pub(super) fn new(handle_counters: &'static HandleCounters) -> Self {
- HandleStore {
- $($oty: handle::OwnedStore::new(&handle_counters.$oty),)*
- $($ity: handle::InternedStore::new(&handle_counters.$ity),)*
- }
- }
- }
-
- $(
- #[repr(C)]
- pub(crate) struct $oty {
- handle: handle::Handle,
- // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual
- // way of doing this, but that requires unstable features.
- // rust-analyzer uses this code and avoids unstable features.
- _marker: PhantomData<*mut ()>,
- }
-
- // Forward `Drop::drop` to the inherent `drop` method.
- impl Drop for $oty {
- fn drop(&mut self) {
- $oty {
- handle: self.handle,
- _marker: PhantomData,
- }.drop();
- }
- }
-
- impl<S> Encode<S> for $oty {
- fn encode(self, w: &mut Writer, s: &mut S) {
- let handle = self.handle;
- mem::forget(self);
- handle.encode(w, s);
- }
- }
-
- impl<S: server::Types> DecodeMut<'_, '_, HandleStore<server::MarkedTypes<S>>>
- for Marked<S::$oty, $oty>
- {
- fn decode(r: &mut Reader<'_>, s: &mut HandleStore<server::MarkedTypes<S>>) -> Self {
- s.$oty.take(handle::Handle::decode(r, &mut ()))
- }
- }
-
- impl<S> Encode<S> for &$oty {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.handle.encode(w, s);
- }
- }
-
- impl<'s, S: server::Types> Decode<'_, 's, HandleStore<server::MarkedTypes<S>>>
- for &'s Marked<S::$oty, $oty>
- {
- fn decode(r: &mut Reader<'_>, s: &'s HandleStore<server::MarkedTypes<S>>) -> Self {
- &s.$oty[handle::Handle::decode(r, &mut ())]
- }
- }
-
- impl<S> Encode<S> for &mut $oty {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.handle.encode(w, s);
- }
- }
-
- impl<'s, S: server::Types> DecodeMut<'_, 's, HandleStore<server::MarkedTypes<S>>>
- for &'s mut Marked<S::$oty, $oty>
- {
- fn decode(
- r: &mut Reader<'_>,
- s: &'s mut HandleStore<server::MarkedTypes<S>>
- ) -> Self {
- &mut s.$oty[handle::Handle::decode(r, &mut ())]
- }
- }
-
- impl<S: server::Types> Encode<HandleStore<server::MarkedTypes<S>>>
- for Marked<S::$oty, $oty>
- {
- fn encode(self, w: &mut Writer, s: &mut HandleStore<server::MarkedTypes<S>>) {
- s.$oty.alloc(self).encode(w, s);
- }
- }
-
- impl<S> DecodeMut<'_, '_, S> for $oty {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- $oty {
- handle: handle::Handle::decode(r, s),
- _marker: PhantomData,
- }
- }
- }
- )*
-
- $(
- #[repr(C)]
- #[derive(Copy, Clone, PartialEq, Eq, Hash)]
- pub(crate) struct $ity {
- handle: handle::Handle,
- // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual
- // way of doing this, but that requires unstable features.
- // rust-analyzer uses this code and avoids unstable features.
- _marker: PhantomData<*mut ()>,
- }
-
- impl<S> Encode<S> for $ity {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.handle.encode(w, s);
- }
- }
-
- impl<S: server::Types> DecodeMut<'_, '_, HandleStore<server::MarkedTypes<S>>>
- for Marked<S::$ity, $ity>
- {
- fn decode(r: &mut Reader<'_>, s: &mut HandleStore<server::MarkedTypes<S>>) -> Self {
- s.$ity.copy(handle::Handle::decode(r, &mut ()))
- }
- }
-
- impl<S: server::Types> Encode<HandleStore<server::MarkedTypes<S>>>
- for Marked<S::$ity, $ity>
- {
- fn encode(self, w: &mut Writer, s: &mut HandleStore<server::MarkedTypes<S>>) {
- s.$ity.alloc(self).encode(w, s);
- }
- }
-
- impl<S> DecodeMut<'_, '_, S> for $ity {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- $ity {
- handle: handle::Handle::decode(r, s),
- _marker: PhantomData,
- }
- }
- }
- )*
- }
-}
-define_handles! {
- 'owned:
- FreeFunctions,
- TokenStream,
- Group,
- Literal,
- SourceFile,
- MultiSpan,
- Diagnostic,
-
- 'interned:
- Punct,
- Ident,
- Span,
-}
-
-// FIXME(eddyb) generate these impls by pattern-matching on the
-// names of methods - also could use the presence of `fn drop`
-// to distinguish between 'owned and 'interned, above.
-// Alternatively, special "modes" could be listed of types in with_api
-// instead of pattern matching on methods, here and in server decl.
-
-impl Clone for TokenStream {
- fn clone(&self) -> Self {
- self.clone()
- }
-}
-
-impl Clone for Group {
- fn clone(&self) -> Self {
- self.clone()
- }
-}
-
-impl Clone for Literal {
- fn clone(&self) -> Self {
- self.clone()
- }
-}
-
-impl fmt::Debug for Literal {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Literal")
- // format the kind without quotes, as in `kind: Float`
- .field("kind", &format_args!("{}", &self.debug_kind()))
- .field("symbol", &self.symbol())
- // format `Some("...")` on one line even in {:#?} mode
- .field("suffix", &format_args!("{:?}", &self.suffix()))
- .field("span", &self.span())
- .finish()
- }
-}
-
-impl Clone for SourceFile {
- fn clone(&self) -> Self {
- self.clone()
- }
-}
-
-impl fmt::Debug for Span {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str(&self.debug())
- }
-}
-
-macro_rules! define_client_side {
- ($($name:ident {
- $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
- }),* $(,)?) => {
- $(impl $name {
- $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* {
- Bridge::with(|bridge| {
- let mut buf = bridge.cached_buffer.take();
-
- buf.clear();
- api_tags::Method::$name(api_tags::$name::$method).encode(&mut buf, &mut ());
- reverse_encode!(buf; $($arg),*);
-
- buf = bridge.dispatch.call(buf);
-
- let r = Result::<_, PanicMessage>::decode(&mut &buf[..], &mut ());
-
- bridge.cached_buffer = buf;
-
- r.unwrap_or_else(|e| panic::resume_unwind(e.into()))
- })
- })*
- })*
- }
-}
-with_api!(self, self, define_client_side);
-
-enum BridgeState<'a> {
- /// No server is currently connected to this client.
- NotConnected,
-
- /// A server is connected and available for requests.
- Connected(Bridge<'a>),
-
- /// Access to the bridge is being exclusively acquired
- /// (e.g., during `BridgeState::with`).
- InUse,
-}
-
-enum BridgeStateL {}
-
-impl<'a> scoped_cell::ApplyL<'a> for BridgeStateL {
- type Out = BridgeState<'a>;
-}
-
-thread_local! {
- static BRIDGE_STATE: scoped_cell::ScopedCell<BridgeStateL> =
- scoped_cell::ScopedCell::new(BridgeState::NotConnected);
-}
-
-impl BridgeState<'_> {
- /// Take exclusive control of the thread-local
- /// `BridgeState`, and pass it to `f`, mutably.
- /// The state will be restored after `f` exits, even
- /// by panic, including modifications made to it by `f`.
- ///
- /// N.B., while `f` is running, the thread-local state
- /// is `BridgeState::InUse`.
- fn with<R>(f: impl FnOnce(&mut BridgeState<'_>) -> R) -> R {
- BRIDGE_STATE.with(|state| {
- state.replace(BridgeState::InUse, |mut state| {
- // FIXME(#52812) pass `f` directly to `replace` when `RefMutL` is gone
- f(&mut state)
- })
- })
- }
-}
-
-impl Bridge<'_> {
- pub(crate) fn is_available() -> bool {
- BridgeState::with(|state| match state {
- BridgeState::Connected(_) | BridgeState::InUse => true,
- BridgeState::NotConnected => false,
- })
- }
-
- fn enter<R>(self, f: impl FnOnce() -> R) -> R {
- let force_show_panics = self.force_show_panics;
- // Hide the default panic output within `proc_macro` expansions.
- // NB. the server can't do this because it may use a different libstd.
- static HIDE_PANICS_DURING_EXPANSION: Once = Once::new();
- HIDE_PANICS_DURING_EXPANSION.call_once(|| {
- let prev = panic::take_hook();
- panic::set_hook(Box::new(move |info| {
- let show = BridgeState::with(|state| match state {
- BridgeState::NotConnected => true,
- BridgeState::Connected(_) | BridgeState::InUse => force_show_panics,
- });
- if show {
- prev(info)
- }
- }));
- });
-
- BRIDGE_STATE.with(|state| state.set(BridgeState::Connected(self), f))
- }
-
- fn with<R>(f: impl FnOnce(&mut Bridge<'_>) -> R) -> R {
- BridgeState::with(|state| match state {
- BridgeState::NotConnected => {
- panic!("procedural macro API is used outside of a procedural macro");
- }
- BridgeState::InUse => {
- panic!("procedural macro API is used while it's already in use");
- }
- BridgeState::Connected(bridge) => f(bridge),
- })
- }
-}
-
-/// A client-side RPC entry-point, which may be using a different `proc_macro`
-/// from the one used by the server, but can be invoked compatibly.
-///
-/// Note that the (phantom) `I` ("input") and `O` ("output") type parameters
-/// decorate the `Client<I, O>` with the RPC "interface" of the entry-point, but
-/// do not themselves participate in ABI, at all, only facilitate type-checking.
-///
-/// E.g. `Client<TokenStream, TokenStream>` is the common proc macro interface,
-/// used for `#[proc_macro] fn foo(input: TokenStream) -> TokenStream`,
-/// indicating that the RPC input and output will be serialized token streams,
-/// and forcing the use of APIs that take/return `S::TokenStream`, server-side.
-#[repr(C)]
-pub struct Client<I, O> {
- // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of
- // a wrapper `fn` pointer, once `const fn` can reference `static`s.
- pub(super) get_handle_counters: extern "C" fn() -> &'static HandleCounters,
-
- pub(super) run: extern "C" fn(Bridge<'_>) -> Buffer,
-
- pub(super) _marker: PhantomData<fn(I) -> O>,
-}
-
-impl<I, O> Copy for Client<I, O> {}
-impl<I, O> Clone for Client<I, O> {
- fn clone(&self) -> Self {
- *self
- }
-}
-
-/// Client-side helper for handling client panics, entering the bridge,
-/// deserializing input and serializing output.
-// FIXME(eddyb) maybe replace `Bridge::enter` with this?
-fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
- mut bridge: Bridge<'_>,
- f: impl FnOnce(A) -> R,
-) -> Buffer {
- // The initial `cached_buffer` contains the input.
- let mut buf = bridge.cached_buffer.take();
-
- panic::catch_unwind(panic::AssertUnwindSafe(|| {
- bridge.enter(|| {
- let reader = &mut &buf[..];
- let input = A::decode(reader, &mut ());
-
- // Put the `cached_buffer` back in the `Bridge`, for requests.
- Bridge::with(|bridge| bridge.cached_buffer = buf.take());
-
- let output = f(input);
-
- // Take the `cached_buffer` back out, for the output value.
- buf = Bridge::with(|bridge| bridge.cached_buffer.take());
-
- // HACK(eddyb) Separate encoding a success value (`Ok(output)`)
- // from encoding a panic (`Err(e: PanicMessage)`) to avoid
- // having handles outside the `bridge.enter(|| ...)` scope, and
- // to catch panics that could happen while encoding the success.
- //
- // Note that panics should be impossible beyond this point, but
- // this is defensively trying to avoid any accidental panicking
- // reaching the `extern "C"` (which should `abort` but might not
- // at the moment, so this is also potentially preventing UB).
- buf.clear();
- Ok::<_, ()>(output).encode(&mut buf, &mut ());
- })
- }))
- .map_err(PanicMessage::from)
- .unwrap_or_else(|e| {
- buf.clear();
- Err::<(), _>(e).encode(&mut buf, &mut ());
- });
- buf
-}
-
-impl Client<super::super::TokenStream, super::super::TokenStream> {
- pub const fn expand1(
- f: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy,
- ) -> Self {
- Client {
- get_handle_counters: HandleCounters::get,
- run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
- run_client(bridge, |input| f(super::super::TokenStream(input)).0)
- }),
- _marker: PhantomData,
- }
- }
-}
-
-impl Client<(super::super::TokenStream, super::super::TokenStream), super::super::TokenStream> {
- pub const fn expand2(
- f: impl Fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream
- + Copy,
- ) -> Self {
- Client {
- get_handle_counters: HandleCounters::get,
- run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
- run_client(bridge, |(input, input2)| {
- f(super::super::TokenStream(input), super::super::TokenStream(input2)).0
- })
- }),
- _marker: PhantomData,
- }
- }
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub enum ProcMacro {
- CustomDerive {
- trait_name: &'static str,
- attributes: &'static [&'static str],
- client: Client<super::super::TokenStream, super::super::TokenStream>,
- },
-
- Attr {
- name: &'static str,
- client: Client<
- (super::super::TokenStream, super::super::TokenStream),
- super::super::TokenStream,
- >,
- },
-
- Bang {
- name: &'static str,
- client: Client<super::super::TokenStream, super::super::TokenStream>,
- },
-}
-
-impl ProcMacro {
- pub fn name(&self) -> &'static str {
- match self {
- ProcMacro::CustomDerive { trait_name, .. } => trait_name,
- ProcMacro::Attr { name, .. } => name,
- ProcMacro::Bang { name, .. } => name,
- }
- }
-
- pub const fn custom_derive(
- trait_name: &'static str,
- attributes: &'static [&'static str],
- expand: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy,
- ) -> Self {
- ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
- }
-
- pub const fn attr(
- name: &'static str,
- expand: impl Fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream
- + Copy,
- ) -> Self {
- ProcMacro::Attr { name, client: Client::expand2(expand) }
- }
-
- pub const fn bang(
- name: &'static str,
- expand: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy,
- ) -> Self {
- ProcMacro::Bang { name, client: Client::expand1(expand) }
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs
deleted file mode 100644
index d371ae3ce..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-//! Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`.
-
-use std::marker::PhantomData;
-
-#[repr(C)]
-pub struct Closure<'a, A, R> {
- call: unsafe extern "C" fn(*mut Env, A) -> R,
- env: *mut Env,
- // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing
- // this, but that requires unstable features. rust-analyzer uses this code
- // and avoids unstable features.
- //
- // The `'a` lifetime parameter represents the lifetime of `Env`.
- _marker: PhantomData<*mut &'a mut ()>,
-}
-
-struct Env;
-
-impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> {
- fn from(f: &'a mut F) -> Self {
- unsafe extern "C" fn call<A, R, F: FnMut(A) -> R>(env: *mut Env, arg: A) -> R {
- (*(env as *mut _ as *mut F))(arg)
- }
- Closure { call: call::<A, R, F>, env: f as *mut _ as *mut Env, _marker: PhantomData }
- }
-}
-
-impl<'a, A, R> Closure<'a, A, R> {
- pub fn call(&mut self, arg: A) -> R {
- unsafe { (self.call)(self.env, arg) }
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs
deleted file mode 100644
index c219a9465..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-//! Server-side handles and storage for per-handle data.
-
-use std::collections::{BTreeMap, HashMap};
-use std::hash::{BuildHasher, Hash};
-use std::num::NonZeroU32;
-use std::ops::{Index, IndexMut};
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-pub(super) type Handle = NonZeroU32;
-
-/// A store that associates values of type `T` with numeric handles. A value can
-/// be looked up using its handle.
-pub(super) struct OwnedStore<T: 'static> {
- counter: &'static AtomicUsize,
- data: BTreeMap<Handle, T>,
-}
-
-impl<T> OwnedStore<T> {
- pub(super) fn new(counter: &'static AtomicUsize) -> Self {
- // Ensure the handle counter isn't 0, which would panic later,
- // when `NonZeroU32::new` (aka `Handle::new`) is called in `alloc`.
- assert_ne!(counter.load(Ordering::SeqCst), 0);
-
- OwnedStore { counter, data: BTreeMap::new() }
- }
-}
-
-impl<T> OwnedStore<T> {
- pub(super) fn alloc(&mut self, x: T) -> Handle {
- let counter = self.counter.fetch_add(1, Ordering::SeqCst);
- let handle = Handle::new(counter as u32).expect("`proc_macro` handle counter overflowed");
- assert!(self.data.insert(handle, x).is_none());
- handle
- }
-
- pub(super) fn take(&mut self, h: Handle) -> T {
- self.data.remove(&h).expect("use-after-free in `proc_macro` handle")
- }
-}
-
-impl<T> Index<Handle> for OwnedStore<T> {
- type Output = T;
- fn index(&self, h: Handle) -> &T {
- self.data.get(&h).expect("use-after-free in `proc_macro` handle")
- }
-}
-
-impl<T> IndexMut<Handle> for OwnedStore<T> {
- fn index_mut(&mut self, h: Handle) -> &mut T {
- self.data.get_mut(&h).expect("use-after-free in `proc_macro` handle")
- }
-}
-
-// HACK(eddyb) deterministic `std::collections::hash_map::RandomState` replacement
-// that doesn't require adding any dependencies to `proc_macro` (like `rustc-hash`).
-#[derive(Clone)]
-struct NonRandomState;
-
-impl BuildHasher for NonRandomState {
- type Hasher = std::collections::hash_map::DefaultHasher;
- #[inline]
- fn build_hasher(&self) -> Self::Hasher {
- Self::Hasher::new()
- }
-}
-
-/// Like `OwnedStore`, but avoids storing any value more than once.
-pub(super) struct InternedStore<T: 'static> {
- owned: OwnedStore<T>,
- interner: HashMap<T, Handle, NonRandomState>,
-}
-
-impl<T: Copy + Eq + Hash> InternedStore<T> {
- pub(super) fn new(counter: &'static AtomicUsize) -> Self {
- InternedStore {
- owned: OwnedStore::new(counter),
- interner: HashMap::with_hasher(NonRandomState),
- }
- }
-
- pub(super) fn alloc(&mut self, x: T) -> Handle {
- let owned = &mut self.owned;
- *self.interner.entry(x).or_insert_with(|| owned.alloc(x))
- }
-
- pub(super) fn copy(&mut self, h: Handle) -> T {
- self.owned[h]
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs
deleted file mode 100644
index 4967da493..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs
+++ /dev/null
@@ -1,451 +0,0 @@
-//! Internal interface for communicating between a `proc_macro` client
-//! (a proc macro crate) and a `proc_macro` server (a compiler front-end).
-//!
-//! Serialization (with C ABI buffers) and unique integer handles are employed
-//! to allow safely interfacing between two copies of `proc_macro` built
-//! (from the same source) by different compilers with potentially mismatching
-//! Rust ABIs (e.g., stage0/bin/rustc vs stage1/bin/rustc during bootstrap).
-
-#![deny(unsafe_code)]
-
-pub use super::{Delimiter, Level, LineColumn, Spacing};
-use std::fmt;
-use std::hash::Hash;
-use std::marker;
-use std::mem;
-use std::ops::Bound;
-use std::panic;
-use std::sync::atomic::AtomicUsize;
-use std::sync::Once;
-use std::thread;
-
-/// Higher-order macro describing the server RPC API, allowing automatic
-/// generation of type-safe Rust APIs, both client-side and server-side.
-///
-/// `with_api!(MySelf, my_self, my_macro)` expands to:
-/// ```rust,ignore (pseudo-code)
-/// my_macro! {
-/// // ...
-/// Literal {
-/// // ...
-/// fn character(ch: char) -> MySelf::Literal;
-/// // ...
-/// fn span(my_self: &MySelf::Literal) -> MySelf::Span;
-/// fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span);
-/// },
-/// // ...
-/// }
-/// ```
-///
-/// The first two arguments serve to customize the arguments names
-/// and argument/return types, to enable several different usecases:
-///
-/// If `my_self` is just `self`, then each `fn` signature can be used
-/// as-is for a method. If it's anything else (`self_` in practice),
-/// then the signatures don't have a special `self` argument, and
-/// can, therefore, have a different one introduced.
-///
-/// If `MySelf` is just `Self`, then the types are only valid inside
-/// a trait or a trait impl, where the trait has associated types
-/// for each of the API types. If non-associated types are desired,
-/// a module name (`self` in practice) can be used instead of `Self`.
-macro_rules! with_api {
- ($S:ident, $self:ident, $m:ident) => {
- $m! {
- FreeFunctions {
- fn drop($self: $S::FreeFunctions);
- fn track_env_var(var: &str, value: Option<&str>);
- fn track_path(path: &str);
- },
- TokenStream {
- fn drop($self: $S::TokenStream);
- fn clone($self: &$S::TokenStream) -> $S::TokenStream;
- fn is_empty($self: &$S::TokenStream) -> bool;
- fn expand_expr($self: &$S::TokenStream) -> Result<$S::TokenStream, ()>;
- fn from_str(src: &str) -> $S::TokenStream;
- fn to_string($self: &$S::TokenStream) -> String;
- fn from_token_tree(
- tree: TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>,
- ) -> $S::TokenStream;
- fn concat_trees(
- base: Option<$S::TokenStream>,
- trees: Vec<TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>>,
- ) -> $S::TokenStream;
- fn concat_streams(
- base: Option<$S::TokenStream>,
- streams: Vec<$S::TokenStream>,
- ) -> $S::TokenStream;
- fn into_trees(
- $self: $S::TokenStream
- ) -> Vec<TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>>;
- },
- Group {
- fn drop($self: $S::Group);
- fn clone($self: &$S::Group) -> $S::Group;
- fn new(delimiter: Delimiter, stream: Option<$S::TokenStream>) -> $S::Group;
- fn delimiter($self: &$S::Group) -> Delimiter;
- fn stream($self: &$S::Group) -> $S::TokenStream;
- fn span($self: &$S::Group) -> $S::Span;
- fn span_open($self: &$S::Group) -> $S::Span;
- fn span_close($self: &$S::Group) -> $S::Span;
- fn set_span($self: &mut $S::Group, span: $S::Span);
- },
- Punct {
- fn new(ch: char, spacing: Spacing) -> $S::Punct;
- fn as_char($self: $S::Punct) -> char;
- fn spacing($self: $S::Punct) -> Spacing;
- fn span($self: $S::Punct) -> $S::Span;
- fn with_span($self: $S::Punct, span: $S::Span) -> $S::Punct;
- },
- Ident {
- fn new(string: &str, span: $S::Span, is_raw: bool) -> $S::Ident;
- fn span($self: $S::Ident) -> $S::Span;
- fn with_span($self: $S::Ident, span: $S::Span) -> $S::Ident;
- },
- Literal {
- fn drop($self: $S::Literal);
- fn clone($self: &$S::Literal) -> $S::Literal;
- fn from_str(s: &str) -> Result<$S::Literal, ()>;
- fn to_string($self: &$S::Literal) -> String;
- fn debug_kind($self: &$S::Literal) -> String;
- fn symbol($self: &$S::Literal) -> String;
- fn suffix($self: &$S::Literal) -> Option<String>;
- fn integer(n: &str) -> $S::Literal;
- fn typed_integer(n: &str, kind: &str) -> $S::Literal;
- fn float(n: &str) -> $S::Literal;
- fn f32(n: &str) -> $S::Literal;
- fn f64(n: &str) -> $S::Literal;
- fn string(string: &str) -> $S::Literal;
- fn character(ch: char) -> $S::Literal;
- fn byte_string(bytes: &[u8]) -> $S::Literal;
- fn span($self: &$S::Literal) -> $S::Span;
- fn set_span($self: &mut $S::Literal, span: $S::Span);
- fn subspan(
- $self: &$S::Literal,
- start: Bound<usize>,
- end: Bound<usize>,
- ) -> Option<$S::Span>;
- },
- SourceFile {
- fn drop($self: $S::SourceFile);
- fn clone($self: &$S::SourceFile) -> $S::SourceFile;
- fn eq($self: &$S::SourceFile, other: &$S::SourceFile) -> bool;
- fn path($self: &$S::SourceFile) -> String;
- fn is_real($self: &$S::SourceFile) -> bool;
- },
- MultiSpan {
- fn drop($self: $S::MultiSpan);
- fn new() -> $S::MultiSpan;
- fn push($self: &mut $S::MultiSpan, span: $S::Span);
- },
- Diagnostic {
- fn drop($self: $S::Diagnostic);
- fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic;
- fn sub(
- $self: &mut $S::Diagnostic,
- level: Level,
- msg: &str,
- span: $S::MultiSpan,
- );
- fn emit($self: $S::Diagnostic);
- },
- Span {
- fn debug($self: $S::Span) -> String;
- fn def_site() -> $S::Span;
- fn call_site() -> $S::Span;
- fn mixed_site() -> $S::Span;
- fn source_file($self: $S::Span) -> $S::SourceFile;
- fn parent($self: $S::Span) -> Option<$S::Span>;
- fn source($self: $S::Span) -> $S::Span;
- fn start($self: $S::Span) -> LineColumn;
- fn end($self: $S::Span) -> LineColumn;
- fn before($self: $S::Span) -> $S::Span;
- fn after($self: $S::Span) -> $S::Span;
- fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>;
- fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span;
- fn source_text($self: $S::Span) -> Option<String>;
- fn save_span($self: $S::Span) -> usize;
- fn recover_proc_macro_span(id: usize) -> $S::Span;
- },
- }
- };
-}
-
-// FIXME(eddyb) this calls `encode` for each argument, but in reverse,
-// to match the ordering in `reverse_decode`.
-macro_rules! reverse_encode {
- ($writer:ident;) => {};
- ($writer:ident; $first:ident $(, $rest:ident)*) => {
- reverse_encode!($writer; $($rest),*);
- $first.encode(&mut $writer, &mut ());
- }
-}
-
-// FIXME(eddyb) this calls `decode` for each argument, but in reverse,
-// to avoid borrow conflicts from borrows started by `&mut` arguments.
-macro_rules! reverse_decode {
- ($reader:ident, $s:ident;) => {};
- ($reader:ident, $s:ident; $first:ident: $first_ty:ty $(, $rest:ident: $rest_ty:ty)*) => {
- reverse_decode!($reader, $s; $($rest: $rest_ty),*);
- let $first = <$first_ty>::decode(&mut $reader, $s);
- }
-}
-
-#[allow(unsafe_code)]
-mod buffer;
-#[forbid(unsafe_code)]
-pub mod client;
-#[allow(unsafe_code)]
-mod closure;
-#[forbid(unsafe_code)]
-mod handle;
-#[macro_use]
-#[forbid(unsafe_code)]
-mod rpc;
-#[allow(unsafe_code)]
-mod scoped_cell;
-#[allow(unsafe_code)]
-mod selfless_reify;
-#[forbid(unsafe_code)]
-pub mod server;
-
-use buffer::Buffer;
-pub use rpc::PanicMessage;
-use rpc::{Decode, DecodeMut, Encode, Reader, Writer};
-
-/// An active connection between a server and a client.
-/// The server creates the bridge (`Bridge::run_server` in `server.rs`),
-/// then passes it to the client through the function pointer in the `run`
-/// field of `client::Client`. The client holds its copy of the `Bridge`
-/// in TLS during its execution (`Bridge::{enter, with}` in `client.rs`).
-#[repr(C)]
-pub struct Bridge<'a> {
- /// Reusable buffer (only `clear`-ed, never shrunk), primarily
- /// used for making requests, but also for passing input to client.
- cached_buffer: Buffer,
-
- /// Server-side function that the client uses to make requests.
- dispatch: closure::Closure<'a, Buffer, Buffer>,
-
- /// If 'true', always invoke the default panic hook
- force_show_panics: bool,
-
- // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing
- // this, but that requires unstable features. rust-analyzer uses this code
- // and avoids unstable features.
- _marker: marker::PhantomData<*mut ()>,
-}
-
-#[forbid(unsafe_code)]
-#[allow(non_camel_case_types)]
-mod api_tags {
- use super::rpc::{DecodeMut, Encode, Reader, Writer};
-
- macro_rules! declare_tags {
- ($($name:ident {
- $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
- }),* $(,)?) => {
- $(
- pub(super) enum $name {
- $($method),*
- }
- rpc_encode_decode!(enum $name { $($method),* });
- )*
-
- pub(super) enum Method {
- $($name($name)),*
- }
- rpc_encode_decode!(enum Method { $($name(m)),* });
- }
- }
- with_api!(self, self, declare_tags);
-}
-
-/// Helper to wrap associated types to allow trait impl dispatch.
-/// That is, normally a pair of impls for `T::Foo` and `T::Bar`
-/// can overlap, but if the impls are, instead, on types like
-/// `Marked<T::Foo, Foo>` and `Marked<T::Bar, Bar>`, they can't.
-trait Mark {
- type Unmarked;
- fn mark(unmarked: Self::Unmarked) -> Self;
-}
-
-/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details).
-trait Unmark {
- type Unmarked;
- fn unmark(self) -> Self::Unmarked;
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-struct Marked<T, M> {
- value: T,
- _marker: marker::PhantomData<M>,
-}
-
-impl<T, M> Mark for Marked<T, M> {
- type Unmarked = T;
- fn mark(unmarked: Self::Unmarked) -> Self {
- Marked { value: unmarked, _marker: marker::PhantomData }
- }
-}
-impl<T, M> Unmark for Marked<T, M> {
- type Unmarked = T;
- fn unmark(self) -> Self::Unmarked {
- self.value
- }
-}
-impl<'a, T, M> Unmark for &'a Marked<T, M> {
- type Unmarked = &'a T;
- fn unmark(self) -> Self::Unmarked {
- &self.value
- }
-}
-impl<'a, T, M> Unmark for &'a mut Marked<T, M> {
- type Unmarked = &'a mut T;
- fn unmark(self) -> Self::Unmarked {
- &mut self.value
- }
-}
-
-impl<T: Mark> Mark for Vec<T> {
- type Unmarked = Vec<T::Unmarked>;
- fn mark(unmarked: Self::Unmarked) -> Self {
- // Should be a no-op due to std's in-place collect optimizations.
- unmarked.into_iter().map(T::mark).collect()
- }
-}
-impl<T: Unmark> Unmark for Vec<T> {
- type Unmarked = Vec<T::Unmarked>;
- fn unmark(self) -> Self::Unmarked {
- // Should be a no-op due to std's in-place collect optimizations.
- self.into_iter().map(T::unmark).collect()
- }
-}
-
-macro_rules! mark_noop {
- ($($ty:ty),* $(,)?) => {
- $(
- impl Mark for $ty {
- type Unmarked = Self;
- fn mark(unmarked: Self::Unmarked) -> Self {
- unmarked
- }
- }
- impl Unmark for $ty {
- type Unmarked = Self;
- fn unmark(self) -> Self::Unmarked {
- self
- }
- }
- )*
- }
-}
-mark_noop! {
- (),
- bool,
- char,
- &'_ [u8],
- &'_ str,
- String,
- usize,
- Delimiter,
- Level,
- LineColumn,
- Spacing,
-}
-
-rpc_encode_decode!(
- enum Delimiter {
- Parenthesis,
- Brace,
- Bracket,
- None,
- }
-);
-rpc_encode_decode!(
- enum Level {
- Error,
- Warning,
- Note,
- Help,
- }
-);
-rpc_encode_decode!(struct LineColumn { line, column });
-rpc_encode_decode!(
- enum Spacing {
- Alone,
- Joint,
- }
-);
-
-macro_rules! mark_compound {
- (enum $name:ident <$($T:ident),+> { $($variant:ident $(($field:ident))?),* $(,)? }) => {
- impl<$($T: Mark),+> Mark for $name <$($T),+> {
- type Unmarked = $name <$($T::Unmarked),+>;
- fn mark(unmarked: Self::Unmarked) -> Self {
- match unmarked {
- $($name::$variant $(($field))? => {
- $name::$variant $((Mark::mark($field)))?
- })*
- }
- }
- }
-
- impl<$($T: Unmark),+> Unmark for $name <$($T),+> {
- type Unmarked = $name <$($T::Unmarked),+>;
- fn unmark(self) -> Self::Unmarked {
- match self {
- $($name::$variant $(($field))? => {
- $name::$variant $((Unmark::unmark($field)))?
- })*
- }
- }
- }
- }
-}
-
-macro_rules! compound_traits {
- ($($t:tt)*) => {
- rpc_encode_decode!($($t)*);
- mark_compound!($($t)*);
- };
-}
-
-compound_traits!(
- enum Bound<T> {
- Included(x),
- Excluded(x),
- Unbounded,
- }
-);
-
-compound_traits!(
- enum Option<T> {
- Some(t),
- None,
- }
-);
-
-compound_traits!(
- enum Result<T, E> {
- Ok(t),
- Err(e),
- }
-);
-
-#[derive(Clone)]
-pub enum TokenTree<G, P, I, L> {
- Group(G),
- Punct(P),
- Ident(I),
- Literal(L),
-}
-
-compound_traits!(
- enum TokenTree<G, P, I, L> {
- Group(tt),
- Punct(tt),
- Ident(tt),
- Literal(tt),
- }
-);
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs
deleted file mode 100644
index e9d7a46c0..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs
+++ /dev/null
@@ -1,304 +0,0 @@
-//! Serialization for client-server communication.
-
-use std::any::Any;
-use std::char;
-use std::io::Write;
-use std::num::NonZeroU32;
-use std::str;
-
-pub(super) type Writer = super::buffer::Buffer;
-
-pub(super) trait Encode<S>: Sized {
- fn encode(self, w: &mut Writer, s: &mut S);
-}
-
-pub(super) type Reader<'a> = &'a [u8];
-
-pub(super) trait Decode<'a, 's, S>: Sized {
- fn decode(r: &mut Reader<'a>, s: &'s S) -> Self;
-}
-
-pub(super) trait DecodeMut<'a, 's, S>: Sized {
- fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self;
-}
-
-macro_rules! rpc_encode_decode {
- (le $ty:ty) => {
- impl<S> Encode<S> for $ty {
- fn encode(self, w: &mut Writer, _: &mut S) {
- w.extend_from_array(&self.to_le_bytes());
- }
- }
-
- impl<S> DecodeMut<'_, '_, S> for $ty {
- fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
- const N: usize = ::std::mem::size_of::<$ty>();
-
- let mut bytes = [0; N];
- bytes.copy_from_slice(&r[..N]);
- *r = &r[N..];
-
- Self::from_le_bytes(bytes)
- }
- }
- };
- (struct $name:ident $(<$($T:ident),+>)? { $($field:ident),* $(,)? }) => {
- impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? {
- fn encode(self, w: &mut Writer, s: &mut S) {
- $(self.$field.encode(w, s);)*
- }
- }
-
- impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S>
- for $name $(<$($T),+>)?
- {
- fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
- $name {
- $($field: DecodeMut::decode(r, s)),*
- }
- }
- }
- };
- (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => {
- impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? {
- fn encode(self, w: &mut Writer, s: &mut S) {
- // HACK(eddyb): `Tag` enum duplicated between the
- // two impls as there's no other place to stash it.
- #[allow(non_upper_case_globals)]
- mod tag {
- #[repr(u8)] enum Tag { $($variant),* }
-
- $(pub const $variant: u8 = Tag::$variant as u8;)*
- }
-
- match self {
- $($name::$variant $(($field))* => {
- tag::$variant.encode(w, s);
- $($field.encode(w, s);)*
- })*
- }
- }
- }
-
- impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S>
- for $name $(<$($T),+>)?
- {
- fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
- // HACK(eddyb): `Tag` enum duplicated between the
- // two impls as there's no other place to stash it.
- #[allow(non_upper_case_globals)]
- mod tag {
- #[repr(u8)] enum Tag { $($variant),* }
-
- $(pub const $variant: u8 = Tag::$variant as u8;)*
- }
-
- match u8::decode(r, s) {
- $(tag::$variant => {
- $(let $field = DecodeMut::decode(r, s);)*
- $name::$variant $(($field))*
- })*
- _ => unreachable!(),
- }
- }
- }
- }
-}
-
-impl<S> Encode<S> for () {
- fn encode(self, _: &mut Writer, _: &mut S) {}
-}
-
-impl<S> DecodeMut<'_, '_, S> for () {
- fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {}
-}
-
-impl<S> Encode<S> for u8 {
- fn encode(self, w: &mut Writer, _: &mut S) {
- w.push(self);
- }
-}
-
-impl<S> DecodeMut<'_, '_, S> for u8 {
- fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
- let x = r[0];
- *r = &r[1..];
- x
- }
-}
-
-rpc_encode_decode!(le u32);
-rpc_encode_decode!(le usize);
-
-impl<S> Encode<S> for bool {
- fn encode(self, w: &mut Writer, s: &mut S) {
- (self as u8).encode(w, s);
- }
-}
-
-impl<S> DecodeMut<'_, '_, S> for bool {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- match u8::decode(r, s) {
- 0 => false,
- 1 => true,
- _ => unreachable!(),
- }
- }
-}
-
-impl<S> Encode<S> for char {
- fn encode(self, w: &mut Writer, s: &mut S) {
- (self as u32).encode(w, s);
- }
-}
-
-impl<S> DecodeMut<'_, '_, S> for char {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- char::from_u32(u32::decode(r, s)).unwrap()
- }
-}
-
-impl<S> Encode<S> for NonZeroU32 {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.get().encode(w, s);
- }
-}
-
-impl<S> DecodeMut<'_, '_, S> for NonZeroU32 {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- Self::new(u32::decode(r, s)).unwrap()
- }
-}
-
-impl<S, A: Encode<S>, B: Encode<S>> Encode<S> for (A, B) {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.0.encode(w, s);
- self.1.encode(w, s);
- }
-}
-
-impl<'a, S, A: for<'s> DecodeMut<'a, 's, S>, B: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S>
- for (A, B)
-{
- fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
- (DecodeMut::decode(r, s), DecodeMut::decode(r, s))
- }
-}
-
-impl<S> Encode<S> for &[u8] {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.len().encode(w, s);
- w.write_all(self).unwrap();
- }
-}
-
-impl<'a, S> DecodeMut<'a, '_, S> for &'a [u8] {
- fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
- let len = usize::decode(r, s);
- let xs = &r[..len];
- *r = &r[len..];
- xs
- }
-}
-
-impl<S> Encode<S> for &str {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.as_bytes().encode(w, s);
- }
-}
-
-impl<'a, S> DecodeMut<'a, '_, S> for &'a str {
- fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
- str::from_utf8(<&[u8]>::decode(r, s)).unwrap()
- }
-}
-
-impl<S> Encode<S> for String {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self[..].encode(w, s);
- }
-}
-
-impl<S> DecodeMut<'_, '_, S> for String {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- <&str>::decode(r, s).to_string()
- }
-}
-
-impl<S, T: Encode<S>> Encode<S> for Vec<T> {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.len().encode(w, s);
- for x in self {
- x.encode(w, s);
- }
- }
-}
-
-impl<'a, S, T: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> for Vec<T> {
- fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
- let len = usize::decode(r, s);
- let mut vec = Vec::with_capacity(len);
- for _ in 0..len {
- vec.push(T::decode(r, s));
- }
- vec
- }
-}
-
-/// Simplified version of panic payloads, ignoring
-/// types other than `&'static str` and `String`.
-pub enum PanicMessage {
- StaticStr(&'static str),
- String(String),
- Unknown,
-}
-
-impl From<Box<dyn Any + Send>> for PanicMessage {
- fn from(payload: Box<dyn Any + Send + 'static>) -> Self {
- if let Some(s) = payload.downcast_ref::<&'static str>() {
- return PanicMessage::StaticStr(s);
- }
- if let Ok(s) = payload.downcast::<String>() {
- return PanicMessage::String(*s);
- }
- PanicMessage::Unknown
- }
-}
-
-impl Into<Box<dyn Any + Send>> for PanicMessage {
- fn into(self) -> Box<dyn Any + Send> {
- match self {
- PanicMessage::StaticStr(s) => Box::new(s),
- PanicMessage::String(s) => Box::new(s),
- PanicMessage::Unknown => {
- struct UnknownPanicMessage;
- Box::new(UnknownPanicMessage)
- }
- }
- }
-}
-
-impl PanicMessage {
- pub fn as_str(&self) -> Option<&str> {
- match self {
- PanicMessage::StaticStr(s) => Some(s),
- PanicMessage::String(s) => Some(s),
- PanicMessage::Unknown => None,
- }
- }
-}
-
-impl<S> Encode<S> for PanicMessage {
- fn encode(self, w: &mut Writer, s: &mut S) {
- self.as_str().encode(w, s);
- }
-}
-
-impl<S> DecodeMut<'_, '_, S> for PanicMessage {
- fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
- match Option::<String>::decode(r, s) {
- Some(s) => PanicMessage::String(s),
- None => PanicMessage::Unknown,
- }
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs
deleted file mode 100644
index 2cde1f65a..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-//! `Cell` variant for (scoped) existential lifetimes.
-
-use std::cell::Cell;
-use std::mem;
-use std::ops::{Deref, DerefMut};
-
-/// Type lambda application, with a lifetime.
-#[allow(unused_lifetimes)]
-pub trait ApplyL<'a> {
- type Out;
-}
-
-/// Type lambda taking a lifetime, i.e., `Lifetime -> Type`.
-pub trait LambdaL: for<'a> ApplyL<'a> {}
-
-impl<T: for<'a> ApplyL<'a>> LambdaL for T {}
-
-// HACK(eddyb) work around projection limitations with a newtype
-// FIXME(#52812) replace with `&'a mut <T as ApplyL<'b>>::Out`
-pub struct RefMutL<'a, 'b, T: LambdaL>(&'a mut <T as ApplyL<'b>>::Out);
-
-impl<'a, 'b, T: LambdaL> Deref for RefMutL<'a, 'b, T> {
- type Target = <T as ApplyL<'b>>::Out;
- fn deref(&self) -> &Self::Target {
- self.0
- }
-}
-
-impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> {
- fn deref_mut(&mut self) -> &mut Self::Target {
- self.0
- }
-}
-
-pub struct ScopedCell<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>);
-
-impl<T: LambdaL> ScopedCell<T> {
- pub const fn new(value: <T as ApplyL<'static>>::Out) -> Self {
- ScopedCell(Cell::new(value))
- }
-
- /// Sets the value in `self` to `replacement` while
- /// running `f`, which gets the old value, mutably.
- /// The old value will be restored after `f` exits, even
- /// by panic, including modifications made to it by `f`.
- pub fn replace<'a, R>(
- &self,
- replacement: <T as ApplyL<'a>>::Out,
- f: impl for<'b, 'c> FnOnce(RefMutL<'b, 'c, T>) -> R,
- ) -> R {
- /// Wrapper that ensures that the cell always gets filled
- /// (with the original state, optionally changed by `f`),
- /// even if `f` had panicked.
- struct PutBackOnDrop<'a, T: LambdaL> {
- cell: &'a ScopedCell<T>,
- value: Option<<T as ApplyL<'static>>::Out>,
- }
-
- impl<'a, T: LambdaL> Drop for PutBackOnDrop<'a, T> {
- fn drop(&mut self) {
- self.cell.0.set(self.value.take().unwrap());
- }
- }
-
- let mut put_back_on_drop = PutBackOnDrop {
- cell: self,
- value: Some(self.0.replace(unsafe {
- let erased = mem::transmute_copy(&replacement);
- mem::forget(replacement);
- erased
- })),
- };
-
- f(RefMutL(put_back_on_drop.value.as_mut().unwrap()))
- }
-
- /// Sets the value in `self` to `value` while running `f`.
- pub fn set<R>(&self, value: <T as ApplyL<'_>>::Out, f: impl FnOnce() -> R) -> R {
- self.replace(value, |_| f())
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs
deleted file mode 100644
index 4ee4bb87c..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-//! Abstraction for creating `fn` pointers from any callable that *effectively*
-//! has the equivalent of implementing `Default`, even if the compiler neither
-//! provides `Default` nor allows reifying closures (i.e. creating `fn` pointers)
-//! other than those with absolutely no captures.
-//!
-//! More specifically, for a closure-like type to be "effectively `Default`":
-//! * it must be a ZST (zero-sized type): no information contained within, so
-//! that `Default`'s return value (if it were implemented) is unambiguous
-//! * it must be `Copy`: no captured "unique ZST tokens" or any other similar
-//! types that would make duplicating values at will unsound
-//! * combined with the ZST requirement, this confers a kind of "telecopy"
-//! ability: similar to `Copy`, but without keeping the value around, and
-//! instead "reconstructing" it (a noop given it's a ZST) when needed
-//! * it must be *provably* inhabited: no captured uninhabited types or any
-//! other types that cannot be constructed by the user of this abstraction
-//! * the proof is a value of the closure-like type itself, in a sense the
-//! "seed" for the "telecopy" process made possible by ZST + `Copy`
-//! * this requirement is the only reason an abstraction limited to a specific
-//! usecase is required: ZST + `Copy` can be checked with *at worst* a panic
-//! at the "attempted `::default()` call" time, but that doesn't guarantee
-//! that the value can be soundly created, and attempting to use the typical
-//! "proof ZST token" approach leads yet again to having a ZST + `Copy` type
-//! that is not proof of anything without a value (i.e. isomorphic to a
-//! newtype of the type it's trying to prove the inhabitation of)
-//!
-//! A more flexible (and safer) solution to the general problem could exist once
-//! `const`-generic parameters can have type parameters in their types:
-//!
-//! ```rust,ignore (needs future const-generics)
-//! extern "C" fn ffi_wrapper<
-//! A, R,
-//! F: Fn(A) -> R,
-//! const f: F, // <-- this `const`-generic is not yet allowed
-//! >(arg: A) -> R {
-//! f(arg)
-//! }
-//! ```
-
-use std::mem;
-
-// FIXME(eddyb) this could be `trait` impls except for the `const fn` requirement.
-macro_rules! define_reify_functions {
- ($(
- fn $name:ident $(<$($param:ident),*>)?
- for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty;
- )+) => {
- $(pub const fn $name<
- $($($param,)*)?
- F: Fn($($arg_ty),*) -> $ret_ty + Copy
- >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty {
- // FIXME(eddyb) describe the `F` type (e.g. via `type_name::<F>`) once panic
- // formatting becomes possible in `const fn`.
- assert!(mem::size_of::<F>() == 0, "selfless_reify: closure must be zero-sized");
-
- $(extern $abi)? fn wrapper<
- $($($param,)*)?
- F: Fn($($arg_ty),*) -> $ret_ty + Copy
- >($($arg: $arg_ty),*) -> $ret_ty {
- let f = unsafe {
- // SAFETY: `F` satisfies all criteria for "out of thin air"
- // reconstructability (see module-level doc comment).
- mem::MaybeUninit::<F>::uninit().assume_init()
- };
- f($($arg),*)
- }
- let _f_proof = f;
- wrapper::<
- $($($param,)*)?
- F
- >
- })+
- }
-}
-
-define_reify_functions! {
- fn _reify_to_extern_c_fn_unary<A, R> for extern "C" fn(arg: A) -> R;
-
- // HACK(eddyb) this abstraction is used with `for<'a> fn(Bridge<'a>) -> T`
- // but that doesn't work with just `reify_to_extern_c_fn_unary` because of
- // the `fn` pointer type being "higher-ranked" (i.e. the `for<'a>` binder).
- // FIXME(eddyb) try to remove the lifetime from `Bridge`, that'd help.
- fn reify_to_extern_c_fn_hrt_bridge<R> for extern "C" fn(bridge: super::Bridge<'_>) -> R;
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs
deleted file mode 100644
index 0fb3c6985..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs
+++ /dev/null
@@ -1,332 +0,0 @@
-//! Server-side traits.
-
-use super::*;
-
-// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
-use super::client::HandleStore;
-
-pub trait Types {
- type FreeFunctions: 'static;
- type TokenStream: 'static + Clone;
- type Group: 'static + Clone;
- type Punct: 'static + Copy + Eq + Hash;
- type Ident: 'static + Copy + Eq + Hash;
- type Literal: 'static + Clone;
- type SourceFile: 'static + Clone;
- type MultiSpan: 'static;
- type Diagnostic: 'static;
- type Span: 'static + Copy + Eq + Hash;
-}
-
-/// Declare an associated fn of one of the traits below, adding necessary
-/// default bodies.
-macro_rules! associated_fn {
- (fn drop(&mut self, $arg:ident: $arg_ty:ty)) =>
- (fn drop(&mut self, $arg: $arg_ty) { mem::drop($arg) });
-
- (fn clone(&mut self, $arg:ident: $arg_ty:ty) -> $ret_ty:ty) =>
- (fn clone(&mut self, $arg: $arg_ty) -> $ret_ty { $arg.clone() });
-
- ($($item:tt)*) => ($($item)*;)
-}
-
-macro_rules! declare_server_traits {
- ($($name:ident {
- $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
- }),* $(,)?) => {
- $(pub trait $name: Types {
- $(associated_fn!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)*
- })*
-
- pub trait Server: Types $(+ $name)* {}
- impl<S: Types $(+ $name)*> Server for S {}
- }
-}
-with_api!(Self, self_, declare_server_traits);
-
-pub(super) struct MarkedTypes<S: Types>(S);
-
-macro_rules! define_mark_types_impls {
- ($($name:ident {
- $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
- }),* $(,)?) => {
- impl<S: Types> Types for MarkedTypes<S> {
- $(type $name = Marked<S::$name, client::$name>;)*
- }
-
- $(impl<S: $name> $name for MarkedTypes<S> {
- $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)? {
- <_>::mark($name::$method(&mut self.0, $($arg.unmark()),*))
- })*
- })*
- }
-}
-with_api!(Self, self_, define_mark_types_impls);
-
-struct Dispatcher<S: Types> {
- handle_store: HandleStore<S>,
- server: S,
-}
-
-macro_rules! define_dispatcher_impl {
- ($($name:ident {
- $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
- }),* $(,)?) => {
- // FIXME(eddyb) `pub` only for `ExecutionStrategy` below.
- pub trait DispatcherTrait {
- // HACK(eddyb) these are here to allow `Self::$name` to work below.
- $(type $name;)*
- fn dispatch(&mut self, buf: Buffer) -> Buffer;
- }
-
- impl<S: Server> DispatcherTrait for Dispatcher<MarkedTypes<S>> {
- $(type $name = <MarkedTypes<S> as Types>::$name;)*
- fn dispatch(&mut self, mut buf: Buffer) -> Buffer {
- let Dispatcher { handle_store, server } = self;
-
- let mut reader = &buf[..];
- match api_tags::Method::decode(&mut reader, &mut ()) {
- $(api_tags::Method::$name(m) => match m {
- $(api_tags::$name::$method => {
- let mut call_method = || {
- reverse_decode!(reader, handle_store; $($arg: $arg_ty),*);
- $name::$method(server, $($arg),*)
- };
- // HACK(eddyb) don't use `panic::catch_unwind` in a panic.
- // If client and server happen to use the same `libstd`,
- // `catch_unwind` asserts that the panic counter was 0,
- // even when the closure passed to it didn't panic.
- let r = if thread::panicking() {
- Ok(call_method())
- } else {
- panic::catch_unwind(panic::AssertUnwindSafe(call_method))
- .map_err(PanicMessage::from)
- };
-
- buf.clear();
- r.encode(&mut buf, handle_store);
- })*
- }),*
- }
- buf
- }
- }
- }
-}
-with_api!(Self, self_, define_dispatcher_impl);
-
-pub trait ExecutionStrategy {
- fn run_bridge_and_client(
- &self,
- dispatcher: &mut impl DispatcherTrait,
- input: Buffer,
- run_client: extern "C" fn(Bridge<'_>) -> Buffer,
- force_show_panics: bool,
- ) -> Buffer;
-}
-
-pub struct SameThread;
-
-impl ExecutionStrategy for SameThread {
- fn run_bridge_and_client(
- &self,
- dispatcher: &mut impl DispatcherTrait,
- input: Buffer,
- run_client: extern "C" fn(Bridge<'_>) -> Buffer,
- force_show_panics: bool,
- ) -> Buffer {
- let mut dispatch = |buf| dispatcher.dispatch(buf);
-
- run_client(Bridge {
- cached_buffer: input,
- dispatch: (&mut dispatch).into(),
- force_show_panics,
- _marker: marker::PhantomData,
- })
- }
-}
-
-// NOTE(eddyb) Two implementations are provided, the second one is a bit
-// faster but neither is anywhere near as fast as same-thread execution.
-
-pub struct CrossThread1;
-
-impl ExecutionStrategy for CrossThread1 {
- fn run_bridge_and_client(
- &self,
- dispatcher: &mut impl DispatcherTrait,
- input: Buffer,
- run_client: extern "C" fn(Bridge<'_>) -> Buffer,
- force_show_panics: bool,
- ) -> Buffer {
- use std::sync::mpsc::channel;
-
- let (req_tx, req_rx) = channel();
- let (res_tx, res_rx) = channel();
-
- let join_handle = thread::spawn(move || {
- let mut dispatch = |buf| {
- req_tx.send(buf).unwrap();
- res_rx.recv().unwrap()
- };
-
- run_client(Bridge {
- cached_buffer: input,
- dispatch: (&mut dispatch).into(),
- force_show_panics,
- _marker: marker::PhantomData,
- })
- });
-
- for b in req_rx {
- res_tx.send(dispatcher.dispatch(b)).unwrap();
- }
-
- join_handle.join().unwrap()
- }
-}
-
-pub struct CrossThread2;
-
-impl ExecutionStrategy for CrossThread2 {
- fn run_bridge_and_client(
- &self,
- dispatcher: &mut impl DispatcherTrait,
- input: Buffer,
- run_client: extern "C" fn(Bridge<'_>) -> Buffer,
- force_show_panics: bool,
- ) -> Buffer {
- use std::sync::{Arc, Mutex};
-
- enum State<T> {
- Req(T),
- Res(T),
- }
-
- let mut state = Arc::new(Mutex::new(State::Res(Buffer::new())));
-
- let server_thread = thread::current();
- let state2 = state.clone();
- let join_handle = thread::spawn(move || {
- let mut dispatch = |b| {
- *state2.lock().unwrap() = State::Req(b);
- server_thread.unpark();
- loop {
- thread::park();
- if let State::Res(b) = &mut *state2.lock().unwrap() {
- break b.take();
- }
- }
- };
-
- let r = run_client(Bridge {
- cached_buffer: input,
- dispatch: (&mut dispatch).into(),
- force_show_panics,
- _marker: marker::PhantomData,
- });
-
- // Wake up the server so it can exit the dispatch loop.
- drop(state2);
- server_thread.unpark();
-
- r
- });
-
- // Check whether `state2` was dropped, to know when to stop.
- while Arc::get_mut(&mut state).is_none() {
- thread::park();
- let mut b = match &mut *state.lock().unwrap() {
- State::Req(b) => b.take(),
- _ => continue,
- };
- b = dispatcher.dispatch(b.take());
- *state.lock().unwrap() = State::Res(b);
- join_handle.thread().unpark();
- }
-
- join_handle.join().unwrap()
- }
-}
-
-fn run_server<
- S: Server,
- I: Encode<HandleStore<MarkedTypes<S>>>,
- O: for<'a, 's> DecodeMut<'a, 's, HandleStore<MarkedTypes<S>>>,
->(
- strategy: &impl ExecutionStrategy,
- handle_counters: &'static client::HandleCounters,
- server: S,
- input: I,
- run_client: extern "C" fn(Bridge<'_>) -> Buffer,
- force_show_panics: bool,
-) -> Result<O, PanicMessage> {
- let mut dispatcher =
- Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) };
-
- let mut buf = Buffer::new();
- input.encode(&mut buf, &mut dispatcher.handle_store);
-
- buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics);
-
- Result::decode(&mut &buf[..], &mut dispatcher.handle_store)
-}
-
-impl client::Client<super::super::TokenStream, super::super::TokenStream> {
- pub fn run<S>(
- &self,
- strategy: &impl ExecutionStrategy,
- server: S,
- input: S::TokenStream,
- force_show_panics: bool,
- ) -> Result<S::TokenStream, PanicMessage>
- where
- S: Server,
- S::TokenStream: Default,
- {
- let client::Client { get_handle_counters, run, _marker } = *self;
- run_server(
- strategy,
- get_handle_counters(),
- server,
- <MarkedTypes<S> as Types>::TokenStream::mark(input),
- run,
- force_show_panics,
- )
- .map(|s| <Option<<MarkedTypes<S> as Types>::TokenStream>>::unmark(s).unwrap_or_default())
- }
-}
-
-impl
- client::Client<
- (super::super::TokenStream, super::super::TokenStream),
- super::super::TokenStream,
- >
-{
- pub fn run<S>(
- &self,
- strategy: &impl ExecutionStrategy,
- server: S,
- input: S::TokenStream,
- input2: S::TokenStream,
- force_show_panics: bool,
- ) -> Result<S::TokenStream, PanicMessage>
- where
- S: Server,
- S::TokenStream: Default,
- {
- let client::Client { get_handle_counters, run, _marker } = *self;
- run_server(
- strategy,
- get_handle_counters(),
- server,
- (
- <MarkedTypes<S> as Types>::TokenStream::mark(input),
- <MarkedTypes<S> as Types>::TokenStream::mark(input2),
- ),
- run,
- force_show_panics,
- )
- .map(|s| <Option<<MarkedTypes<S> as Types>::TokenStream>>::unmark(s).unwrap_or_default())
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs
deleted file mode 100644
index 3fade2dc4..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs
+++ /dev/null
@@ -1,166 +0,0 @@
-//! lib-proc-macro diagnostic
-//!
-//! Copy from <https://github.com/rust-lang/rust/blob/e45d9973b2665897a768312e971b82cc62633103/src/libproc_macro/diagnostic.rs>
-//! augmented with removing unstable features
-
-use super::Span;
-
-/// An enum representing a diagnostic level.
-#[derive(Copy, Clone, Debug)]
-#[non_exhaustive]
-pub enum Level {
- /// An error.
- Error,
- /// A warning.
- Warning,
- /// A note.
- Note,
- /// A help message.
- Help,
-}
-
-/// Trait implemented by types that can be converted into a set of `Span`s.
-pub trait MultiSpan {
- /// Converts `self` into a `Vec<Span>`.
- fn into_spans(self) -> Vec<Span>;
-}
-
-impl MultiSpan for Span {
- fn into_spans(self) -> Vec<Span> {
- vec![self]
- }
-}
-
-impl MultiSpan for Vec<Span> {
- fn into_spans(self) -> Vec<Span> {
- self
- }
-}
-
-impl<'a> MultiSpan for &'a [Span] {
- fn into_spans(self) -> Vec<Span> {
- self.to_vec()
- }
-}
-
-/// A structure representing a diagnostic message and associated children
-/// messages.
-#[derive(Clone, Debug)]
-pub struct Diagnostic {
- level: Level,
- message: String,
- spans: Vec<Span>,
- children: Vec<Diagnostic>,
-}
-
-macro_rules! diagnostic_child_methods {
- ($spanned:ident, $regular:ident, $level:expr) => {
- #[doc = concat!("Adds a new child diagnostics message to `self` with the [`",
- stringify!($level), "`] level, and the given `spans` and `message`.")]
- pub fn $spanned<S, T>(mut self, spans: S, message: T) -> Diagnostic
- where
- S: MultiSpan,
- T: Into<String>,
- {
- self.children.push(Diagnostic::spanned(spans, $level, message));
- self
- }
-
- #[doc = concat!("Adds a new child diagnostic message to `self` with the [`",
- stringify!($level), "`] level, and the given `message`.")]
- pub fn $regular<T: Into<String>>(mut self, message: T) -> Diagnostic {
- self.children.push(Diagnostic::new($level, message));
- self
- }
- };
-}
-
-/// Iterator over the children diagnostics of a `Diagnostic`.
-#[derive(Debug, Clone)]
-pub struct Children<'a>(std::slice::Iter<'a, Diagnostic>);
-
-impl<'a> Iterator for Children<'a> {
- type Item = &'a Diagnostic;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.0.next()
- }
-}
-
-impl Diagnostic {
- /// Creates a new diagnostic with the given `level` and `message`.
- pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
- Diagnostic { level, message: message.into(), spans: vec![], children: vec![] }
- }
-
- /// Creates a new diagnostic with the given `level` and `message` pointing to
- /// the given set of `spans`.
- pub fn spanned<S, T>(spans: S, level: Level, message: T) -> Diagnostic
- where
- S: MultiSpan,
- T: Into<String>,
- {
- Diagnostic { level, message: message.into(), spans: spans.into_spans(), children: vec![] }
- }
-
- diagnostic_child_methods!(span_error, error, Level::Error);
- diagnostic_child_methods!(span_warning, warning, Level::Warning);
- diagnostic_child_methods!(span_note, note, Level::Note);
- diagnostic_child_methods!(span_help, help, Level::Help);
-
- /// Returns the diagnostic `level` for `self`.
- pub fn level(&self) -> Level {
- self.level
- }
-
- /// Sets the level in `self` to `level`.
- pub fn set_level(&mut self, level: Level) {
- self.level = level;
- }
-
- /// Returns the message in `self`.
- pub fn message(&self) -> &str {
- &self.message
- }
-
- /// Sets the message in `self` to `message`.
- pub fn set_message<T: Into<String>>(&mut self, message: T) {
- self.message = message.into();
- }
-
- /// Returns the `Span`s in `self`.
- pub fn spans(&self) -> &[Span] {
- &self.spans
- }
-
- /// Sets the `Span`s in `self` to `spans`.
- pub fn set_spans<S: MultiSpan>(&mut self, spans: S) {
- self.spans = spans.into_spans();
- }
-
- /// Returns an iterator over the children diagnostics of `self`.
- pub fn children(&self) -> Children<'_> {
- Children(self.children.iter())
- }
-
- /// Emit the diagnostic.
- pub fn emit(self) {
- fn to_internal(spans: Vec<Span>) -> super::bridge::client::MultiSpan {
- let mut multi_span = super::bridge::client::MultiSpan::new();
- for span in spans {
- multi_span.push(span.0);
- }
- multi_span
- }
-
- let mut diag = super::bridge::client::Diagnostic::new(
- self.level,
- &self.message[..],
- to_internal(self.spans),
- );
- for c in self.children {
- diag.sub(c.level, &c.message[..], to_internal(c.spans));
- }
- diag.emit();
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs
deleted file mode 100644
index 89bd10da5..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs
+++ /dev/null
@@ -1,1106 +0,0 @@
-//! A support library for macro authors when defining new macros.
-//!
-//! This library, provided by the standard distribution, provides the types
-//! consumed in the interfaces of procedurally defined macro definitions such as
-//! function-like macros `#[proc_macro]`, macro attributes `#[proc_macro_attribute]` and
-//! custom derive attributes`#[proc_macro_derive]`.
-//!
-//! See [the book] for more.
-//!
-//! [the book]: ../book/ch19-06-macros.html#procedural-macros-for-generating-code-from-attributes
-
-#[doc(hidden)]
-pub mod bridge;
-
-mod diagnostic;
-
-pub use diagnostic::{Diagnostic, Level, MultiSpan};
-
-use std::cmp::Ordering;
-use std::ops::RangeBounds;
-use std::path::PathBuf;
-use std::str::FromStr;
-use std::{error, fmt, iter, mem};
-
-/// Determines whether proc_macro has been made accessible to the currently
-/// running program.
-///
-/// The proc_macro crate is only intended for use inside the implementation of
-/// procedural macros. All the functions in this crate panic if invoked from
-/// outside of a procedural macro, such as from a build script or unit test or
-/// ordinary Rust binary.
-///
-/// With consideration for Rust libraries that are designed to support both
-/// macro and non-macro use cases, `proc_macro::is_available()` provides a
-/// non-panicking way to detect whether the infrastructure required to use the
-/// API of proc_macro is presently available. Returns true if invoked from
-/// inside of a procedural macro, false if invoked from any other binary.
-pub fn is_available() -> bool {
- bridge::Bridge::is_available()
-}
-
-/// The main type provided by this crate, representing an abstract stream of
-/// tokens, or, more specifically, a sequence of token trees.
-/// The type provide interfaces for iterating over those token trees and, conversely,
-/// collecting a number of token trees into one stream.
-///
-/// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]`
-/// and `#[proc_macro_derive]` definitions.
-#[derive(Clone)]
-pub struct TokenStream(Option<bridge::client::TokenStream>);
-
-/// Error returned from `TokenStream::from_str`.
-#[non_exhaustive]
-#[derive(Debug)]
-pub struct LexError;
-
-impl fmt::Display for LexError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str("cannot parse string into token stream")
- }
-}
-
-impl error::Error for LexError {}
-
-/// Error returned from `TokenStream::expand_expr`.
-#[non_exhaustive]
-#[derive(Debug)]
-pub struct ExpandError;
-
-impl fmt::Display for ExpandError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str("macro expansion failed")
- }
-}
-
-impl error::Error for ExpandError {}
-
-impl TokenStream {
- /// Returns an empty `TokenStream` containing no token trees.
- pub fn new() -> TokenStream {
- TokenStream(None)
- }
-
- /// Checks if this `TokenStream` is empty.
- pub fn is_empty(&self) -> bool {
- self.0.as_ref().map(|h| h.is_empty()).unwrap_or(true)
- }
-
- /// Parses this `TokenStream` as an expression and attempts to expand any
- /// macros within it. Returns the expanded `TokenStream`.
- ///
- /// Currently only expressions expanding to literals will succeed, although
- /// this may be relaxed in the future.
- ///
- /// NOTE: In error conditions, `expand_expr` may leave macros unexpanded,
- /// report an error, failing compilation, and/or return an `Err(..)`. The
- /// specific behavior for any error condition, and what conditions are
- /// considered errors, is unspecified and may change in the future.
- pub fn expand_expr(&self) -> Result<TokenStream, ExpandError> {
- let stream = self.0.as_ref().ok_or(ExpandError)?;
- match bridge::client::TokenStream::expand_expr(stream) {
- Ok(stream) => Ok(TokenStream(Some(stream))),
- Err(_) => Err(ExpandError),
- }
- }
-}
-
-/// Attempts to break the string into tokens and parse those tokens into a token stream.
-/// May fail for a number of reasons, for example, if the string contains unbalanced delimiters
-/// or characters not existing in the language.
-/// All tokens in the parsed stream get `Span::call_site()` spans.
-///
-/// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to
-/// change these errors into `LexError`s later.
-impl FromStr for TokenStream {
- type Err = LexError;
-
- fn from_str(src: &str) -> Result<TokenStream, LexError> {
- Ok(TokenStream(Some(bridge::client::TokenStream::from_str(src))))
- }
-}
-
-/// Prints the token stream as a string that is supposed to be losslessly convertible back
-/// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s
-/// with `Delimiter::None` delimiters and negative numeric literals.
-impl fmt::Display for TokenStream {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
-/// Prints token in a form convenient for debugging.
-impl fmt::Debug for TokenStream {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.write_str("TokenStream ")?;
- f.debug_list().entries(self.clone()).finish()
- }
-}
-
-impl Default for TokenStream {
- fn default() -> Self {
- TokenStream::new()
- }
-}
-
-pub use quote::{quote, quote_span};
-
-fn tree_to_bridge_tree(
- tree: TokenTree,
-) -> bridge::TokenTree<
- bridge::client::Group,
- bridge::client::Punct,
- bridge::client::Ident,
- bridge::client::Literal,
-> {
- match tree {
- TokenTree::Group(tt) => bridge::TokenTree::Group(tt.0),
- TokenTree::Punct(tt) => bridge::TokenTree::Punct(tt.0),
- TokenTree::Ident(tt) => bridge::TokenTree::Ident(tt.0),
- TokenTree::Literal(tt) => bridge::TokenTree::Literal(tt.0),
- }
-}
-
-/// Creates a token stream containing a single token tree.
-impl From<TokenTree> for TokenStream {
- fn from(tree: TokenTree) -> TokenStream {
- TokenStream(Some(bridge::client::TokenStream::from_token_tree(tree_to_bridge_tree(tree))))
- }
-}
-
-/// Non-generic helper for implementing `FromIterator<TokenStream>` and
-/// `Extend<TokenStream>` with less monomorphization in calling crates.
-struct ConcatStreamsHelper {
- streams: Vec<bridge::client::TokenStream>,
-}
-
-impl ConcatStreamsHelper {
- fn new(capacity: usize) -> Self {
- ConcatStreamsHelper { streams: Vec::with_capacity(capacity) }
- }
-
- fn push(&mut self, stream: TokenStream) {
- if let Some(stream) = stream.0 {
- self.streams.push(stream);
- }
- }
-
- fn build(mut self) -> TokenStream {
- if self.streams.len() <= 1 {
- TokenStream(self.streams.pop())
- } else {
- TokenStream(Some(bridge::client::TokenStream::concat_streams(None, self.streams)))
- }
- }
-
- fn append_to(mut self, stream: &mut TokenStream) {
- if self.streams.is_empty() {
- return;
- }
- let base = stream.0.take();
- if base.is_none() && self.streams.len() == 1 {
- stream.0 = self.streams.pop();
- } else {
- stream.0 = Some(bridge::client::TokenStream::concat_streams(base, self.streams));
- }
- }
-}
-
-/// Collects a number of token trees into a single stream.
-impl FromIterator<TokenTree> for TokenStream {
- fn from_iter<I: IntoIterator<Item = TokenTree>>(trees: I) -> Self {
- trees.into_iter().map(TokenStream::from).collect()
- }
-}
-
-/// A "flattening" operation on token streams, collects token trees
-/// from multiple token streams into a single stream.
-impl FromIterator<TokenStream> for TokenStream {
- fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self {
- let iter = streams.into_iter();
- let mut builder = ConcatStreamsHelper::new(iter.size_hint().0);
- iter.for_each(|stream| builder.push(stream));
- builder.build()
- }
-}
-
-impl Extend<TokenTree> for TokenStream {
- fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, trees: I) {
- self.extend(trees.into_iter().map(TokenStream::from));
- }
-}
-
-impl Extend<TokenStream> for TokenStream {
- fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) {
- // FIXME(eddyb) Use an optimized implementation if/when possible.
- *self = iter::once(mem::replace(self, Self::new())).chain(streams).collect();
- }
-}
-
-/// Public implementation details for the `TokenStream` type, such as iterators.
-pub mod token_stream {
- use super::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree};
-
- /// An iterator over `TokenStream`'s `TokenTree`s.
- /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups,
- /// and returns whole groups as token trees.
- #[derive(Clone)]
- pub struct IntoIter(
- std::vec::IntoIter<
- bridge::TokenTree<
- bridge::client::Group,
- bridge::client::Punct,
- bridge::client::Ident,
- bridge::client::Literal,
- >,
- >,
- );
-
- impl Iterator for IntoIter {
- type Item = TokenTree;
-
- fn next(&mut self) -> Option<TokenTree> {
- self.0.next().map(|tree| match tree {
- bridge::TokenTree::Group(tt) => TokenTree::Group(Group(tt)),
- bridge::TokenTree::Punct(tt) => TokenTree::Punct(Punct(tt)),
- bridge::TokenTree::Ident(tt) => TokenTree::Ident(Ident(tt)),
- bridge::TokenTree::Literal(tt) => TokenTree::Literal(Literal(tt)),
- })
- }
- }
-
- impl IntoIterator for TokenStream {
- type Item = TokenTree;
- type IntoIter = IntoIter;
-
- fn into_iter(self) -> IntoIter {
- IntoIter(self.0.map(|v| v.into_trees()).unwrap_or_default().into_iter())
- }
- }
-}
-
-#[doc(hidden)]
-mod quote;
-
-/// A region of source code, along with macro expansion information.
-#[derive(Copy, Clone)]
-pub struct Span(bridge::client::Span);
-
-macro_rules! diagnostic_method {
- ($name:ident, $level:expr) => {
- /// Creates a new `Diagnostic` with the given `message` at the span
- /// `self`.
- pub fn $name<T: Into<String>>(self, message: T) -> Diagnostic {
- Diagnostic::spanned(self, $level, message)
- }
- };
-}
-
-impl Span {
- /// A span that resolves at the macro definition site.
- pub fn def_site() -> Span {
- Span(bridge::client::Span::def_site())
- }
-
- /// The span of the invocation of the current procedural macro.
- /// Identifiers created with this span will be resolved as if they were written
- /// directly at the macro call location (call-site hygiene) and other code
- /// at the macro call site will be able to refer to them as well.
- pub fn call_site() -> Span {
- Span(bridge::client::Span::call_site())
- }
-
- /// A span that represents `macro_rules` hygiene, and sometimes resolves at the macro
- /// definition site (local variables, labels, `$crate`) and sometimes at the macro
- /// call site (everything else).
- /// The span location is taken from the call-site.
- pub fn mixed_site() -> Span {
- Span(bridge::client::Span::mixed_site())
- }
-
- /// The original source file into which this span points.
- pub fn source_file(&self) -> SourceFile {
- SourceFile(self.0.source_file())
- }
-
- /// The `Span` for the tokens in the previous macro expansion from which
- /// `self` was generated from, if any.
- pub fn parent(&self) -> Option<Span> {
- self.0.parent().map(Span)
- }
-
- /// The span for the origin source code that `self` was generated from. If
- /// this `Span` wasn't generated from other macro expansions then the return
- /// value is the same as `*self`.
- pub fn source(&self) -> Span {
- Span(self.0.source())
- }
-
- /// Gets the starting line/column in the source file for this span.
- pub fn start(&self) -> LineColumn {
- self.0.start().add_1_to_column()
- }
-
- /// Gets the ending line/column in the source file for this span.
- pub fn end(&self) -> LineColumn {
- self.0.end().add_1_to_column()
- }
-
- /// Creates an empty span pointing to directly before this span.
- pub fn before(&self) -> Span {
- Span(self.0.before())
- }
-
- /// Creates an empty span pointing to directly after this span.
- pub fn after(&self) -> Span {
- Span(self.0.after())
- }
-
- /// Creates a new span encompassing `self` and `other`.
- ///
- /// Returns `None` if `self` and `other` are from different files.
- pub fn join(&self, other: Span) -> Option<Span> {
- self.0.join(other.0).map(Span)
- }
-
- /// Creates a new span with the same line/column information as `self` but
- /// that resolves symbols as though it were at `other`.
- pub fn resolved_at(&self, other: Span) -> Span {
- Span(self.0.resolved_at(other.0))
- }
-
- /// Creates a new span with the same name resolution behavior as `self` but
- /// with the line/column information of `other`.
- pub fn located_at(&self, other: Span) -> Span {
- other.resolved_at(*self)
- }
-
- /// Compares to spans to see if they're equal.
- pub fn eq(&self, other: &Span) -> bool {
- self.0 == other.0
- }
-
- /// Returns the source text behind a span. This preserves the original source
- /// code, including spaces and comments. It only returns a result if the span
- /// corresponds to real source code.
- ///
- /// Note: The observable result of a macro should only rely on the tokens and
- /// not on this source text. The result of this function is a best effort to
- /// be used for diagnostics only.
- pub fn source_text(&self) -> Option<String> {
- self.0.source_text()
- }
-
- // Used by the implementation of `Span::quote`
- #[doc(hidden)]
- pub fn save_span(&self) -> usize {
- self.0.save_span()
- }
-
- // Used by the implementation of `Span::quote`
- #[doc(hidden)]
- pub fn recover_proc_macro_span(id: usize) -> Span {
- Span(bridge::client::Span::recover_proc_macro_span(id))
- }
-
- diagnostic_method!(error, Level::Error);
- diagnostic_method!(warning, Level::Warning);
- diagnostic_method!(note, Level::Note);
- diagnostic_method!(help, Level::Help);
-}
-
-/// Prints a span in a form convenient for debugging.
-impl fmt::Debug for Span {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-
-/// A line-column pair representing the start or end of a `Span`.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub struct LineColumn {
- /// The 1-indexed line in the source file on which the span starts or ends (inclusive).
- pub line: usize,
- /// The 1-indexed column (number of bytes in UTF-8 encoding) in the source
- /// file on which the span starts or ends (inclusive).
- pub column: usize,
-}
-
-impl LineColumn {
- fn add_1_to_column(self) -> Self {
- LineColumn { line: self.line, column: self.column + 1 }
- }
-}
-
-impl Ord for LineColumn {
- fn cmp(&self, other: &Self) -> Ordering {
- self.line.cmp(&other.line).then(self.column.cmp(&other.column))
- }
-}
-
-impl PartialOrd for LineColumn {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-/// The source file of a given `Span`.
-#[derive(Clone)]
-pub struct SourceFile(bridge::client::SourceFile);
-
-impl SourceFile {
- /// Gets the path to this source file.
- ///
- /// ### Note
- /// If the code span associated with this `SourceFile` was generated by an external macro, this
- /// macro, this might not be an actual path on the filesystem. Use [`is_real`] to check.
- ///
- /// Also note that even if `is_real` returns `true`, if `--remap-path-prefix` was passed on
- /// the command line, the path as given might not actually be valid.
- ///
- /// [`is_real`]: Self::is_real
- pub fn path(&self) -> PathBuf {
- PathBuf::from(self.0.path())
- }
-
- /// Returns `true` if this source file is a real source file, and not generated by an external
- /// macro's expansion.
- pub fn is_real(&self) -> bool {
- // This is a hack until intercrate spans are implemented and we can have real source files
- // for spans generated in external macros.
- // https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368
- self.0.is_real()
- }
-}
-
-impl fmt::Debug for SourceFile {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("SourceFile")
- .field("path", &self.path())
- .field("is_real", &self.is_real())
- .finish()
- }
-}
-
-impl PartialEq for SourceFile {
- fn eq(&self, other: &Self) -> bool {
- self.0.eq(&other.0)
- }
-}
-
-impl Eq for SourceFile {}
-
-/// A single token or a delimited sequence of token trees (e.g., `[1, (), ..]`).
-#[derive(Clone)]
-pub enum TokenTree {
- /// A token stream surrounded by bracket delimiters.
- Group(Group),
- /// An identifier.
- Ident(Ident),
- /// A single punctuation character (`+`, `,`, `$`, etc.).
- Punct(Punct),
- /// A literal character (`'a'`), string (`"hello"`), number (`2.3`), etc.
- Literal(Literal),
-}
-
-impl TokenTree {
- /// Returns the span of this tree, delegating to the `span` method of
- /// the contained token or a delimited stream.
- pub fn span(&self) -> Span {
- match *self {
- TokenTree::Group(ref t) => t.span(),
- TokenTree::Ident(ref t) => t.span(),
- TokenTree::Punct(ref t) => t.span(),
- TokenTree::Literal(ref t) => t.span(),
- }
- }
-
- /// Configures the span for *only this token*.
- ///
- /// Note that if this token is a `Group` then this method will not configure
- /// the span of each of the internal tokens, this will simply delegate to
- /// the `set_span` method of each variant.
- pub fn set_span(&mut self, span: Span) {
- match *self {
- TokenTree::Group(ref mut t) => t.set_span(span),
- TokenTree::Ident(ref mut t) => t.set_span(span),
- TokenTree::Punct(ref mut t) => t.set_span(span),
- TokenTree::Literal(ref mut t) => t.set_span(span),
- }
- }
-}
-
-/// Prints token tree in a form convenient for debugging.
-impl fmt::Debug for TokenTree {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- // Each of these has the name in the struct type in the derived debug,
- // so don't bother with an extra layer of indirection
- match *self {
- TokenTree::Group(ref tt) => tt.fmt(f),
- TokenTree::Ident(ref tt) => tt.fmt(f),
- TokenTree::Punct(ref tt) => tt.fmt(f),
- TokenTree::Literal(ref tt) => tt.fmt(f),
- }
- }
-}
-
-impl From<Group> for TokenTree {
- fn from(g: Group) -> TokenTree {
- TokenTree::Group(g)
- }
-}
-
-impl From<Ident> for TokenTree {
- fn from(g: Ident) -> TokenTree {
- TokenTree::Ident(g)
- }
-}
-
-impl From<Punct> for TokenTree {
- fn from(g: Punct) -> TokenTree {
- TokenTree::Punct(g)
- }
-}
-
-impl From<Literal> for TokenTree {
- fn from(g: Literal) -> TokenTree {
- TokenTree::Literal(g)
- }
-}
-
-/// Prints the token tree as a string that is supposed to be losslessly convertible back
-/// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s
-/// with `Delimiter::None` delimiters and negative numeric literals.
-impl fmt::Display for TokenTree {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
-/// A delimited token stream.
-///
-/// A `Group` internally contains a `TokenStream` which is surrounded by `Delimiter`s.
-#[derive(Clone)]
-pub struct Group(bridge::client::Group);
-
-/// Describes how a sequence of token trees is delimited.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Delimiter {
- /// `( ... )`
- Parenthesis,
- /// `{ ... }`
- Brace,
- /// `[ ... ]`
- Bracket,
- /// `Ø ... Ø`
- /// An invisible delimiter, that may, for example, appear around tokens coming from a
- /// "macro variable" `$var`. It is important to preserve operator priorities in cases like
- /// `$var * 3` where `$var` is `1 + 2`.
- /// Invisible delimiters might not survive roundtrip of a token stream through a string.
- None,
-}
-
-impl Group {
- /// Creates a new `Group` with the given delimiter and token stream.
- ///
- /// This constructor will set the span for this group to
- /// `Span::call_site()`. To change the span you can use the `set_span`
- /// method below.
- pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group {
- Group(bridge::client::Group::new(delimiter, stream.0))
- }
-
- /// Returns the delimiter of this `Group`
- pub fn delimiter(&self) -> Delimiter {
- self.0.delimiter()
- }
-
- /// Returns the `TokenStream` of tokens that are delimited in this `Group`.
- ///
- /// Note that the returned token stream does not include the delimiter
- /// returned above.
- pub fn stream(&self) -> TokenStream {
- TokenStream(Some(self.0.stream()))
- }
-
- /// Returns the span for the delimiters of this token stream, spanning the
- /// entire `Group`.
- ///
- /// ```text
- /// pub fn span(&self) -> Span {
- /// ^^^^^^^
- /// ```
- pub fn span(&self) -> Span {
- Span(self.0.span())
- }
-
- /// Returns the span pointing to the opening delimiter of this group.
- ///
- /// ```text
- /// pub fn span_open(&self) -> Span {
- /// ^
- /// ```
- pub fn span_open(&self) -> Span {
- Span(self.0.span_open())
- }
-
- /// Returns the span pointing to the closing delimiter of this group.
- ///
- /// ```text
- /// pub fn span_close(&self) -> Span {
- /// ^
- /// ```
- pub fn span_close(&self) -> Span {
- Span(self.0.span_close())
- }
-
- /// Configures the span for this `Group`'s delimiters, but not its internal
- /// tokens.
- ///
- /// This method will **not** set the span of all the internal tokens spanned
- /// by this group, but rather it will only set the span of the delimiter
- /// tokens at the level of the `Group`.
- pub fn set_span(&mut self, span: Span) {
- self.0.set_span(span.0);
- }
-}
-
-/// Prints the group as a string that should be losslessly convertible back
-/// into the same group (modulo spans), except for possibly `TokenTree::Group`s
-/// with `Delimiter::None` delimiters.
-impl fmt::Display for Group {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
-impl fmt::Debug for Group {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Group")
- .field("delimiter", &self.delimiter())
- .field("stream", &self.stream())
- .field("span", &self.span())
- .finish()
- }
-}
-
-/// A `Punct` is a single punctuation character such as `+`, `-` or `#`.
-///
-/// Multi-character operators like `+=` are represented as two instances of `Punct` with different
-/// forms of `Spacing` returned.
-#[derive(Clone)]
-pub struct Punct(bridge::client::Punct);
-
-/// Describes whether a `Punct` is followed immediately by another `Punct` ([`Spacing::Joint`]) or
-/// by a different token or whitespace ([`Spacing::Alone`]).
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum Spacing {
- /// A `Punct` is not immediately followed by another `Punct`.
- /// E.g. `+` is `Alone` in `+ =`, `+ident` and `+()`.
- Alone,
- /// A `Punct` is immediately followed by another `Punct`.
- /// E.g. `+` is `Joint` in `+=` and `++`.
- ///
- /// Additionally, single quote `'` can join with identifiers to form lifetimes: `'ident`.
- Joint,
-}
-
-impl Punct {
- /// Creates a new `Punct` from the given character and spacing.
- /// The `ch` argument must be a valid punctuation character permitted by the language,
- /// otherwise the function will panic.
- ///
- /// The returned `Punct` will have the default span of `Span::call_site()`
- /// which can be further configured with the `set_span` method below.
- pub fn new(ch: char, spacing: Spacing) -> Punct {
- Punct(bridge::client::Punct::new(ch, spacing))
- }
-
- /// Returns the value of this punctuation character as `char`.
- pub fn as_char(&self) -> char {
- self.0.as_char()
- }
-
- /// Returns the spacing of this punctuation character, indicating whether it's immediately
- /// followed by another `Punct` in the token stream, so they can potentially be combined into
- /// a multi-character operator (`Joint`), or it's followed by some other token or whitespace
- /// (`Alone`) so the operator has certainly ended.
- pub fn spacing(&self) -> Spacing {
- self.0.spacing()
- }
-
- /// Returns the span for this punctuation character.
- pub fn span(&self) -> Span {
- Span(self.0.span())
- }
-
- /// Configure the span for this punctuation character.
- pub fn set_span(&mut self, span: Span) {
- self.0 = self.0.with_span(span.0);
- }
-}
-
-/// Prints the punctuation character as a string that should be losslessly convertible
-/// back into the same character.
-impl fmt::Display for Punct {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
-impl fmt::Debug for Punct {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Punct")
- .field("ch", &self.as_char())
- .field("spacing", &self.spacing())
- .field("span", &self.span())
- .finish()
- }
-}
-
-impl PartialEq<char> for Punct {
- fn eq(&self, rhs: &char) -> bool {
- self.as_char() == *rhs
- }
-}
-
-impl PartialEq<Punct> for char {
- fn eq(&self, rhs: &Punct) -> bool {
- *self == rhs.as_char()
- }
-}
-
-/// An identifier (`ident`).
-#[derive(Clone)]
-pub struct Ident(bridge::client::Ident);
-
-impl Ident {
- /// Creates a new `Ident` with the given `string` as well as the specified
- /// `span`.
- /// The `string` argument must be a valid identifier permitted by the
- /// language (including keywords, e.g. `self` or `fn`). Otherwise, the function will panic.
- ///
- /// Note that `span`, currently in rustc, configures the hygiene information
- /// for this identifier.
- ///
- /// As of this time `Span::call_site()` explicitly opts-in to "call-site" hygiene
- /// meaning that identifiers created with this span will be resolved as if they were written
- /// directly at the location of the macro call, and other code at the macro call site will be
- /// able to refer to them as well.
- ///
- /// Later spans like `Span::def_site()` will allow to opt-in to "definition-site" hygiene
- /// meaning that identifiers created with this span will be resolved at the location of the
- /// macro definition and other code at the macro call site will not be able to refer to them.
- ///
- /// Due to the current importance of hygiene this constructor, unlike other
- /// tokens, requires a `Span` to be specified at construction.
- pub fn new(string: &str, span: Span) -> Ident {
- Ident(bridge::client::Ident::new(string, span.0, false))
- }
-
- /// Same as `Ident::new`, but creates a raw identifier (`r#ident`).
- /// The `string` argument be a valid identifier permitted by the language
- /// (including keywords, e.g. `fn`). Keywords which are usable in path segments
- /// (e.g. `self`, `super`) are not supported, and will cause a panic.
- pub fn new_raw(string: &str, span: Span) -> Ident {
- Ident(bridge::client::Ident::new(string, span.0, true))
- }
-
- /// Returns the span of this `Ident`, encompassing the entire string returned
- /// by [`to_string`](Self::to_string).
- pub fn span(&self) -> Span {
- Span(self.0.span())
- }
-
- /// Configures the span of this `Ident`, possibly changing its hygiene context.
- pub fn set_span(&mut self, span: Span) {
- self.0 = self.0.with_span(span.0);
- }
-}
-
-/// Prints the identifier as a string that should be losslessly convertible
-/// back into the same identifier.
-impl fmt::Display for Ident {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
-impl fmt::Debug for Ident {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Ident")
- .field("ident", &self.to_string())
- .field("span", &self.span())
- .finish()
- }
-}
-
-/// A literal string (`"hello"`), byte string (`b"hello"`),
-/// character (`'a'`), byte character (`b'a'`), an integer or floating point number
-/// with or without a suffix (`1`, `1u8`, `2.3`, `2.3f32`).
-/// Boolean literals like `true` and `false` do not belong here, they are `Ident`s.
-#[derive(Clone)]
-pub struct Literal(bridge::client::Literal);
-
-macro_rules! suffixed_int_literals {
- ($($name:ident => $kind:ident,)*) => ($(
- /// Creates a new suffixed integer literal with the specified value.
- ///
- /// This function will create an integer like `1u32` where the integer
- /// value specified is the first part of the token and the integral is
- /// also suffixed at the end.
- /// Literals created from negative numbers might not survive round-trips through
- /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
- ///
- /// Literals created through this method have the `Span::call_site()`
- /// span by default, which can be configured with the `set_span` method
- /// below.
- pub fn $name(n: $kind) -> Literal {
- Literal(bridge::client::Literal::typed_integer(&n.to_string(), stringify!($kind)))
- }
- )*)
-}
-
-macro_rules! unsuffixed_int_literals {
- ($($name:ident => $kind:ident,)*) => ($(
- /// Creates a new unsuffixed integer literal with the specified value.
- ///
- /// This function will create an integer like `1` where the integer
- /// value specified is the first part of the token. No suffix is
- /// specified on this token, meaning that invocations like
- /// `Literal::i8_unsuffixed(1)` are equivalent to
- /// `Literal::u32_unsuffixed(1)`.
- /// Literals created from negative numbers might not survive rountrips through
- /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
- ///
- /// Literals created through this method have the `Span::call_site()`
- /// span by default, which can be configured with the `set_span` method
- /// below.
- pub fn $name(n: $kind) -> Literal {
- Literal(bridge::client::Literal::integer(&n.to_string()))
- }
- )*)
-}
-
-impl Literal {
- suffixed_int_literals! {
- u8_suffixed => u8,
- u16_suffixed => u16,
- u32_suffixed => u32,
- u64_suffixed => u64,
- u128_suffixed => u128,
- usize_suffixed => usize,
- i8_suffixed => i8,
- i16_suffixed => i16,
- i32_suffixed => i32,
- i64_suffixed => i64,
- i128_suffixed => i128,
- isize_suffixed => isize,
- }
-
- unsuffixed_int_literals! {
- u8_unsuffixed => u8,
- u16_unsuffixed => u16,
- u32_unsuffixed => u32,
- u64_unsuffixed => u64,
- u128_unsuffixed => u128,
- usize_unsuffixed => usize,
- i8_unsuffixed => i8,
- i16_unsuffixed => i16,
- i32_unsuffixed => i32,
- i64_unsuffixed => i64,
- i128_unsuffixed => i128,
- isize_unsuffixed => isize,
- }
-
- /// Creates a new unsuffixed floating-point literal.
- ///
- /// This constructor is similar to those like `Literal::i8_unsuffixed` where
- /// the float's value is emitted directly into the token but no suffix is
- /// used, so it may be inferred to be a `f64` later in the compiler.
- /// Literals created from negative numbers might not survive rountrips through
- /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
- ///
- /// # Panics
- ///
- /// This function requires that the specified float is finite, for
- /// example if it is infinity or NaN this function will panic.
- pub fn f32_unsuffixed(n: f32) -> Literal {
- if !n.is_finite() {
- panic!("Invalid float literal {n}");
- }
- let mut repr = n.to_string();
- if !repr.contains('.') {
- repr.push_str(".0");
- }
- Literal(bridge::client::Literal::float(&repr))
- }
-
- /// Creates a new suffixed floating-point literal.
- ///
- /// This constructor will create a literal like `1.0f32` where the value
- /// specified is the preceding part of the token and `f32` is the suffix of
- /// the token. This token will always be inferred to be an `f32` in the
- /// compiler.
- /// Literals created from negative numbers might not survive rountrips through
- /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
- ///
- /// # Panics
- ///
- /// This function requires that the specified float is finite, for
- /// example if it is infinity or NaN this function will panic.
- pub fn f32_suffixed(n: f32) -> Literal {
- if !n.is_finite() {
- panic!("Invalid float literal {n}");
- }
- Literal(bridge::client::Literal::f32(&n.to_string()))
- }
-
- /// Creates a new unsuffixed floating-point literal.
- ///
- /// This constructor is similar to those like `Literal::i8_unsuffixed` where
- /// the float's value is emitted directly into the token but no suffix is
- /// used, so it may be inferred to be a `f64` later in the compiler.
- /// Literals created from negative numbers might not survive rountrips through
- /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
- ///
- /// # Panics
- ///
- /// This function requires that the specified float is finite, for
- /// example if it is infinity or NaN this function will panic.
- pub fn f64_unsuffixed(n: f64) -> Literal {
- if !n.is_finite() {
- panic!("Invalid float literal {n}");
- }
- let mut repr = n.to_string();
- if !repr.contains('.') {
- repr.push_str(".0");
- }
- Literal(bridge::client::Literal::float(&repr))
- }
-
- /// Creates a new suffixed floating-point literal.
- ///
- /// This constructor will create a literal like `1.0f64` where the value
- /// specified is the preceding part of the token and `f64` is the suffix of
- /// the token. This token will always be inferred to be an `f64` in the
- /// compiler.
- /// Literals created from negative numbers might not survive rountrips through
- /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal).
- ///
- /// # Panics
- ///
- /// This function requires that the specified float is finite, for
- /// example if it is infinity or NaN this function will panic.
- pub fn f64_suffixed(n: f64) -> Literal {
- if !n.is_finite() {
- panic!("Invalid float literal {n}");
- }
- Literal(bridge::client::Literal::f64(&n.to_string()))
- }
-
- /// String literal.
- pub fn string(string: &str) -> Literal {
- Literal(bridge::client::Literal::string(string))
- }
-
- /// Character literal.
- pub fn character(ch: char) -> Literal {
- Literal(bridge::client::Literal::character(ch))
- }
-
- /// Byte string literal.
- pub fn byte_string(bytes: &[u8]) -> Literal {
- Literal(bridge::client::Literal::byte_string(bytes))
- }
-
- /// Returns the span encompassing this literal.
- pub fn span(&self) -> Span {
- Span(self.0.span())
- }
-
- /// Configures the span associated for this literal.
- pub fn set_span(&mut self, span: Span) {
- self.0.set_span(span.0);
- }
-
- /// Returns a `Span` that is a subset of `self.span()` containing only the
- /// source bytes in range `range`. Returns `None` if the would-be trimmed
- /// span is outside the bounds of `self`.
- // FIXME(SergioBenitez): check that the byte range starts and ends at a
- // UTF-8 boundary of the source. otherwise, it's likely that a panic will
- // occur elsewhere when the source text is printed.
- // FIXME(SergioBenitez): there is no way for the user to know what
- // `self.span()` actually maps to, so this method can currently only be
- // called blindly. For example, `to_string()` for the character 'c' returns
- // "'\u{63}'"; there is no way for the user to know whether the source text
- // was 'c' or whether it was '\u{63}'.
- pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> {
- self.0.subspan(range.start_bound().cloned(), range.end_bound().cloned()).map(Span)
- }
-}
-
-/// Parse a single literal from its stringified representation.
-///
-/// In order to parse successfully, the input string must not contain anything
-/// but the literal token. Specifically, it must not contain whitespace or
-/// comments in addition to the literal.
-///
-/// The resulting literal token will have a `Span::call_site()` span.
-///
-/// NOTE: some errors may cause panics instead of returning `LexError`. We
-/// reserve the right to change these errors into `LexError`s later.
-impl FromStr for Literal {
- type Err = LexError;
-
- fn from_str(src: &str) -> Result<Self, LexError> {
- match bridge::client::Literal::from_str(src) {
- Ok(literal) => Ok(Literal(literal)),
- Err(()) => Err(LexError),
- }
- }
-}
-
-/// Prints the literal as a string that should be losslessly convertible
-/// back into the same literal (except for possible rounding for floating point literals).
-impl fmt::Display for Literal {
- fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!()
- }
-}
-
-impl fmt::Debug for Literal {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-
-/// Tracked access to environment variables.
-pub mod tracked_env {
- use std::env::{self, VarError};
- use std::ffi::OsStr;
-
- /// Retrieve an environment variable and add it to build dependency info.
- /// Build system executing the compiler will know that the variable was accessed during
- /// compilation, and will be able to rerun the build when the value of that variable changes.
- /// Besides the dependency tracking this function should be equivalent to `env::var` from the
- /// standard library, except that the argument must be UTF-8.
- pub fn var<K: AsRef<OsStr> + AsRef<str>>(key: K) -> Result<String, VarError> {
- let key: &str = key.as_ref();
- let value = env::var(key);
- super::bridge::client::FreeFunctions::track_env_var(key, value.as_deref().ok());
- value
- }
-}
-
-/// Tracked access to additional files.
-pub mod tracked_path {
-
- /// Track a file explicitly.
- ///
- /// Commonly used for tracking asset preprocessing.
- pub fn path<P: AsRef<str>>(path: P) {
- let path: &str = path.as_ref();
- super::bridge::client::FreeFunctions::track_path(path);
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs
deleted file mode 100644
index 39309faa4..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs
+++ /dev/null
@@ -1,139 +0,0 @@
-//! # Quasiquoter
-//! This file contains the implementation internals of the quasiquoter provided by `quote!`.
-
-//! This quasiquoter uses macros 2.0 hygiene to reliably access
-//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
-
-use super::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
-
-macro_rules! quote_tt {
- (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) };
- ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) };
- ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) };
- (,) => { Punct::new(',', Spacing::Alone) };
- (.) => { Punct::new('.', Spacing::Alone) };
- (;) => { Punct::new(';', Spacing::Alone) };
- (!) => { Punct::new('!', Spacing::Alone) };
- (<) => { Punct::new('<', Spacing::Alone) };
- (>) => { Punct::new('>', Spacing::Alone) };
- (&) => { Punct::new('&', Spacing::Alone) };
- (=) => { Punct::new('=', Spacing::Alone) };
- ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) };
-}
-
-macro_rules! quote_ts {
- ((@ $($t:tt)*)) => { $($t)* };
- (::) => {
- [
- TokenTree::from(Punct::new(':', Spacing::Joint)),
- TokenTree::from(Punct::new(':', Spacing::Alone)),
- ].iter()
- .cloned()
- .map(|mut x| {
- x.set_span(Span::def_site());
- x
- })
- .collect::<TokenStream>()
- };
- ($t:tt) => { TokenTree::from(quote_tt!($t)) };
-}
-
-/// Simpler version of the real `quote!` macro, implemented solely
-/// through `macro_rules`, for bootstrapping the real implementation
-/// (see the `quote` function), which does not have access to the
-/// real `quote!` macro due to the `proc_macro` crate not being
-/// able to depend on itself.
-///
-/// Note: supported tokens are a subset of the real `quote!`, but
-/// unquoting is different: instead of `$x`, this uses `(@ expr)`.
-macro_rules! quote {
- () => { TokenStream::new() };
- ($($t:tt)*) => {
- [
- $(TokenStream::from(quote_ts!($t)),)*
- ].iter().cloned().collect::<TokenStream>()
- };
-}
-
-/// Quote a `TokenStream` into a `TokenStream`.
-/// This is the actual implementation of the `quote!()` proc macro.
-///
-/// It is loaded by the compiler in `register_builtin_macros`.
-pub fn quote(stream: TokenStream) -> TokenStream {
- if stream.is_empty() {
- return quote!(super::TokenStream::new());
- }
- let proc_macro_crate = quote!(crate);
- let mut after_dollar = false;
- let tokens = stream
- .into_iter()
- .filter_map(|tree| {
- if after_dollar {
- after_dollar = false;
- match tree {
- TokenTree::Ident(_) => {
- return Some(quote!(Into::<super::TokenStream>::into(
- Clone::clone(&(@ tree))),));
- }
- TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
- _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
- }
- } else if let TokenTree::Punct(ref tt) = tree {
- if tt.as_char() == '$' {
- after_dollar = true;
- return None;
- }
- }
-
- Some(quote!(super::TokenStream::from((@ match tree {
- TokenTree::Punct(tt) => quote!(super::TokenTree::Punct(super::Punct::new(
- (@ TokenTree::from(Literal::character(tt.as_char()))),
- (@ match tt.spacing() {
- Spacing::Alone => quote!(super::Spacing::Alone),
- Spacing::Joint => quote!(super::Spacing::Joint),
- }),
- ))),
- TokenTree::Group(tt) => quote!(super::TokenTree::Group(super::Group::new(
- (@ match tt.delimiter() {
- Delimiter::Parenthesis => quote!(super::Delimiter::Parenthesis),
- Delimiter::Brace => quote!(super::Delimiter::Brace),
- Delimiter::Bracket => quote!(super::Delimiter::Bracket),
- Delimiter::None => quote!(super::Delimiter::None),
- }),
- (@ quote(tt.stream())),
- ))),
- TokenTree::Ident(tt) => quote!(super::TokenTree::Ident(super::Ident::new(
- (@ TokenTree::from(Literal::string(&tt.to_string()))),
- (@ quote_span(proc_macro_crate.clone(), tt.span())),
- ))),
- TokenTree::Literal(tt) => quote!(super::TokenTree::Literal({
- let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string())))
- .parse::<super::TokenStream>()
- .unwrap()
- .into_iter();
- if let (Some(super::TokenTree::Literal(mut lit)), None) =
- (iter.next(), iter.next())
- {
- lit.set_span((@ quote_span(proc_macro_crate.clone(), tt.span())));
- lit
- } else {
- unreachable!()
- }
- }))
- })),))
- })
- .collect::<TokenStream>();
-
- if after_dollar {
- panic!("unexpected trailing `$` in `quote!`");
- }
-
- quote!([(@ tokens)].iter().cloned().collect::<super::TokenStream>())
-}
-
-/// Quote a `Span` into a `TokenStream`.
-/// This is needed to implement a custom quoter.
-pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream {
- let id = span.save_span();
- quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id)))))
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs
deleted file mode 100644
index 30baf3a13..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs
+++ /dev/null
@@ -1,840 +0,0 @@
-//! Rustc proc-macro server implementation with tt
-//!
-//! Based on idea from <https://github.com/fedochet/rust-proc-macro-expander>
-//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that
-//! we could provide any TokenStream implementation.
-//! The original idea from fedochet is using proc-macro2 as backend,
-//! we use tt instead for better integration with RA.
-//!
-//! FIXME: No span and source file information is implemented yet
-
-use super::proc_macro::bridge::{self, server};
-
-use std::collections::HashMap;
-use std::hash::Hash;
-use std::ops::Bound;
-use std::{ascii, vec::IntoIter};
-
-use crate::tt;
-
-type Group = tt::Subtree;
-type TokenTree = tt::TokenTree;
-type Punct = tt::Punct;
-type Spacing = tt::Spacing;
-type Literal = tt::Literal;
-type Span = tt::TokenId;
-
-#[derive(Debug, Default, Clone)]
-pub struct TokenStream {
- pub token_trees: Vec<TokenTree>,
-}
-
-impl TokenStream {
- pub fn new() -> Self {
- TokenStream::default()
- }
-
- pub fn with_subtree(subtree: tt::Subtree) -> Self {
- if subtree.delimiter.kind != tt::DelimiterKind::Invisible {
- TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] }
- } else {
- TokenStream { token_trees: subtree.token_trees }
- }
- }
-
- pub fn into_subtree(self) -> tt::Subtree {
- tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: self.token_trees }
- }
-
- pub fn is_empty(&self) -> bool {
- self.token_trees.is_empty()
- }
-}
-
-/// Creates a token stream containing a single token tree.
-impl From<TokenTree> for TokenStream {
- fn from(tree: TokenTree) -> TokenStream {
- TokenStream { token_trees: vec![tree] }
- }
-}
-
-/// Collects a number of token trees into a single stream.
-impl FromIterator<TokenTree> for TokenStream {
- fn from_iter<I: IntoIterator<Item = TokenTree>>(trees: I) -> Self {
- trees.into_iter().map(TokenStream::from).collect()
- }
-}
-
-/// A "flattening" operation on token streams, collects token trees
-/// from multiple token streams into a single stream.
-impl FromIterator<TokenStream> for TokenStream {
- fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self {
- let mut builder = TokenStreamBuilder::new();
- streams.into_iter().for_each(|stream| builder.push(stream));
- builder.build()
- }
-}
-
-impl Extend<TokenTree> for TokenStream {
- fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, trees: I) {
- self.extend(trees.into_iter().map(TokenStream::from));
- }
-}
-
-impl Extend<TokenStream> for TokenStream {
- fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) {
- for item in streams {
- for tkn in item {
- match tkn {
- tt::TokenTree::Subtree(subtree)
- if subtree.delimiter.kind == tt::DelimiterKind::Invisible =>
- {
- self.token_trees.extend(subtree.token_trees);
- }
- _ => {
- self.token_trees.push(tkn);
- }
- }
- }
- }
- }
-}
-
-#[derive(Clone)]
-pub struct SourceFile {
- // FIXME stub
-}
-
-type Level = super::proc_macro::Level;
-type LineColumn = super::proc_macro::LineColumn;
-
-/// A structure representing a diagnostic message and associated children
-/// messages.
-#[derive(Clone, Debug)]
-pub struct Diagnostic {
- level: Level,
- message: String,
- spans: Vec<Span>,
- children: Vec<Diagnostic>,
-}
-
-impl Diagnostic {
- /// Creates a new diagnostic with the given `level` and `message`.
- pub fn new<T: Into<String>>(level: Level, message: T) -> Diagnostic {
- Diagnostic { level, message: message.into(), spans: vec![], children: vec![] }
- }
-}
-
-// Rustc Server Ident has to be `Copyable`
-// We use a stub here for bypassing
-#[derive(Hash, Eq, PartialEq, Copy, Clone)]
-pub struct IdentId(u32);
-
-#[derive(Clone, Hash, Eq, PartialEq)]
-struct IdentData(tt::Ident);
-
-#[derive(Default)]
-struct IdentInterner {
- idents: HashMap<IdentData, u32>,
- ident_data: Vec<IdentData>,
-}
-
-impl IdentInterner {
- fn intern(&mut self, data: &IdentData) -> u32 {
- if let Some(index) = self.idents.get(data) {
- return *index;
- }
-
- let index = self.idents.len() as u32;
- self.ident_data.push(data.clone());
- self.idents.insert(data.clone(), index);
- index
- }
-
- fn get(&self, index: u32) -> &IdentData {
- &self.ident_data[index as usize]
- }
-
- #[allow(unused)]
- fn get_mut(&mut self, index: u32) -> &mut IdentData {
- self.ident_data.get_mut(index as usize).expect("Should be consistent")
- }
-}
-
-pub struct TokenStreamBuilder {
- acc: TokenStream,
-}
-
-/// Public implementation details for the `TokenStream` type, such as iterators.
-pub mod token_stream {
- use std::str::FromStr;
-
- use super::{tt, TokenStream, TokenTree};
-
- /// An iterator over `TokenStream`'s `TokenTree`s.
- /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups,
- /// and returns whole groups as token trees.
- impl IntoIterator for TokenStream {
- type Item = TokenTree;
- type IntoIter = super::IntoIter<TokenTree>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.token_trees.into_iter()
- }
- }
-
- type LexError = String;
-
- /// Attempts to break the string into tokens and parse those tokens into a token stream.
- /// May fail for a number of reasons, for example, if the string contains unbalanced delimiters
- /// or characters not existing in the language.
- /// All tokens in the parsed stream get `Span::call_site()` spans.
- ///
- /// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to
- /// change these errors into `LexError`s later.
- impl FromStr for TokenStream {
- type Err = LexError;
-
- fn from_str(src: &str) -> Result<TokenStream, LexError> {
- let (subtree, _token_map) =
- mbe::parse_to_token_tree(src).ok_or("Failed to parse from mbe")?;
-
- let subtree = subtree_replace_token_ids_with_unspecified(subtree);
- Ok(TokenStream::with_subtree(subtree))
- }
- }
-
- impl ToString for TokenStream {
- fn to_string(&self) -> String {
- ::tt::pretty(&self.token_trees)
- }
- }
-
- fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree {
- tt::Subtree {
- delimiter: tt::Delimiter {
- open: tt::TokenId::UNSPECIFIED,
- close: tt::TokenId::UNSPECIFIED,
- ..subtree.delimiter
- },
- token_trees: subtree
- .token_trees
- .into_iter()
- .map(token_tree_replace_token_ids_with_unspecified)
- .collect(),
- }
- }
-
- fn token_tree_replace_token_ids_with_unspecified(tt: tt::TokenTree) -> tt::TokenTree {
- match tt {
- tt::TokenTree::Leaf(leaf) => {
- tt::TokenTree::Leaf(leaf_replace_token_ids_with_unspecified(leaf))
- }
- tt::TokenTree::Subtree(subtree) => {
- tt::TokenTree::Subtree(subtree_replace_token_ids_with_unspecified(subtree))
- }
- }
- }
-
- fn leaf_replace_token_ids_with_unspecified(leaf: tt::Leaf) -> tt::Leaf {
- match leaf {
- tt::Leaf::Literal(lit) => {
- tt::Leaf::Literal(tt::Literal { span: tt::TokenId::unspecified(), ..lit })
- }
- tt::Leaf::Punct(punct) => {
- tt::Leaf::Punct(tt::Punct { span: tt::TokenId::unspecified(), ..punct })
- }
- tt::Leaf::Ident(ident) => {
- tt::Leaf::Ident(tt::Ident { span: tt::TokenId::unspecified(), ..ident })
- }
- }
- }
-}
-
-impl TokenStreamBuilder {
- fn new() -> TokenStreamBuilder {
- TokenStreamBuilder { acc: TokenStream::new() }
- }
-
- fn push(&mut self, stream: TokenStream) {
- self.acc.extend(stream.into_iter())
- }
-
- fn build(self) -> TokenStream {
- self.acc
- }
-}
-
-pub struct FreeFunctions;
-
-#[derive(Clone)]
-pub struct TokenStreamIter {
- trees: IntoIter<TokenTree>,
-}
-
-#[derive(Default)]
-pub struct RustAnalyzer {
- ident_interner: IdentInterner,
- // FIXME: store span information here.
-}
-
-impl server::Types for RustAnalyzer {
- type FreeFunctions = FreeFunctions;
- type TokenStream = TokenStream;
- type Group = Group;
- type Punct = Punct;
- type Ident = IdentId;
- type Literal = Literal;
- type SourceFile = SourceFile;
- type Diagnostic = Diagnostic;
- type Span = Span;
- type MultiSpan = Vec<Span>;
-}
-
-impl server::FreeFunctions for RustAnalyzer {
- fn track_env_var(&mut self, _var: &str, _value: Option<&str>) {
- // FIXME: track env var accesses
- // https://github.com/rust-lang/rust/pull/71858
- }
- fn track_path(&mut self, _path: &str) {}
-}
-
-impl server::TokenStream for RustAnalyzer {
- fn is_empty(&mut self, stream: &Self::TokenStream) -> bool {
- stream.is_empty()
- }
- fn from_str(&mut self, src: &str) -> Self::TokenStream {
- use std::str::FromStr;
-
- Self::TokenStream::from_str(src).expect("cannot parse string")
- }
- fn to_string(&mut self, stream: &Self::TokenStream) -> String {
- stream.to_string()
- }
- fn from_token_tree(
- &mut self,
- tree: bridge::TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>,
- ) -> Self::TokenStream {
- match tree {
- bridge::TokenTree::Group(group) => {
- let tree = TokenTree::from(group);
- Self::TokenStream::from_iter(vec![tree])
- }
-
- bridge::TokenTree::Ident(IdentId(index)) => {
- let IdentData(ident) = self.ident_interner.get(index).clone();
- let ident: tt::Ident = ident;
- let leaf = tt::Leaf::from(ident);
- let tree = TokenTree::from(leaf);
- Self::TokenStream::from_iter(vec![tree])
- }
-
- bridge::TokenTree::Literal(literal) => {
- let leaf = tt::Leaf::from(literal);
- let tree = TokenTree::from(leaf);
- Self::TokenStream::from_iter(vec![tree])
- }
-
- bridge::TokenTree::Punct(p) => {
- let leaf = tt::Leaf::from(p);
- let tree = TokenTree::from(leaf);
- Self::TokenStream::from_iter(vec![tree])
- }
- }
- }
-
- fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result<Self::TokenStream, ()> {
- Ok(self_.clone())
- }
-
- fn concat_trees(
- &mut self,
- base: Option<Self::TokenStream>,
- trees: Vec<bridge::TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>>,
- ) -> Self::TokenStream {
- let mut builder = TokenStreamBuilder::new();
- if let Some(base) = base {
- builder.push(base);
- }
- for tree in trees {
- builder.push(self.from_token_tree(tree));
- }
- builder.build()
- }
-
- fn concat_streams(
- &mut self,
- base: Option<Self::TokenStream>,
- streams: Vec<Self::TokenStream>,
- ) -> Self::TokenStream {
- let mut builder = TokenStreamBuilder::new();
- if let Some(base) = base {
- builder.push(base);
- }
- for stream in streams {
- builder.push(stream);
- }
- builder.build()
- }
-
- fn into_trees(
- &mut self,
- stream: Self::TokenStream,
- ) -> Vec<bridge::TokenTree<Self::Group, Self::Punct, Self::Ident, Self::Literal>> {
- stream
- .into_iter()
- .map(|tree| match tree {
- tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
- bridge::TokenTree::Ident(IdentId(self.ident_interner.intern(&IdentData(ident))))
- }
- tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => bridge::TokenTree::Literal(lit),
- tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => bridge::TokenTree::Punct(punct),
- tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(subtree),
- })
- .collect()
- }
-}
-
-fn delim_to_internal(d: bridge::Delimiter) -> tt::Delimiter {
- let kind = match d {
- bridge::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis,
- bridge::Delimiter::Brace => tt::DelimiterKind::Brace,
- bridge::Delimiter::Bracket => tt::DelimiterKind::Bracket,
- bridge::Delimiter::None => tt::DelimiterKind::Invisible,
- };
- tt::Delimiter { open: tt::TokenId::unspecified(), close: tt::TokenId::unspecified(), kind }
-}
-
-fn delim_to_external(d: tt::Delimiter) -> bridge::Delimiter {
- match d.kind {
- tt::DelimiterKind::Parenthesis => bridge::Delimiter::Parenthesis,
- tt::DelimiterKind::Brace => bridge::Delimiter::Brace,
- tt::DelimiterKind::Bracket => bridge::Delimiter::Bracket,
- tt::DelimiterKind::Invisible => bridge::Delimiter::None,
- }
-}
-
-fn spacing_to_internal(spacing: bridge::Spacing) -> Spacing {
- match spacing {
- bridge::Spacing::Alone => Spacing::Alone,
- bridge::Spacing::Joint => Spacing::Joint,
- }
-}
-
-fn spacing_to_external(spacing: Spacing) -> bridge::Spacing {
- match spacing {
- Spacing::Alone => bridge::Spacing::Alone,
- Spacing::Joint => bridge::Spacing::Joint,
- }
-}
-
-impl server::Group for RustAnalyzer {
- fn new(
- &mut self,
- delimiter: bridge::Delimiter,
- stream: Option<Self::TokenStream>,
- ) -> Self::Group {
- Self::Group {
- delimiter: delim_to_internal(delimiter),
- token_trees: stream.unwrap_or_default().token_trees,
- }
- }
- fn delimiter(&mut self, group: &Self::Group) -> bridge::Delimiter {
- delim_to_external(group.delimiter)
- }
-
- // NOTE: Return value of do not include delimiter
- fn stream(&mut self, group: &Self::Group) -> Self::TokenStream {
- TokenStream { token_trees: group.token_trees.clone() }
- }
-
- fn span(&mut self, group: &Self::Group) -> Self::Span {
- group.delimiter.open
- }
-
- fn set_span(&mut self, group: &mut Self::Group, span: Self::Span) {
- group.delimiter.open = span;
- }
-
- fn span_open(&mut self, group: &Self::Group) -> Self::Span {
- group.delimiter.open
- }
-
- fn span_close(&mut self, group: &Self::Group) -> Self::Span {
- group.delimiter.close
- }
-}
-
-impl server::Punct for RustAnalyzer {
- fn new(&mut self, ch: char, spacing: bridge::Spacing) -> Self::Punct {
- tt::Punct {
- char: ch,
- spacing: spacing_to_internal(spacing),
- span: tt::TokenId::unspecified(),
- }
- }
- fn as_char(&mut self, punct: Self::Punct) -> char {
- punct.char
- }
- fn spacing(&mut self, punct: Self::Punct) -> bridge::Spacing {
- spacing_to_external(punct.spacing)
- }
- fn span(&mut self, punct: Self::Punct) -> Self::Span {
- punct.span
- }
- fn with_span(&mut self, punct: Self::Punct, span: Self::Span) -> Self::Punct {
- tt::Punct { span: span, ..punct }
- }
-}
-
-impl server::Ident for RustAnalyzer {
- fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident {
- IdentId(self.ident_interner.intern(&IdentData(tt::Ident {
- text: if is_raw { ::tt::SmolStr::from_iter(["r#", string]) } else { string.into() },
- span,
- })))
- }
-
- fn span(&mut self, ident: Self::Ident) -> Self::Span {
- self.ident_interner.get(ident.0).0.span
- }
- fn with_span(&mut self, ident: Self::Ident, span: Self::Span) -> Self::Ident {
- let data = self.ident_interner.get(ident.0);
- let new = IdentData(tt::Ident { span: span, ..data.0.clone() });
- IdentId(self.ident_interner.intern(&new))
- }
-}
-
-impl server::Literal for RustAnalyzer {
- fn debug_kind(&mut self, _literal: &Self::Literal) -> String {
- // r-a: debug_kind and suffix are unsupported; corresponding client code has been changed to not call these.
- // They must still be present to be ABI-compatible and work with upstream proc_macro.
- "".to_owned()
- }
- fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> {
- Ok(Literal { text: s.into(), span: tt::TokenId::unspecified() })
- }
- fn symbol(&mut self, literal: &Self::Literal) -> String {
- literal.text.to_string()
- }
- fn suffix(&mut self, _literal: &Self::Literal) -> Option<String> {
- None
- }
-
- fn to_string(&mut self, literal: &Self::Literal) -> String {
- literal.to_string()
- }
-
- fn integer(&mut self, n: &str) -> Self::Literal {
- let n = match n.parse::<i128>() {
- Ok(n) => n.to_string(),
- Err(_) => n.parse::<u128>().unwrap().to_string(),
- };
- Literal { text: n.into(), span: tt::TokenId::unspecified() }
- }
-
- fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal {
- macro_rules! def_suffixed_integer {
- ($kind:ident, $($ty:ty),*) => {
- match $kind {
- $(
- stringify!($ty) => {
- let n: $ty = n.parse().unwrap();
- format!(concat!("{}", stringify!($ty)), n)
- }
- )*
- _ => unimplemented!("unknown args for typed_integer: n {}, kind {}", n, $kind),
- }
- }
- }
-
- let text = def_suffixed_integer! {kind, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize};
-
- Literal { text: text.into(), span: tt::TokenId::unspecified() }
- }
-
- fn float(&mut self, n: &str) -> Self::Literal {
- let n: f64 = n.parse().unwrap();
- let mut text = f64::to_string(&n);
- if !text.contains('.') {
- text += ".0"
- }
- Literal { text: text.into(), span: tt::TokenId::unspecified() }
- }
-
- fn f32(&mut self, n: &str) -> Self::Literal {
- let n: f32 = n.parse().unwrap();
- let text = format!("{n}f32");
- Literal { text: text.into(), span: tt::TokenId::unspecified() }
- }
-
- fn f64(&mut self, n: &str) -> Self::Literal {
- let n: f64 = n.parse().unwrap();
- let text = format!("{n}f64");
- Literal { text: text.into(), span: tt::TokenId::unspecified() }
- }
-
- fn string(&mut self, string: &str) -> Self::Literal {
- let mut escaped = String::new();
- for ch in string.chars() {
- escaped.extend(ch.escape_debug());
- }
- Literal { text: format!("\"{escaped}\"").into(), span: tt::TokenId::unspecified() }
- }
-
- fn character(&mut self, ch: char) -> Self::Literal {
- Literal { text: format!("'{ch}'").into(), span: tt::TokenId::unspecified() }
- }
-
- fn byte_string(&mut self, bytes: &[u8]) -> Self::Literal {
- let string = bytes
- .iter()
- .cloned()
- .flat_map(ascii::escape_default)
- .map(Into::<char>::into)
- .collect::<String>();
-
- Literal { text: format!("b\"{string}\"").into(), span: tt::TokenId::unspecified() }
- }
-
- fn span(&mut self, literal: &Self::Literal) -> Self::Span {
- literal.span
- }
-
- fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) {
- literal.span = span;
- }
-
- fn subspan(
- &mut self,
- _literal: &Self::Literal,
- _start: Bound<usize>,
- _end: Bound<usize>,
- ) -> Option<Self::Span> {
- // FIXME handle span
- None
- }
-}
-
-impl server::SourceFile for RustAnalyzer {
- // FIXME these are all stubs
- fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool {
- true
- }
- fn path(&mut self, _file: &Self::SourceFile) -> String {
- String::new()
- }
- fn is_real(&mut self, _file: &Self::SourceFile) -> bool {
- true
- }
-}
-
-impl server::Diagnostic for RustAnalyzer {
- fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic {
- let mut diag = Diagnostic::new(level, msg);
- diag.spans = spans;
- diag
- }
-
- fn sub(
- &mut self,
- _diag: &mut Self::Diagnostic,
- _level: Level,
- _msg: &str,
- _spans: Self::MultiSpan,
- ) {
- // FIXME handle diagnostic
- //
- }
-
- fn emit(&mut self, _diag: Self::Diagnostic) {
- // FIXME handle diagnostic
- // diag.emit()
- }
-}
-
-impl server::Span for RustAnalyzer {
- fn debug(&mut self, span: Self::Span) -> String {
- format!("{:?}", span.0)
- }
- fn def_site(&mut self) -> Self::Span {
- // MySpan(self.span_interner.intern(&MySpanData(Span::def_site())))
- // FIXME handle span
- tt::TokenId::unspecified()
- }
- fn call_site(&mut self) -> Self::Span {
- // MySpan(self.span_interner.intern(&MySpanData(Span::call_site())))
- // FIXME handle span
- tt::TokenId::unspecified()
- }
- fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile {
- SourceFile {}
- }
- fn save_span(&mut self, _span: Self::Span) -> usize {
- // FIXME stub
- 0
- }
- fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span {
- // FIXME stub
- tt::TokenId::unspecified()
- }
- /// Recent feature, not yet in the proc_macro
- ///
- /// See PR:
- /// https://github.com/rust-lang/rust/pull/55780
- fn source_text(&mut self, _span: Self::Span) -> Option<String> {
- None
- }
-
- fn parent(&mut self, _span: Self::Span) -> Option<Self::Span> {
- // FIXME handle span
- None
- }
- fn source(&mut self, span: Self::Span) -> Self::Span {
- // FIXME handle span
- span
- }
- fn start(&mut self, _span: Self::Span) -> LineColumn {
- // FIXME handle span
- LineColumn { line: 0, column: 0 }
- }
- fn end(&mut self, _span: Self::Span) -> LineColumn {
- // FIXME handle span
- LineColumn { line: 0, column: 0 }
- }
- fn join(&mut self, first: Self::Span, _second: Self::Span) -> Option<Self::Span> {
- // Just return the first span again, because some macros will unwrap the result.
- Some(first)
- }
- fn resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span {
- // FIXME handle span
- tt::TokenId::unspecified()
- }
-
- fn mixed_site(&mut self) -> Self::Span {
- // FIXME handle span
- tt::TokenId::unspecified()
- }
-
- fn after(&mut self, _self_: Self::Span) -> Self::Span {
- tt::TokenId::unspecified()
- }
-
- fn before(&mut self, _self_: Self::Span) -> Self::Span {
- tt::TokenId::unspecified()
- }
-}
-
-impl server::MultiSpan for RustAnalyzer {
- fn new(&mut self) -> Self::MultiSpan {
- // FIXME handle span
- vec![]
- }
-
- fn push(&mut self, other: &mut Self::MultiSpan, span: Self::Span) {
- //TODP
- other.push(span)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::super::proc_macro::bridge::server::Literal;
- use super::*;
-
- #[test]
- fn test_ra_server_literals() {
- let mut srv = RustAnalyzer { ident_interner: IdentInterner::default() };
- assert_eq!(srv.integer("1234").text, "1234");
-
- assert_eq!(srv.typed_integer("12", "u8").text, "12u8");
- assert_eq!(srv.typed_integer("255", "u16").text, "255u16");
- assert_eq!(srv.typed_integer("1234", "u32").text, "1234u32");
- assert_eq!(srv.typed_integer("15846685", "u64").text, "15846685u64");
- assert_eq!(srv.typed_integer("15846685258", "u128").text, "15846685258u128");
- assert_eq!(srv.typed_integer("156788984", "usize").text, "156788984usize");
- assert_eq!(srv.typed_integer("127", "i8").text, "127i8");
- assert_eq!(srv.typed_integer("255", "i16").text, "255i16");
- assert_eq!(srv.typed_integer("1234", "i32").text, "1234i32");
- assert_eq!(srv.typed_integer("15846685", "i64").text, "15846685i64");
- assert_eq!(srv.typed_integer("15846685258", "i128").text, "15846685258i128");
- assert_eq!(srv.float("0").text, "0.0");
- assert_eq!(srv.float("15684.5867").text, "15684.5867");
- assert_eq!(srv.f32("15684.58").text, "15684.58f32");
- assert_eq!(srv.f64("15684.58").text, "15684.58f64");
-
- assert_eq!(srv.string("hello_world").text, "\"hello_world\"");
- assert_eq!(srv.character('c').text, "'c'");
- assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\"");
-
- // u128::max
- assert_eq!(
- srv.integer("340282366920938463463374607431768211455").text,
- "340282366920938463463374607431768211455"
- );
- // i128::min
- assert_eq!(
- srv.integer("-170141183460469231731687303715884105728").text,
- "-170141183460469231731687303715884105728"
- );
- }
-
- #[test]
- fn test_ra_server_to_string() {
- let s = TokenStream {
- token_trees: vec![
- tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
- text: "struct".into(),
- span: tt::TokenId::unspecified(),
- })),
- tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
- text: "T".into(),
- span: tt::TokenId::unspecified(),
- })),
- tt::TokenTree::Subtree(tt::Subtree {
- delimiter: tt::Delimiter {
- open: tt::TokenId::unspecified(),
- close: tt::TokenId::unspecified(),
- kind: tt::DelimiterKind::Brace,
- },
- token_trees: vec![],
- }),
- ],
- };
-
- assert_eq!(s.to_string(), "struct T {}");
- }
-
- #[test]
- fn test_ra_server_from_str() {
- use std::str::FromStr;
- let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree {
- delimiter: tt::Delimiter {
- open: tt::TokenId::unspecified(),
- close: tt::TokenId::unspecified(),
- kind: tt::DelimiterKind::Parenthesis,
- },
- token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
- text: "a".into(),
- span: tt::TokenId::unspecified(),
- }))],
- });
-
- let t1 = TokenStream::from_str("(a)").unwrap();
- assert_eq!(t1.token_trees.len(), 1);
- assert_eq!(t1.token_trees[0], subtree_paren_a);
-
- let t2 = TokenStream::from_str("(a);").unwrap();
- assert_eq!(t2.token_trees.len(), 2);
- assert_eq!(t2.token_trees[0], subtree_paren_a);
-
- let underscore = TokenStream::from_str("_").unwrap();
- assert_eq!(
- underscore.token_trees[0],
- tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
- text: "_".into(),
- span: tt::TokenId::unspecified(),
- }))
- );
- }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs
deleted file mode 100644
index 04be39cff..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs
+++ /dev/null
@@ -1,146 +0,0 @@
-//! Procedural macros are implemented by compiling the macro providing crate
-//! to a dynamic library with a particular ABI which the compiler uses to expand
-//! macros. Unfortunately this ABI is not specified and can change from version
-//! to version of the compiler. To support this we copy the ABI from the rust
-//! compiler into submodules of this module (e.g proc_macro_srv::abis::abi_1_47).
-//!
-//! All of these ABIs are subsumed in the `Abi` enum, which exposes a simple
-//! interface the rest of rust-analyzer can use to talk to the macro
-//! provider.
-//!
-//! # Adding a new ABI
-//!
-//! To add a new ABI you'll need to copy the source of the target proc_macro
-//! crate from the source tree of the Rust compiler into this directory tree.
-//! Then you'll need to modify it
-//! - Remove any feature! or other things which won't compile on stable
-//! - change any absolute imports to relative imports within the ABI tree
-//!
-//! Then you'll need to add a branch to the `Abi` enum and an implementation of
-//! `Abi::expand`, `Abi::list_macros` and `Abi::from_lib` for the new ABI. See
-//! `proc_macro_srv/src/abis/abi_1_47/mod.rs` for an example. Finally you'll
-//! need to update the conditionals in `Abi::from_lib` to return your new ABI
-//! for the relevant versions of the rust compiler
-//!
-
-mod abi_1_63;
-#[cfg(feature = "sysroot-abi")]
-mod abi_sysroot;
-
-// see `build.rs`
-include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
-
-// Used by `test/utils.rs`
-#[cfg(all(test, feature = "sysroot-abi"))]
-pub(crate) use abi_sysroot::TokenStream as TestTokenStream;
-
-use super::dylib::LoadProcMacroDylibError;
-pub(crate) use abi_1_63::Abi as Abi_1_63;
-#[cfg(feature = "sysroot-abi")]
-pub(crate) use abi_sysroot::Abi as Abi_Sysroot;
-use libloading::Library;
-use proc_macro_api::{ProcMacroKind, RustCInfo};
-
-use crate::tt;
-
-pub struct PanicMessage {
- message: Option<String>,
-}
-
-impl PanicMessage {
- pub fn as_str(&self) -> Option<String> {
- self.message.clone()
- }
-}
-
-pub(crate) enum Abi {
- Abi1_63(Abi_1_63),
- #[cfg(feature = "sysroot-abi")]
- AbiSysroot(Abi_Sysroot),
-}
-
-impl Abi {
- /// Load a new ABI.
- ///
- /// # Arguments
- ///
- /// *`lib` - The dynamic library containing the macro implementations
- /// *`symbol_name` - The symbol name the macros can be found attributes
- /// *`info` - RustCInfo about the compiler that was used to compile the
- /// macro crate. This is the information we use to figure out
- /// which ABI to return
- pub fn from_lib(
- lib: &Library,
- symbol_name: String,
- info: RustCInfo,
- ) -> Result<Abi, LoadProcMacroDylibError> {
- // the sysroot ABI relies on `extern proc_macro` with unstable features,
- // instead of a snapshot of the proc macro bridge's source code. it's only
- // enabled if we have an exact version match.
- #[cfg(feature = "sysroot-abi")]
- {
- if info.version_string == RUSTC_VERSION_STRING {
- let inner = unsafe { Abi_Sysroot::from_lib(lib, symbol_name) }?;
- return Ok(Abi::AbiSysroot(inner));
- }
-
- // if we reached this point, versions didn't match. in testing, we
- // want that to panic - this could mean that the format of `rustc
- // --version` no longer matches the format of the version string
- // stored in the `.rustc` section, and we want to catch that in-tree
- // with `x.py test`
- #[cfg(test)]
- {
- let allow_mismatch = std::env::var("PROC_MACRO_SRV_ALLOW_SYSROOT_MISMATCH");
- if let Ok("1") = allow_mismatch.as_deref() {
- // only used by rust-analyzer developers, when working on the
- // sysroot ABI from the rust-analyzer repository - which should
- // only happen pre-subtree. this can be removed later.
- } else {
- panic!(
- "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}",
- info.version_string, RUSTC_VERSION_STRING
- );
- }
- }
- }
-
- // FIXME: this should use exclusive ranges when they're stable
- // https://github.com/rust-lang/rust/issues/37854
- match (info.version.0, info.version.1) {
- (1, 63) => {
- let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?;
- Ok(Abi::Abi1_63(inner))
- }
- _ => Err(LoadProcMacroDylibError::UnsupportedABI(info.version_string)),
- }
- }
-
- pub fn expand(
- &self,
- macro_name: &str,
- macro_body: &tt::Subtree,
- attributes: Option<&tt::Subtree>,
- ) -> Result<tt::Subtree, PanicMessage> {
- match self {
- Self::Abi1_63(abi) => abi.expand(macro_name, macro_body, attributes),
- #[cfg(feature = "sysroot-abi")]
- Self::AbiSysroot(abi) => abi.expand(macro_name, macro_body, attributes),
- }
- }
-
- pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
- match self {
- Self::Abi1_63(abi) => abi.list_macros(),
- #[cfg(feature = "sysroot-abi")]
- Self::AbiSysroot(abi) => abi.list_macros(),
- }
- }
-}
-
-#[test]
-fn test_version_check() {
- let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path());
- let info = proc_macro_api::read_dylib_info(&path).unwrap();
- assert!(info.version.1 >= 50);
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/cli.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/cli.rs
deleted file mode 100644
index 05168feb6..000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/cli.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-//! Driver for proc macro server
-use std::io;
-
-use proc_macro_api::msg::{self, Message};
-
-use crate::ProcMacroSrv;
-
-pub fn run() -> io::Result<()> {
- let mut srv = ProcMacroSrv::default();
- let mut buf = String::new();
-
- while let Some(req) = read_request(&mut buf)? {
- let res = match req {
- msg::Request::ListMacros { dylib_path } => {
- msg::Response::ListMacros(srv.list_macros(&dylib_path))
- }
- msg::Request::ExpandMacro(task) => msg::Response::ExpandMacro(srv.expand(task)),
- msg::Request::ApiVersionCheck {} => {
- msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION)
- }
- };
- write_response(res)?
- }
-
- Ok(())
-}
-
-fn read_request(buf: &mut String) -> io::Result<Option<msg::Request>> {
- msg::Request::read(&mut io::stdin().lock(), buf)
-}
-
-fn write_response(msg: msg::Response) -> io::Result<()> {
- msg.write(&mut io::stdout().lock())
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
index 89ffd1f49..dd05e250c 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs
@@ -13,10 +13,6 @@ use object::Object;
use paths::AbsPath;
use proc_macro_api::{read_dylib_info, ProcMacroKind};
-use crate::tt;
-
-use super::abis::Abi;
-
const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> io::Error {
@@ -82,14 +78,17 @@ fn load_library(file: &Path) -> Result<Library, libloading::Error> {
pub enum LoadProcMacroDylibError {
Io(io::Error),
LibLoading(libloading::Error),
- UnsupportedABI(String),
+ AbiMismatch(String),
}
impl fmt::Display for LoadProcMacroDylibError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Io(e) => e.fmt(f),
- Self::UnsupportedABI(v) => write!(f, "unsupported ABI `{v}`"),
+ Self::AbiMismatch(v) => {
+ use crate::RUSTC_VERSION_STRING;
+ write!(f, "mismatched ABI expected: `{RUSTC_VERSION_STRING}`, got `{v}`")
+ }
Self::LibLoading(e) => e.fmt(f),
}
}
@@ -110,7 +109,7 @@ impl From<libloading::Error> for LoadProcMacroDylibError {
struct ProcMacroLibraryLibloading {
// Hold on to the library so it doesn't unload
_lib: Library,
- abi: Abi,
+ proc_macros: crate::proc_macros::ProcMacros,
}
impl ProcMacroLibraryLibloading {
@@ -125,8 +124,9 @@ impl ProcMacroLibraryLibloading {
let version_info = read_dylib_info(abs_file)?;
let lib = load_library(file).map_err(invalid_data_err)?;
- let abi = Abi::from_lib(&lib, symbol_name, version_info)?;
- Ok(ProcMacroLibraryLibloading { _lib: lib, abi })
+ let proc_macros =
+ crate::proc_macros::ProcMacros::from_lib(&lib, symbol_name, version_info)?;
+ Ok(ProcMacroLibraryLibloading { _lib: lib, proc_macros })
}
}
@@ -150,15 +150,15 @@ impl Expander {
pub fn expand(
&self,
macro_name: &str,
- macro_body: &tt::Subtree,
- attributes: Option<&tt::Subtree>,
- ) -> Result<tt::Subtree, String> {
- let result = self.inner.abi.expand(macro_name, macro_body, attributes);
+ macro_body: &crate::tt::Subtree,
+ attributes: Option<&crate::tt::Subtree>,
+ ) -> Result<crate::tt::Subtree, String> {
+ let result = self.inner.proc_macros.expand(macro_name, macro_body, attributes);
result.map_err(|e| e.as_str().unwrap_or_else(|| "<unknown error>".to_string()))
}
pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
- self.inner.abi.list_macros()
+ self.inner.proc_macros.list_macros()
}
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
index ee70fe7d4..84bd15efb 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
@@ -10,17 +10,16 @@
//! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable`
//! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)…
+#![cfg(feature = "sysroot-abi")]
+#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)]
#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
-#![cfg_attr(
- feature = "sysroot-abi",
- feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)
-)]
#![allow(unreachable_pub)]
-mod dylib;
-mod abis;
+extern crate proc_macro;
-pub mod cli;
+mod dylib;
+mod server;
+mod proc_macros;
use std::{
collections::{hash_map::Entry, HashMap},
@@ -33,24 +32,27 @@ use std::{
};
use proc_macro_api::{
- msg::{ExpandMacro, FlatTree, PanicMessage},
+ msg::{self, CURRENT_API_VERSION},
ProcMacroKind,
};
use ::tt::token_id as tt;
+// see `build.rs`
+include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
+
#[derive(Default)]
-pub(crate) struct ProcMacroSrv {
+pub struct ProcMacroSrv {
expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>,
}
const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024;
impl ProcMacroSrv {
- pub fn expand(&mut self, task: ExpandMacro) -> Result<FlatTree, PanicMessage> {
+ pub fn expand(&mut self, task: msg::ExpandMacro) -> Result<msg::FlatTree, msg::PanicMessage> {
let expander = self.expander(task.lib.as_ref()).map_err(|err| {
debug_assert!(false, "should list macros before asking to expand");
- PanicMessage(format!("failed to load macro: {err}"))
+ msg::PanicMessage(format!("failed to load macro: {err}"))
})?;
let prev_env = EnvSnapshot::new();
@@ -68,8 +70,8 @@ impl ProcMacroSrv {
None => None,
};
- let macro_body = task.macro_body.to_subtree();
- let attributes = task.attributes.map(|it| it.to_subtree());
+ let macro_body = task.macro_body.to_subtree(CURRENT_API_VERSION);
+ let attributes = task.attributes.map(|it| it.to_subtree(CURRENT_API_VERSION));
let result = thread::scope(|s| {
let thread = thread::Builder::new()
.stack_size(EXPANDER_STACK_SIZE)
@@ -77,7 +79,7 @@ impl ProcMacroSrv {
.spawn_scoped(s, || {
expander
.expand(&task.macro_name, &macro_body, attributes.as_ref())
- .map(|it| FlatTree::new(&it))
+ .map(|it| msg::FlatTree::new(&it, CURRENT_API_VERSION))
});
let res = match thread {
Ok(handle) => handle.join(),
@@ -102,10 +104,10 @@ impl ProcMacroSrv {
}
}
- result.map_err(PanicMessage)
+ result.map_err(msg::PanicMessage)
}
- pub(crate) fn list_macros(
+ pub fn list_macros(
&mut self,
dylib_path: &Path,
) -> Result<Vec<(String, ProcMacroKind)>, String> {
@@ -129,6 +131,16 @@ impl ProcMacroSrv {
}
}
+pub struct PanicMessage {
+ message: Option<String>,
+}
+
+impl PanicMessage {
+ pub fn as_str(&self) -> Option<String> {
+ self.message.clone()
+ }
+}
+
struct EnvSnapshot {
vars: HashMap<OsString, OsString>,
}
@@ -138,10 +150,13 @@ impl EnvSnapshot {
EnvSnapshot { vars: env::vars_os().collect() }
}
- fn rollback(self) {
- let mut old_vars = self.vars;
+ fn rollback(self) {}
+}
+
+impl Drop for EnvSnapshot {
+ fn drop(&mut self) {
for (name, value) in env::vars_os() {
- let old_value = old_vars.remove(&name);
+ let old_value = self.vars.remove(&name);
if old_value != Some(value) {
match old_value {
None => env::remove_var(name),
@@ -149,13 +164,13 @@ impl EnvSnapshot {
}
}
}
- for (name, old_value) in old_vars {
+ for (name, old_value) in self.vars.drain() {
env::set_var(name, old_value)
}
}
}
-#[cfg(all(feature = "sysroot-abi", test))]
+#[cfg(test)]
mod tests;
#[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs
index 0a3b8866a..3c6f32033 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/proc_macros.rs
@@ -1,45 +1,55 @@
//! Proc macro ABI
-extern crate proc_macro;
-
-#[allow(dead_code)]
-#[doc(hidden)]
-mod ra_server;
-
use libloading::Library;
-use proc_macro_api::ProcMacroKind;
+use proc_macro_api::{ProcMacroKind, RustCInfo};
-use super::{tt, PanicMessage};
+use crate::{dylib::LoadProcMacroDylibError, server::SYMBOL_INTERNER, tt};
-pub use ra_server::TokenStream;
-
-pub(crate) struct Abi {
+pub(crate) struct ProcMacros {
exported_macros: Vec<proc_macro::bridge::client::ProcMacro>,
}
-impl From<proc_macro::bridge::PanicMessage> for PanicMessage {
+impl From<proc_macro::bridge::PanicMessage> for crate::PanicMessage {
fn from(p: proc_macro::bridge::PanicMessage) -> Self {
Self { message: p.as_str().map(|s| s.to_string()) }
}
}
-impl Abi {
- pub unsafe fn from_lib(lib: &Library, symbol_name: String) -> Result<Abi, libloading::Error> {
- let macros: libloading::Symbol<'_, &&[proc_macro::bridge::client::ProcMacro]> =
- lib.get(symbol_name.as_bytes())?;
- Ok(Self { exported_macros: macros.to_vec() })
+impl ProcMacros {
+ /// Load a new ABI.
+ ///
+ /// # Arguments
+ ///
+ /// *`lib` - The dynamic library containing the macro implementations
+ /// *`symbol_name` - The symbol name the macros can be found attributes
+ /// *`info` - RustCInfo about the compiler that was used to compile the
+ /// macro crate. This is the information we use to figure out
+ /// which ABI to return
+ pub(crate) fn from_lib(
+ lib: &Library,
+ symbol_name: String,
+ info: RustCInfo,
+ ) -> Result<ProcMacros, LoadProcMacroDylibError> {
+ if info.version_string == crate::RUSTC_VERSION_STRING {
+ let macros = unsafe {
+ lib.get::<&&[proc_macro::bridge::client::ProcMacro]>(symbol_name.as_bytes())
+ }?;
+
+ return Ok(Self { exported_macros: macros.to_vec() });
+ }
+ Err(LoadProcMacroDylibError::AbiMismatch(info.version_string))
}
- pub fn expand(
+ pub(crate) fn expand(
&self,
macro_name: &str,
macro_body: &tt::Subtree,
attributes: Option<&tt::Subtree>,
- ) -> Result<tt::Subtree, PanicMessage> {
- let parsed_body = ra_server::TokenStream::with_subtree(macro_body.clone());
+ ) -> Result<tt::Subtree, crate::PanicMessage> {
+ let parsed_body = crate::server::TokenStream::with_subtree(macro_body.clone());
- let parsed_attributes = attributes.map_or(ra_server::TokenStream::new(), |attr| {
- ra_server::TokenStream::with_subtree(attr.clone())
+ let parsed_attributes = attributes.map_or(crate::server::TokenStream::new(), |attr| {
+ crate::server::TokenStream::with_subtree(attr.clone())
});
for proc_macro in &self.exported_macros {
@@ -49,34 +59,34 @@ impl Abi {
} if *trait_name == macro_name => {
let res = client.run(
&proc_macro::bridge::server::SameThread,
- ra_server::RustAnalyzer::default(),
+ crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER },
parsed_body,
true,
);
- return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
+ return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from);
}
proc_macro::bridge::client::ProcMacro::Bang { name, client }
if *name == macro_name =>
{
let res = client.run(
&proc_macro::bridge::server::SameThread,
- ra_server::RustAnalyzer::default(),
+ crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER },
parsed_body,
true,
);
- return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
+ return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from);
}
proc_macro::bridge::client::ProcMacro::Attr { name, client }
if *name == macro_name =>
{
let res = client.run(
&proc_macro::bridge::server::SameThread,
- ra_server::RustAnalyzer::default(),
+ crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER },
parsed_attributes,
parsed_body,
true,
);
- return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
+ return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from);
}
_ => continue,
}
@@ -85,7 +95,7 @@ impl Abi {
Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into())
}
- pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
+ pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
self.exported_macros
.iter()
.map(|proc_macro| match proc_macro {
@@ -102,3 +112,16 @@ impl Abi {
.collect()
}
}
+
+#[test]
+fn test_version_check() {
+ let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path());
+ let info = proc_macro_api::read_dylib_info(&path).unwrap();
+ assert_eq!(
+ info.version_string,
+ crate::RUSTC_VERSION_STRING,
+ "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}",
+ info.version_string,
+ crate::RUSTC_VERSION_STRING,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs
index a9cd8e705..1980d4c78 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server.rs
@@ -8,10 +8,7 @@
//!
//! FIXME: No span and source file information is implemented yet
-use super::proc_macro::{
- self,
- bridge::{self, server},
-};
+use proc_macro::bridge::{self, server};
mod token_stream;
pub use token_stream::TokenStream;
@@ -26,8 +23,10 @@ use crate::tt;
type Group = tt::Subtree;
type TokenTree = tt::TokenTree;
+#[allow(unused)]
type Punct = tt::Punct;
type Spacing = tt::Spacing;
+#[allow(unused)]
type Literal = tt::Literal;
type Span = tt::TokenId;
@@ -36,14 +35,11 @@ pub struct SourceFile {
// FIXME stub
}
-type Level = super::proc_macro::Level;
-type LineColumn = super::proc_macro::LineColumn;
-
pub struct FreeFunctions;
-#[derive(Default)]
pub struct RustAnalyzer {
// FIXME: store span information here.
+ pub(crate) interner: SymbolInternerRef,
}
impl server::Types for RustAnalyzer {
@@ -68,7 +64,7 @@ impl server::FreeFunctions for RustAnalyzer {
// FIXME: keep track of LitKind and Suffix
Ok(bridge::Literal {
kind: bridge::LitKind::Err,
- symbol: Symbol::intern(s),
+ symbol: Symbol::intern(self.interner, s),
suffix: None,
span: tt::TokenId::unspecified(),
})
@@ -98,7 +94,7 @@ impl server::TokenStream for RustAnalyzer {
match tree {
bridge::TokenTree::Group(group) => {
let group = Group {
- delimiter: delim_to_internal(group.delimiter),
+ delimiter: delim_to_internal(group.delimiter, group.span),
token_trees: match group.stream {
Some(stream) => stream.into_iter().collect(),
None => Vec::new(),
@@ -109,7 +105,7 @@ impl server::TokenStream for RustAnalyzer {
}
bridge::TokenTree::Ident(ident) => {
- let text = ident.sym.text();
+ let text = ident.sym.text(self.interner);
let text =
if ident.is_raw { ::tt::SmolStr::from_iter(["r#", &text]) } else { text };
let ident: tt::Ident = tt::Ident { text, span: ident.span };
@@ -120,8 +116,9 @@ impl server::TokenStream for RustAnalyzer {
bridge::TokenTree::Literal(literal) => {
let literal = LiteralFormatter(literal);
- let text = literal
- .with_stringify_parts(|parts| ::tt::SmolStr::from_iter(parts.iter().copied()));
+ let text = literal.with_stringify_parts(self.interner, |parts| {
+ ::tt::SmolStr::from_iter(parts.iter().copied())
+ });
let literal = tt::Literal { text, span: literal.0.span };
let leaf = tt::Leaf::from(literal);
@@ -185,7 +182,7 @@ impl server::TokenStream for RustAnalyzer {
.map(|tree| match tree {
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
bridge::TokenTree::Ident(bridge::Ident {
- sym: Symbol::intern(ident.text.trim_start_matches("r#")),
+ sym: Symbol::intern(self.interner, ident.text.trim_start_matches("r#")),
is_raw: ident.text.starts_with("r#"),
span: ident.span,
})
@@ -194,7 +191,7 @@ impl server::TokenStream for RustAnalyzer {
bridge::TokenTree::Literal(bridge::Literal {
// FIXME: handle literal kinds
kind: bridge::LitKind::Err,
- symbol: Symbol::intern(&lit.text),
+ symbol: Symbol::intern(self.interner, &lit.text),
// FIXME: handle suffixes
suffix: None,
span: lit.span,
@@ -221,14 +218,14 @@ impl server::TokenStream for RustAnalyzer {
}
}
-fn delim_to_internal(d: proc_macro::Delimiter) -> tt::Delimiter {
+fn delim_to_internal(d: proc_macro::Delimiter, span: bridge::DelimSpan<Span>) -> tt::Delimiter {
let kind = match d {
proc_macro::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis,
proc_macro::Delimiter::Brace => tt::DelimiterKind::Brace,
proc_macro::Delimiter::Bracket => tt::DelimiterKind::Bracket,
proc_macro::Delimiter::None => tt::DelimiterKind::Invisible,
};
- tt::Delimiter { open: tt::TokenId::unspecified(), close: tt::TokenId::unspecified(), kind }
+ tt::Delimiter { open: span.open, close: span.close, kind }
}
fn delim_to_external(d: tt::Delimiter) -> proc_macro::Delimiter {
@@ -240,6 +237,7 @@ fn delim_to_external(d: tt::Delimiter) -> proc_macro::Delimiter {
}
}
+#[allow(unused)]
fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing {
match spacing {
proc_macro::Spacing::Alone => Spacing::Alone,
@@ -247,6 +245,7 @@ fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing {
}
}
+#[allow(unused)]
fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing {
match spacing {
Spacing::Alone => proc_macro::Spacing::Alone,
@@ -302,14 +301,6 @@ impl server::Span for RustAnalyzer {
// FIXME handle span
Range { start: 0, end: 0 }
}
- fn start(&mut self, _span: Self::Span) -> LineColumn {
- // FIXME handle span
- LineColumn { line: 0, column: 0 }
- }
- fn end(&mut self, _span: Self::Span) -> LineColumn {
- // FIXME handle span
- LineColumn { line: 0, column: 0 }
- }
fn join(&mut self, first: Self::Span, _second: Self::Span) -> Option<Self::Span> {
// Just return the first span again, because some macros will unwrap the result.
Some(first)
@@ -328,13 +319,23 @@ impl server::Span for RustAnalyzer {
tt::TokenId::unspecified()
}
- fn after(&mut self, _self_: Self::Span) -> Self::Span {
+ fn end(&mut self, _self_: Self::Span) -> Self::Span {
tt::TokenId::unspecified()
}
- fn before(&mut self, _self_: Self::Span) -> Self::Span {
+ fn start(&mut self, _self_: Self::Span) -> Self::Span {
tt::TokenId::unspecified()
}
+
+ fn line(&mut self, _span: Self::Span) -> usize {
+ // FIXME handle line
+ 0
+ }
+
+ fn column(&mut self, _span: Self::Span) -> usize {
+ // FIXME handle column
+ 0
+ }
}
impl server::Symbol for RustAnalyzer {
@@ -354,11 +355,13 @@ impl server::Server for RustAnalyzer {
}
fn intern_symbol(ident: &str) -> Self::Symbol {
- Symbol::intern(&::tt::SmolStr::from(ident))
+ // FIXME: should be self.interner once the proc-macro api allows is
+ Symbol::intern(&SYMBOL_INTERNER, &::tt::SmolStr::from(ident))
}
fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) {
- f(symbol.text().as_str())
+ // FIXME: should be self.interner once the proc-macro api allows is
+ f(symbol.text(&SYMBOL_INTERNER).as_str())
}
}
@@ -369,7 +372,11 @@ impl LiteralFormatter {
/// literal's representation. This is done to allow the `ToString` and
/// `Display` implementations to borrow references to symbol values, and
/// both be optimized to reduce overhead.
- fn with_stringify_parts<R>(&self, f: impl FnOnce(&[&str]) -> R) -> R {
+ fn with_stringify_parts<R>(
+ &self,
+ interner: SymbolInternerRef,
+ f: impl FnOnce(&[&str]) -> R,
+ ) -> R {
/// Returns a string containing exactly `num` '#' characters.
/// Uses a 256-character source string literal which is always safe to
/// index with a `u8` index.
@@ -384,7 +391,7 @@ impl LiteralFormatter {
&HASHES[..num as usize]
}
- self.with_symbol_and_suffix(|symbol, suffix| match self.0.kind {
+ self.with_symbol_and_suffix(interner, |symbol, suffix| match self.0.kind {
bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]),
bridge::LitKind::Char => f(&["'", symbol, "'", suffix]),
bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]),
@@ -401,9 +408,13 @@ impl LiteralFormatter {
})
}
- fn with_symbol_and_suffix<R>(&self, f: impl FnOnce(&str, &str) -> R) -> R {
- let symbol = self.0.symbol.text();
- let suffix = self.0.suffix.map(|s| s.text()).unwrap_or_default();
+ fn with_symbol_and_suffix<R>(
+ &self,
+ interner: SymbolInternerRef,
+ f: impl FnOnce(&str, &str) -> R,
+ ) -> R {
+ let symbol = self.0.symbol.text(interner);
+ let suffix = self.0.suffix.map(|s| s.text(interner)).unwrap_or_default();
f(symbol.as_str(), suffix.as_str())
}
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/symbol.rs
index 51dfba2ea..540d06457 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/symbol.rs
@@ -1,28 +1,30 @@
//! Symbol interner for proc-macro-srv
-use std::{cell::RefCell, collections::HashMap};
+use std::{cell::RefCell, collections::HashMap, thread::LocalKey};
use tt::SmolStr;
thread_local! {
- static SYMBOL_INTERNER: RefCell<SymbolInterner> = Default::default();
+ pub(crate) static SYMBOL_INTERNER: RefCell<SymbolInterner> = Default::default();
}
// ID for an interned symbol.
#[derive(Hash, Eq, PartialEq, Copy, Clone)]
pub struct Symbol(u32);
+pub(crate) type SymbolInternerRef = &'static LocalKey<RefCell<SymbolInterner>>;
+
impl Symbol {
- pub fn intern(data: &str) -> Symbol {
- SYMBOL_INTERNER.with(|i| i.borrow_mut().intern(data))
+ pub(super) fn intern(interner: SymbolInternerRef, data: &str) -> Symbol {
+ interner.with(|i| i.borrow_mut().intern(data))
}
- pub fn text(&self) -> SmolStr {
- SYMBOL_INTERNER.with(|i| i.borrow().get(self).clone())
+ pub(super) fn text(&self, interner: SymbolInternerRef) -> SmolStr {
+ interner.with(|i| i.borrow().get(self).clone())
}
}
#[derive(Default)]
-struct SymbolInterner {
+pub(crate) struct SymbolInterner {
idents: HashMap<SmolStr, u32>,
ident_data: Vec<SmolStr>,
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs
index d091d4319..2589d8b64 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server/token_stream.rs
@@ -4,15 +4,15 @@ use crate::tt::{self, TokenTree};
#[derive(Debug, Default, Clone)]
pub struct TokenStream {
- pub token_trees: Vec<TokenTree>,
+ pub(super) token_trees: Vec<TokenTree>,
}
impl TokenStream {
- pub fn new() -> Self {
+ pub(crate) fn new() -> Self {
TokenStream::default()
}
- pub fn with_subtree(subtree: tt::Subtree) -> Self {
+ pub(crate) fn with_subtree(subtree: tt::Subtree) -> Self {
if subtree.delimiter.kind != tt::DelimiterKind::Invisible {
TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] }
} else {
@@ -20,11 +20,11 @@ impl TokenStream {
}
}
- pub fn into_subtree(self) -> tt::Subtree {
+ pub(crate) fn into_subtree(self) -> tt::Subtree {
tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: self.token_trees }
}
- pub fn is_empty(&self) -> bool {
+ pub(super) fn is_empty(&self) -> bool {
self.token_trees.is_empty()
}
}
@@ -78,12 +78,12 @@ impl Extend<TokenStream> for TokenStream {
}
}
-pub struct TokenStreamBuilder {
+pub(super) struct TokenStreamBuilder {
acc: TokenStream,
}
-/// Public implementation details for the `TokenStream` type, such as iterators.
-pub mod token_stream {
+/// pub(super)lic implementation details for the `TokenStream` type, such as iterators.
+pub(super) mod token_stream {
use std::str::FromStr;
use super::{tt, TokenStream, TokenTree};
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
index efbeb90ca..49b4d973b 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
@@ -5,14 +5,14 @@ use std::str::FromStr;
use crate::{dylib, proc_macro_test_dylib_path, ProcMacroSrv};
-fn parse_string(code: &str) -> Option<crate::abis::TestTokenStream> {
+fn parse_string(code: &str) -> Option<crate::server::TokenStream> {
// This is a bit strange. We need to parse a string into a token stream into
// order to create a tt::SubTree from it in fixtures. `into_subtree` is
// implemented by all the ABIs we have so we arbitrarily choose one ABI to
// write a `parse_string` function for and use that. The tests don't really
// care which ABI we're using as the `into_subtree` function isn't part of
// the ABI and shouldn't change between ABI versions.
- crate::abis::TestTokenStream::from_str(code).ok()
+ crate::server::TokenStream::from_str(code).ok()
}
pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect) {
diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml
index 6273ea51d..602e74275 100644
--- a/src/tools/rust-analyzer/crates/profile/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml
@@ -20,7 +20,7 @@ countme = { version = "3.0.1", features = ["enable"] }
jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = true }
[target.'cfg(target_os = "linux")'.dependencies]
-perf-event = "0.4.7"
+perf-event = "=0.4.7"
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.9", features = ["processthreadsapi", "psapi"] }
diff --git a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs
index 8017f8657..f089c78e0 100644
--- a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs
+++ b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs
@@ -91,6 +91,12 @@ fn memusage_linux() -> MemoryUsage {
pub struct Bytes(isize);
impl Bytes {
+ pub fn new(bytes: isize) -> Bytes {
+ Bytes(bytes)
+ }
+}
+
+impl Bytes {
pub fn megabytes(self) -> isize {
self.0 / 1024 / 1024
}
diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml
index 22d6a6e78..3abff64a8 100644
--- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml
@@ -16,10 +16,12 @@ tracing = "0.1.35"
rustc-hash = "1.1.0"
cargo_metadata = "0.15.0"
semver = "1.0.14"
-serde = { version = "1.0.137", features = ["derive"] }
-serde_json = "1.0.86"
+serde_json.workspace = true
+serde.workspace = true
+triomphe.workspace = true
anyhow = "1.0.62"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
+itertools = "0.10.5"
# local deps
base-db.workspace = true
diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
index 4e5d640f1..6cbf403cb 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
@@ -14,9 +14,10 @@ use std::{
};
use cargo_metadata::{camino::Utf8Path, Message};
+use itertools::Itertools;
use la_arena::ArenaMap;
use paths::{AbsPath, AbsPathBuf};
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
use serde::Deserialize;
@@ -56,7 +57,10 @@ impl BuildScriptOutput {
}
impl WorkspaceBuildScripts {
- fn build_command(config: &CargoConfig) -> io::Result<Command> {
+ fn build_command(
+ config: &CargoConfig,
+ allowed_features: &FxHashSet<String>,
+ ) -> io::Result<Command> {
let mut cmd = match config.run_build_script_command.as_deref() {
Some([program, args @ ..]) => {
let mut cmd = Command::new(program);
@@ -88,7 +92,12 @@ impl WorkspaceBuildScripts {
}
if !features.is_empty() {
cmd.arg("--features");
- cmd.arg(features.join(" "));
+ cmd.arg(
+ features
+ .iter()
+ .filter(|&feat| allowed_features.contains(feat))
+ .join(","),
+ );
}
}
}
@@ -127,13 +136,20 @@ impl WorkspaceBuildScripts {
}
.as_ref();
- match Self::run_per_ws(Self::build_command(config)?, workspace, current_dir, progress) {
+ let allowed_features = workspace.workspace_features();
+
+ match Self::run_per_ws(
+ Self::build_command(config, &allowed_features)?,
+ workspace,
+ current_dir,
+ progress,
+ ) {
Ok(WorkspaceBuildScripts { error: Some(error), .. })
if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) =>
{
// building build scripts failed, attempt to build with --keep-going so
// that we potentially get more build data
- let mut cmd = Self::build_command(config)?;
+ let mut cmd = Self::build_command(config, &allowed_features)?;
cmd.args(["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1");
let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?;
res.error = Some(error);
@@ -161,7 +177,7 @@ impl WorkspaceBuildScripts {
))
}
};
- let cmd = Self::build_command(config)?;
+ let cmd = Self::build_command(config, &Default::default())?;
// NB: Cargo.toml could have been modified between `cargo metadata` and
// `cargo check`. We shouldn't assume that package ids we see here are
// exactly those from `config`.
@@ -415,7 +431,6 @@ impl WorkspaceBuildScripts {
let dir_entry = entry.ok()?;
if dir_entry.file_type().ok()?.is_file() {
let path = dir_entry.path();
- tracing::info!("p{:?}", path);
let extension = path.extension()?;
if extension == std::env::consts::DLL_EXTENSION {
let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned();
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index 01162b1a8..92b454150 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -1,6 +1,5 @@
//! See [`CargoWorkspace`].
-use std::iter;
use std::path::PathBuf;
use std::str::from_utf8;
use std::{ops, process::Command};
@@ -10,7 +9,7 @@ use base_db::Edition;
use cargo_metadata::{CargoOpt, MetadataCommand};
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};
-use rustc_hash::FxHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
use serde::Deserialize;
use serde_json::from_value;
@@ -32,6 +31,7 @@ pub struct CargoWorkspace {
packages: Arena<PackageData>,
targets: Arena<TargetData>,
workspace_root: AbsPathBuf,
+ target_directory: AbsPathBuf,
}
impl ops::Index<Package> for CargoWorkspace {
@@ -57,20 +57,6 @@ pub enum RustLibSource {
Discover,
}
-/// Crates to disable `#[cfg(test)]` on.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum UnsetTestCrates {
- None,
- Only(Vec<String>),
- All,
-}
-
-impl Default for UnsetTestCrates {
- fn default() -> Self {
- Self::None
- }
-}
-
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CargoFeatures {
All,
@@ -99,8 +85,7 @@ pub struct CargoConfig {
pub sysroot_src: Option<AbsPathBuf>,
/// rustc private crate source
pub rustc_source: Option<RustLibSource>,
- /// crates to disable `#[cfg(test)]` on
- pub unset_test_crates: UnsetTestCrates,
+ pub cfg_overrides: CfgOverrides,
/// Invoke `cargo check` through the RUSTC_WRAPPER.
pub wrap_rustc_in_build_scripts: bool,
/// The command to run instead of `cargo check` for building build scripts.
@@ -113,27 +98,6 @@ pub struct CargoConfig {
pub invocation_location: InvocationLocation,
}
-impl CargoConfig {
- pub fn cfg_overrides(&self) -> CfgOverrides {
- match &self.unset_test_crates {
- UnsetTestCrates::None => CfgOverrides::Selective(iter::empty().collect()),
- UnsetTestCrates::Only(unset_test_crates) => CfgOverrides::Selective(
- unset_test_crates
- .iter()
- .cloned()
- .zip(iter::repeat_with(|| {
- cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())])
- .unwrap()
- }))
- .collect(),
- ),
- UnsetTestCrates::All => CfgOverrides::Wildcard(
- cfg::CfgDiff::new(Vec::new(), vec![cfg::CfgAtom::Flag("test".into())]).unwrap(),
- ),
- }
- }
-}
-
pub type Package = Idx<PackageData>;
pub type Target = Idx<TargetData>;
@@ -293,13 +257,29 @@ impl CargoWorkspace {
}
meta.current_dir(current_dir.as_os_str());
+ let mut other_options = vec![];
+ // cargo metadata only supports a subset of flags of what cargo usually accepts, and usually
+ // the only relevant flags for metadata here are unstable ones, so we pass those along
+ // but nothing else
+ let mut extra_args = config.extra_args.iter();
+ while let Some(arg) = extra_args.next() {
+ if arg == "-Z" {
+ if let Some(arg) = extra_args.next() {
+ other_options.push("-Z".to_owned());
+ other_options.push(arg.to_owned());
+ }
+ }
+ }
+
if !targets.is_empty() {
- let other_options: Vec<_> = targets
- .into_iter()
- .flat_map(|target| ["--filter-platform".to_string(), target])
- .collect();
- meta.other_options(other_options);
+ other_options.append(
+ &mut targets
+ .into_iter()
+ .flat_map(|target| ["--filter-platform".to_owned().to_string(), target])
+ .collect(),
+ );
}
+ meta.other_options(other_options);
// FIXME: Fetching metadata is a slow process, as it might require
// calling crates.io. We should be reporting progress here, but it's
@@ -411,7 +391,10 @@ impl CargoWorkspace {
let workspace_root =
AbsPathBuf::assert(PathBuf::from(meta.workspace_root.into_os_string()));
- CargoWorkspace { packages, targets, workspace_root }
+ let target_directory =
+ AbsPathBuf::assert(PathBuf::from(meta.target_directory.into_os_string()));
+
+ CargoWorkspace { packages, targets, workspace_root, target_directory }
}
pub fn packages(&self) -> impl Iterator<Item = Package> + ExactSizeIterator + '_ {
@@ -429,6 +412,10 @@ impl CargoWorkspace {
&self.workspace_root
}
+ pub fn target_directory(&self) -> &AbsPath {
+ &self.target_directory
+ }
+
pub fn package_flag(&self, package: &PackageData) -> String {
if self.is_unique(&package.name) {
package.name.clone()
@@ -467,6 +454,21 @@ impl CargoWorkspace {
None
}
+ /// Returns the union of the features of all member crates in this workspace.
+ pub fn workspace_features(&self) -> FxHashSet<String> {
+ self.packages()
+ .filter_map(|package| {
+ let package = &self[package];
+ if package.is_member {
+ Some(package.features.keys().cloned())
+ } else {
+ None
+ }
+ })
+ .flatten()
+ .collect()
+ }
+
fn is_unique(&self, name: &str) -> bool {
self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs b/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs
index c134b78ab..e366d441c 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cfg_flag.rs
@@ -49,6 +49,14 @@ impl Extend<CfgFlag> for CfgOptions {
}
}
+impl FromIterator<CfgFlag> for CfgOptions {
+ fn from_iter<T: IntoIterator<Item = CfgFlag>>(iter: T) -> Self {
+ let mut this = CfgOptions::default();
+ this.extend(iter);
+ this
+ }
+}
+
impl fmt::Display for CfgFlag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
index 70cb71ae3..61acc646f 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
@@ -44,7 +44,7 @@ pub use crate::{
build_scripts::WorkspaceBuildScripts,
cargo_workspace::{
CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency,
- RustLibSource, Target, TargetData, TargetKind, UnsetTestCrates,
+ RustLibSource, Target, TargetData, TargetKind,
},
manifest_path::ManifestPath,
project_json::{ProjectJson, ProjectJsonData},
diff --git a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs
index 980d92d3d..3f60e4dd9 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/manifest_path.rs
@@ -34,6 +34,10 @@ impl ManifestPath {
pub fn parent(&self) -> &AbsPath {
self.file.parent().unwrap()
}
+
+ pub fn canonicalize(&self) -> ! {
+ (&**self).canonicalize()
+ }
}
impl ops::Deref for ManifestPath {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
index 4b2448e47..80897f747 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
@@ -49,12 +49,12 @@
//! user explores them belongs to that extension (it's totally valid to change
//! rust-project.json over time via configuration request!)
-use std::path::PathBuf;
-
use base_db::{CrateDisplayName, CrateId, CrateName, Dependency, Edition};
+use la_arena::RawIdx;
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
use serde::{de, Deserialize};
+use std::path::PathBuf;
use crate::cfg_flag::CfgFlag;
@@ -98,26 +98,23 @@ impl ProjectJson {
/// * `data` - The parsed contents of `rust-project.json`, or project json that's passed via
/// configuration.
pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson {
+ let absolutize_on_base = |p| base.absolutize(p);
ProjectJson {
- sysroot: data.sysroot.map(|it| base.join(it)),
- sysroot_src: data.sysroot_src.map(|it| base.join(it)),
+ sysroot: data.sysroot.map(absolutize_on_base),
+ sysroot_src: data.sysroot_src.map(absolutize_on_base),
project_root: base.to_path_buf(),
crates: data
.crates
.into_iter()
.map(|crate_data| {
- let is_workspace_member = crate_data.is_workspace_member.unwrap_or_else(|| {
- crate_data.root_module.is_relative()
- && !crate_data.root_module.starts_with("..")
- || crate_data.root_module.starts_with(base)
- });
- let root_module = base.join(crate_data.root_module).normalize();
+ let root_module = absolutize_on_base(crate_data.root_module);
+ let is_workspace_member = crate_data
+ .is_workspace_member
+ .unwrap_or_else(|| root_module.starts_with(base));
let (include, exclude) = match crate_data.source {
Some(src) => {
let absolutize = |dirs: Vec<PathBuf>| {
- dirs.into_iter()
- .map(|it| base.join(it).normalize())
- .collect::<Vec<_>>()
+ dirs.into_iter().map(absolutize_on_base).collect::<Vec<_>>()
};
(absolutize(src.include_dirs), absolutize(src.exclude_dirs))
}
@@ -135,7 +132,10 @@ impl ProjectJson {
.deps
.into_iter()
.map(|dep_data| {
- Dependency::new(dep_data.name, CrateId(dep_data.krate as u32))
+ Dependency::new(
+ dep_data.name,
+ CrateId::from_raw(RawIdx::from(dep_data.krate as u32)),
+ )
})
.collect::<Vec<_>>(),
cfg: crate_data.cfg,
@@ -143,7 +143,7 @@ impl ProjectJson {
env: crate_data.env,
proc_macro_dylib_path: crate_data
.proc_macro_dylib_path
- .map(|it| base.join(it)),
+ .map(absolutize_on_base),
is_workspace_member,
include,
exclude,
@@ -151,7 +151,7 @@ impl ProjectJson {
repository: crate_data.repository,
}
})
- .collect::<Vec<_>>(),
+ .collect(),
}
}
@@ -162,7 +162,10 @@ impl ProjectJson {
/// Returns an iterator over the crates in the project.
pub fn crates(&self) -> impl Iterator<Item = (CrateId, &Crate)> + '_ {
- self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate))
+ self.crates
+ .iter()
+ .enumerate()
+ .map(|(idx, krate)| (CrateId::from_raw(RawIdx::from(idx as u32)), krate))
}
/// Returns the path to the project's root folder.
@@ -236,7 +239,7 @@ struct CrateSource {
exclude_dirs: Vec<PathBuf>,
}
-fn deserialize_crate_name<'de, D>(de: D) -> Result<CrateName, D::Error>
+fn deserialize_crate_name<'de, D>(de: D) -> std::result::Result<CrateName, D::Error>
where
D: de::Deserializer<'de>,
{
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index 74e41eda7..e3a2de927 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -12,13 +12,15 @@ use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
-use crate::{utf8_stdout, ManifestPath};
+use crate::{utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath};
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Sysroot {
root: AbsPathBuf,
src_root: AbsPathBuf,
crates: Arena<SysrootCrateData>,
+ /// Stores the result of `cargo metadata` of the `RA_UNSTABLE_SYSROOT_HACK` workspace.
+ pub hack_cargo_workspace: Option<CargoWorkspace>,
}
pub(crate) type SysrootCrate = Idx<SysrootCrateData>;
@@ -74,6 +76,23 @@ impl Sysroot {
pub fn is_empty(&self) -> bool {
self.crates.is_empty()
}
+
+ pub fn loading_warning(&self) -> Option<String> {
+ if self.by_name("core").is_none() {
+ let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
+ " (`RUST_SRC_PATH` might be incorrect, try unsetting it)"
+ } else {
+ " try running `rustup component add rust-src` to possible fix this"
+ };
+ Some(format!(
+ "could not find libcore in loaded sysroot at `{}`{}",
+ self.src_root.as_path().display(),
+ var_note,
+ ))
+ } else {
+ None
+ }
+ }
}
// FIXME: Expose a builder api as loading the sysroot got way too modular and complicated.
@@ -103,14 +122,36 @@ impl Sysroot {
pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result<Sysroot> {
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| {
- format_err!("can't load standard library from sysroot {}", sysroot_dir.display())
+ format_err!("can't load standard library from sysroot path {}", sysroot_dir.display())
})?;
Ok(Sysroot::load(sysroot_dir, sysroot_src_dir))
}
- pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Sysroot {
- let mut sysroot =
- Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() };
+ pub fn load(sysroot_dir: AbsPathBuf, mut sysroot_src_dir: AbsPathBuf) -> Sysroot {
+ // FIXME: Remove this `hack_cargo_workspace` field completely once we support sysroot dependencies
+ let hack_cargo_workspace = if let Ok(path) = std::env::var("RA_UNSTABLE_SYSROOT_HACK") {
+ let cargo_toml = ManifestPath::try_from(
+ AbsPathBuf::try_from(&*format!("{path}/Cargo.toml")).unwrap(),
+ )
+ .unwrap();
+ sysroot_src_dir = AbsPathBuf::try_from(&*path).unwrap().join("library");
+ CargoWorkspace::fetch_metadata(
+ &cargo_toml,
+ &AbsPathBuf::try_from("/").unwrap(),
+ &CargoConfig::default(),
+ &|_| (),
+ )
+ .map(CargoWorkspace::new)
+ .ok()
+ } else {
+ None
+ };
+ let mut sysroot = Sysroot {
+ root: sysroot_dir,
+ src_root: sysroot_src_dir,
+ crates: Arena::default(),
+ hack_cargo_workspace,
+ };
for path in SYSROOT_CRATES.trim().lines() {
let name = path.split('/').last().unwrap();
@@ -153,19 +194,6 @@ impl Sysroot {
}
}
- if sysroot.by_name("core").is_none() {
- let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
- " (`RUST_SRC_PATH` might be incorrect, try unsetting it)"
- } else {
- ""
- };
- tracing::error!(
- "could not find libcore in sysroot path `{}`{}",
- sysroot.src_root.as_path().display(),
- var_note,
- );
- }
-
sysroot
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs
index 42c06ad0e..30ca7b348 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/target_data_layout.rs
@@ -16,7 +16,7 @@ pub fn get(
let mut cmd = Command::new(toolchain::rustc());
cmd.envs(extra_env);
cmd.current_dir(cargo_toml.parent())
- .args(["-Z", "unstable-options", "rustc", "--print", "target-spec-json"])
+ .args(["-Z", "unstable-options", "--print", "target-spec-json"])
.env("RUSTC_BOOTSTRAP", "1");
if let Some(target) = target {
cmd.args(["--target", target]);
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index 3754accbb..7815b9dda 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -3,10 +3,11 @@ use std::{
path::{Path, PathBuf},
};
-use base_db::{CrateGraph, FileId};
+use base_db::{CrateGraph, FileId, ProcMacroPaths};
use cfg::{CfgAtom, CfgDiff};
-use expect_test::{expect, Expect};
+use expect_test::{expect_file, ExpectFile};
use paths::{AbsPath, AbsPathBuf};
+use rustc_hash::FxHashMap;
use serde::de::DeserializeOwned;
use crate::{
@@ -14,11 +15,14 @@ use crate::{
WorkspaceBuildScripts,
};
-fn load_cargo(file: &str) -> CrateGraph {
+fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) {
load_cargo_with_overrides(file, CfgOverrides::default())
}
-fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGraph {
+fn load_cargo_with_overrides(
+ file: &str,
+ cfg_overrides: CfgOverrides,
+) -> (CrateGraph, ProcMacroPaths) {
let meta = get_test_json_file(file);
let cargo_workspace = CargoWorkspace::new(meta);
let project_workspace = ProjectWorkspace::Cargo {
@@ -34,11 +38,39 @@ fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGr
to_crate_graph(project_workspace)
}
-fn load_rust_project(file: &str) -> CrateGraph {
+fn load_cargo_with_sysroot(
+ file_map: &mut FxHashMap<AbsPathBuf, FileId>,
+ file: &str,
+) -> (CrateGraph, ProcMacroPaths) {
+ let meta = get_test_json_file(file);
+ let cargo_workspace = CargoWorkspace::new(meta);
+ let project_workspace = ProjectWorkspace::Cargo {
+ cargo: cargo_workspace,
+ build_scripts: WorkspaceBuildScripts::default(),
+ sysroot: Ok(get_fake_sysroot()),
+ rustc: Err(None),
+ rustc_cfg: Vec::new(),
+ cfg_overrides: Default::default(),
+ toolchain: None,
+ target_layout: Err("target_data_layout not loaded".into()),
+ };
+ project_workspace.to_crate_graph(
+ &mut {
+ |path| {
+ let len = file_map.len();
+ Some(*file_map.entry(path.to_path_buf()).or_insert(FileId(len as u32)))
+ }
+ },
+ &Default::default(),
+ )
+}
+
+fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) {
let data = get_test_json_file(file);
let project = rooted_project_json(data);
let sysroot = Ok(get_fake_sysroot());
- let project_workspace = ProjectWorkspace::Json { project, sysroot, rustc_cfg: Vec::new() };
+ let project_workspace =
+ ProjectWorkspace::Json { project, sysroot, rustc_cfg: Vec::new(), toolchain: None };
to_crate_graph(project_workspace)
}
@@ -70,6 +102,18 @@ fn replace_root(s: &mut String, direction: bool) {
}
}
+fn replace_fake_sys_root(s: &mut String) {
+ let fake_sysroot_path = get_test_path("fake-sysroot");
+ let fake_sysroot_path = if cfg!(windows) {
+ let normalized_path =
+ fake_sysroot_path.to_str().expect("expected str").replace(r#"\"#, r#"\\"#);
+ format!(r#"{}\\"#, normalized_path)
+ } else {
+ format!("{}/", fake_sysroot_path.to_str().expect("expected str"))
+ };
+ *s = s.replace(&fake_sysroot_path, "$FAKESYSROOT$")
+}
+
fn get_test_path(file: &str) -> PathBuf {
let base = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
base.join("test_data").join(file)
@@ -92,9 +136,8 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
ProjectJson::new(base, data)
}
-fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph {
+fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacroPaths) {
project_workspace.to_crate_graph(
- &mut |_, _| Ok(Vec::new()),
&mut {
let mut counter = 0;
move |_path| {
@@ -106,1808 +149,70 @@ fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph {
)
}
-fn check_crate_graph(crate_graph: CrateGraph, expect: Expect) {
+fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) {
let mut crate_graph = format!("{crate_graph:#?}");
replace_root(&mut crate_graph, false);
+ replace_fake_sys_root(&mut crate_graph);
expect.assert_eq(&crate_graph);
}
#[test]
fn cargo_hello_world_project_model_with_wildcard_overrides() {
- let cfg_overrides = CfgOverrides::Wildcard(
- CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(),
- );
- let crate_graph = load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides);
+ let cfg_overrides = CfgOverrides {
+ global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(),
+ selective: Default::default(),
+ };
+ let (crate_graph, _proc_macros) =
+ load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides);
check_crate_graph(
crate_graph,
- expect![[r#"
- CrateGraph {
- arena: {
- CrateId(
- 0,
- ): CrateData {
- root_file_id: FileId(
- 1,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello-world",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 1,
- ): CrateData {
- root_file_id: FileId(
- 2,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello-world",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 2,
- ): CrateData {
- root_file_id: FileId(
- 3,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "an_example",
- ),
- canonical_name: "an-example",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 3,
- ): CrateData {
- root_file_id: FileId(
- 4,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "it",
- ),
- canonical_name: "it",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 4,
- ): CrateData {
- root_file_id: FileId(
- 5,
- ),
- edition: Edition2015,
- version: Some(
- "0.2.98",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "libc",
- ),
- canonical_name: "libc",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=default",
- "feature=std",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=align",
- "feature=const-extern-fn",
- "feature=default",
- "feature=extra_traits",
- "feature=rustc-dep-of-std",
- "feature=std",
- "feature=use_std",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
- "CARGO_PKG_VERSION": "0.2.98",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "libc",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "libc",
- "CARGO_PKG_VERSION_PATCH": "98",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "2",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: Some(
- "https://github.com/rust-lang/libc",
- ),
- name: Some(
- "libc",
- ),
- },
- is_proc_macro: false,
- },
- },
- }"#]],
+ expect_file![
+ "../test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt"
+ ],
)
}
#[test]
fn cargo_hello_world_project_model_with_selective_overrides() {
- let cfg_overrides = {
- CfgOverrides::Selective(
- std::iter::once((
- "libc".to_owned(),
- CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(),
- ))
- .collect(),
- )
+ let cfg_overrides = CfgOverrides {
+ global: Default::default(),
+ selective: std::iter::once((
+ "libc".to_owned(),
+ CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(),
+ ))
+ .collect(),
};
- let crate_graph = load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides);
+ let (crate_graph, _proc_macros) =
+ load_cargo_with_overrides("hello-world-metadata.json", cfg_overrides);
check_crate_graph(
crate_graph,
- expect![[r#"
- CrateGraph {
- arena: {
- CrateId(
- 0,
- ): CrateData {
- root_file_id: FileId(
- 1,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello-world",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 1,
- ): CrateData {
- root_file_id: FileId(
- 2,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello-world",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 2,
- ): CrateData {
- root_file_id: FileId(
- 3,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "an_example",
- ),
- canonical_name: "an-example",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 3,
- ): CrateData {
- root_file_id: FileId(
- 4,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "it",
- ),
- canonical_name: "it",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 4,
- ): CrateData {
- root_file_id: FileId(
- 5,
- ),
- edition: Edition2015,
- version: Some(
- "0.2.98",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "libc",
- ),
- canonical_name: "libc",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=default",
- "feature=std",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=align",
- "feature=const-extern-fn",
- "feature=default",
- "feature=extra_traits",
- "feature=rustc-dep-of-std",
- "feature=std",
- "feature=use_std",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
- "CARGO_PKG_VERSION": "0.2.98",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "libc",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "libc",
- "CARGO_PKG_VERSION_PATCH": "98",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "2",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: Some(
- "https://github.com/rust-lang/libc",
- ),
- name: Some(
- "libc",
- ),
- },
- is_proc_macro: false,
- },
- },
- }"#]],
+ expect_file![
+ "../test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt"
+ ],
)
}
#[test]
fn cargo_hello_world_project_model() {
- let crate_graph = load_cargo("hello-world-metadata.json");
+ let (crate_graph, _proc_macros) = load_cargo("hello-world-metadata.json");
check_crate_graph(
crate_graph,
- expect![[r#"
- CrateGraph {
- arena: {
- CrateId(
- 0,
- ): CrateData {
- root_file_id: FileId(
- 1,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello-world",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 1,
- ): CrateData {
- root_file_id: FileId(
- 2,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello-world",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 2,
- ): CrateData {
- root_file_id: FileId(
- 3,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "an_example",
- ),
- canonical_name: "an-example",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 3,
- ): CrateData {
- root_file_id: FileId(
- 4,
- ),
- edition: Edition2018,
- version: Some(
- "0.1.0",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "it",
- ),
- canonical_name: "it",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "test",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
- "CARGO_PKG_VERSION": "0.1.0",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "hello-world",
- "CARGO_PKG_VERSION_PATCH": "0",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "1",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "hello_world",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 4,
- ),
- name: CrateName(
- "libc",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello-world",
- ),
- },
- is_proc_macro: false,
- },
- CrateId(
- 4,
- ): CrateData {
- root_file_id: FileId(
- 5,
- ),
- edition: Edition2015,
- version: Some(
- "0.2.98",
- ),
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "libc",
- ),
- canonical_name: "libc",
- },
- ),
- cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=default",
- "feature=std",
- ],
- ),
- potential_cfg_options: CfgOptions(
- [
- "debug_assertions",
- "feature=align",
- "feature=const-extern-fn",
- "feature=default",
- "feature=extra_traits",
- "feature=rustc-dep-of-std",
- "feature=std",
- "feature=use_std",
- ],
- ),
- target_layout: Err(
- "target_data_layout not loaded",
- ),
- env: Env {
- entries: {
- "CARGO_PKG_LICENSE": "",
- "CARGO_PKG_VERSION_MAJOR": "0",
- "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
- "CARGO_PKG_VERSION": "0.2.98",
- "CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "libc",
- "CARGO_PKG_LICENSE_FILE": "",
- "CARGO_PKG_HOMEPAGE": "",
- "CARGO_PKG_DESCRIPTION": "",
- "CARGO_PKG_NAME": "libc",
- "CARGO_PKG_VERSION_PATCH": "98",
- "CARGO": "cargo",
- "CARGO_PKG_REPOSITORY": "",
- "CARGO_PKG_VERSION_MINOR": "2",
- "CARGO_PKG_VERSION_PRE": "",
- },
- },
- dependencies: [],
- proc_macro: Err(
- "crate has not (yet) been built",
- ),
- origin: CratesIo {
- repo: Some(
- "https://github.com/rust-lang/libc",
- ),
- name: Some(
- "libc",
- ),
- },
- is_proc_macro: false,
- },
- },
- }"#]],
+ expect_file!["../test_data/output/cargo_hello_world_project_model.txt"],
)
}
#[test]
fn rust_project_hello_world_project_model() {
- let crate_graph = load_rust_project("hello-world-project.json");
+ let (crate_graph, _proc_macros) = load_rust_project("hello-world-project.json");
check_crate_graph(
crate_graph,
- expect![[r#"
- CrateGraph {
- arena: {
- CrateId(
- 0,
- ): CrateData {
- root_file_id: FileId(
- 1,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "alloc",
- ),
- canonical_name: "alloc",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 1,
- ),
- name: CrateName(
- "core",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Alloc,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 1,
- ): CrateData {
- root_file_id: FileId(
- 2,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "core",
- ),
- canonical_name: "core",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Core,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 2,
- ): CrateData {
- root_file_id: FileId(
- 3,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "panic_abort",
- ),
- canonical_name: "panic_abort",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 3,
- ): CrateData {
- root_file_id: FileId(
- 4,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "panic_unwind",
- ),
- canonical_name: "panic_unwind",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 4,
- ): CrateData {
- root_file_id: FileId(
- 5,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "proc_macro",
- ),
- canonical_name: "proc_macro",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 6,
- ),
- name: CrateName(
- "std",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 1,
- ),
- name: CrateName(
- "core",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 5,
- ): CrateData {
- root_file_id: FileId(
- 6,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "profiler_builtins",
- ),
- canonical_name: "profiler_builtins",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 6,
- ): CrateData {
- root_file_id: FileId(
- 7,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "std",
- ),
- canonical_name: "std",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "alloc",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 3,
- ),
- name: CrateName(
- "panic_unwind",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 2,
- ),
- name: CrateName(
- "panic_abort",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 1,
- ),
- name: CrateName(
- "core",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 5,
- ),
- name: CrateName(
- "profiler_builtins",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 9,
- ),
- name: CrateName(
- "unwind",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 7,
- ),
- name: CrateName(
- "std_detect",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 8,
- ),
- name: CrateName(
- "test",
- ),
- prelude: true,
- },
- ],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Std,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 7,
- ): CrateData {
- root_file_id: FileId(
- 8,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "std_detect",
- ),
- canonical_name: "std_detect",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 8,
- ): CrateData {
- root_file_id: FileId(
- 9,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "test",
- ),
- canonical_name: "test",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Test,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 9,
- ): CrateData {
- root_file_id: FileId(
- 10,
- ),
- edition: Edition2021,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "unwind",
- ),
- canonical_name: "unwind",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [],
- proc_macro: Err(
- "no proc macro loaded for sysroot crate",
- ),
- origin: Lang(
- Other,
- ),
- is_proc_macro: false,
- },
- CrateId(
- 10,
- ): CrateData {
- root_file_id: FileId(
- 11,
- ),
- edition: Edition2018,
- version: None,
- display_name: Some(
- CrateDisplayName {
- crate_name: CrateName(
- "hello_world",
- ),
- canonical_name: "hello_world",
- },
- ),
- cfg_options: CfgOptions(
- [],
- ),
- potential_cfg_options: CfgOptions(
- [],
- ),
- target_layout: Err(
- "rust-project.json projects have no target layout set",
- ),
- env: Env {
- entries: {},
- },
- dependencies: [
- Dependency {
- crate_id: CrateId(
- 1,
- ),
- name: CrateName(
- "core",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 0,
- ),
- name: CrateName(
- "alloc",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 6,
- ),
- name: CrateName(
- "std",
- ),
- prelude: true,
- },
- Dependency {
- crate_id: CrateId(
- 8,
- ),
- name: CrateName(
- "test",
- ),
- prelude: false,
- },
- ],
- proc_macro: Err(
- "no proc macro dylib present",
- ),
- origin: CratesIo {
- repo: None,
- name: Some(
- "hello_world",
- ),
- },
- is_proc_macro: false,
- },
- },
- }"#]],
+ expect_file!["../test_data/output/rust_project_hello_world_project_model.txt"],
);
}
#[test]
fn rust_project_is_proc_macro_has_proc_macro_dep() {
- let crate_graph = load_rust_project("is-proc-macro-project.json");
+ let (crate_graph, _proc_macros) = load_rust_project("is-proc-macro-project.json");
// Since the project only defines one crate (outside the sysroot crates),
// it should be the one with the biggest Id.
let crate_id = crate_graph.iter().max().unwrap();
@@ -1916,3 +221,31 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() {
// on the proc_macro sysroot crate.
crate_data.dependencies.iter().find(|&dep| dep.name.deref() == "proc_macro").unwrap();
}
+
+#[test]
+fn crate_graph_dedup_identical() {
+ let (mut crate_graph, proc_macros) =
+ load_cargo_with_sysroot(&mut Default::default(), "regex-metadata.json");
+ crate_graph.sort_deps();
+
+ let (d_crate_graph, mut d_proc_macros) = (crate_graph.clone(), proc_macros.clone());
+
+ crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros);
+ assert!(crate_graph.iter().eq(d_crate_graph.iter()));
+ assert_eq!(proc_macros, d_proc_macros);
+}
+
+#[test]
+fn crate_graph_dedup() {
+ let path_map = &mut Default::default();
+ let (mut crate_graph, _proc_macros) =
+ load_cargo_with_sysroot(path_map, "ripgrep-metadata.json");
+ assert_eq!(crate_graph.iter().count(), 81);
+ crate_graph.sort_deps();
+ let (regex_crate_graph, mut regex_proc_macros) =
+ load_cargo_with_sysroot(path_map, "regex-metadata.json");
+ assert_eq!(regex_crate_graph.iter().count(), 60);
+
+ crate_graph.extend(regex_crate_graph, &mut regex_proc_macros);
+ assert_eq!(crate_graph.iter().count(), 118);
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index d1e53e12e..b5fe237fc 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -2,53 +2,43 @@
//! metadata` or `rust-project.json`) into representation stored in the salsa
//! database -- `CrateGraph`.
-use std::{collections::VecDeque, fmt, fs, process::Command, sync::Arc};
+use std::{collections::VecDeque, fmt, fs, process::Command, sync};
use anyhow::{format_err, Context, Result};
use base_db::{
CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Edition, Env,
- FileId, LangCrateOrigin, ProcMacroLoadResult, TargetLayoutLoadResult,
+ FileId, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, TargetLayoutLoadResult,
};
use cfg::{CfgDiff, CfgOptions};
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
-use stdx::{always, hash::NoHashHashMap};
+use stdx::always;
+use triomphe::Arc;
use crate::{
build_scripts::BuildScriptOutput,
cargo_workspace::{DepKind, PackageData, RustLibSource},
cfg_flag::CfgFlag,
+ project_json::Crate,
rustc_cfg,
sysroot::SysrootCrate,
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
- Package, ProjectJson, ProjectManifest, Sysroot, TargetKind, WorkspaceBuildScripts,
+ Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
};
/// A set of cfg-overrides per crate.
-///
-/// `Wildcard(..)` is useful e.g. disabling `#[cfg(test)]` on all crates,
-/// without having to first obtain a list of all crates.
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub enum CfgOverrides {
- /// A single global set of overrides matching all crates.
- Wildcard(CfgDiff),
+#[derive(Default, Debug, Clone, Eq, PartialEq)]
+pub struct CfgOverrides {
+ /// A global set of overrides matching all crates.
+ pub global: CfgDiff,
/// A set of overrides matching specific crates.
- Selective(FxHashMap<String, CfgDiff>),
-}
-
-impl Default for CfgOverrides {
- fn default() -> Self {
- Self::Selective(FxHashMap::default())
- }
+ pub selective: FxHashMap<String, CfgDiff>,
}
impl CfgOverrides {
pub fn len(&self) -> usize {
- match self {
- CfgOverrides::Wildcard(_) => 1,
- CfgOverrides::Selective(hash_map) => hash_map.len(),
- }
+ self.global.len() + self.selective.iter().map(|(_, it)| it.len()).sum::<usize>()
}
}
@@ -82,7 +72,14 @@ pub enum ProjectWorkspace {
target_layout: Result<String, String>,
},
/// Project workspace was manually specified using a `rust-project.json` file.
- Json { project: ProjectJson, sysroot: Result<Sysroot, Option<String>>, rustc_cfg: Vec<CfgFlag> },
+ Json {
+ project: ProjectJson,
+ sysroot: Result<Sysroot, Option<String>>,
+ /// Holds cfg flags for the current target. We get those by running
+ /// `rustc --print cfg`.
+ rustc_cfg: Vec<CfgFlag>,
+ toolchain: Option<Version>,
+ },
// FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
// That's not the end user experience we should strive for.
// Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
@@ -96,6 +93,8 @@ pub enum ProjectWorkspace {
DetachedFiles {
files: Vec<AbsPathBuf>,
sysroot: Result<Sysroot, Option<String>>,
+ /// Holds cfg flags for the current target. We get those by running
+ /// `rustc --print cfg`.
rustc_cfg: Vec<CfgFlag>,
},
}
@@ -127,12 +126,13 @@ impl fmt::Debug for ProjectWorkspace {
.field("toolchain", &toolchain)
.field("data_layout", &data_layout)
.finish(),
- ProjectWorkspace::Json { project, sysroot, rustc_cfg } => {
+ ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain } => {
let mut debug_struct = f.debug_struct("Json");
debug_struct.field("n_crates", &project.n_crates());
if let Ok(sysroot) = sysroot {
debug_struct.field("n_sysroot_crates", &sysroot.crates().len());
}
+ debug_struct.field("toolchain", &toolchain);
debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
debug_struct.finish()
}
@@ -152,6 +152,19 @@ impl ProjectWorkspace {
config: &CargoConfig,
progress: &dyn Fn(String),
) -> Result<ProjectWorkspace> {
+ let version = |current_dir, cmd_path, prefix: &str| {
+ let cargo_version = utf8_stdout({
+ let mut cmd = Command::new(cmd_path);
+ cmd.envs(&config.extra_env);
+ cmd.arg("--version").current_dir(current_dir);
+ cmd
+ })?;
+ anyhow::Ok(
+ cargo_version
+ .get(prefix.len()..)
+ .and_then(|it| Version::parse(it.split_whitespace().next()?).ok()),
+ )
+ };
let res = match manifest {
ProjectManifest::ProjectJson(project_json) => {
let file = fs::read_to_string(&project_json).with_context(|| {
@@ -161,24 +174,17 @@ impl ProjectWorkspace {
format!("Failed to deserialize json file {}", project_json.display())
})?;
let project_location = project_json.parent().to_path_buf();
+ let toolchain = version(&*project_location, toolchain::rustc(), "rustc ")?;
let project_json = ProjectJson::new(&project_location, data);
ProjectWorkspace::load_inline(
project_json,
config.target.as_deref(),
&config.extra_env,
+ toolchain,
)
}
ProjectManifest::CargoToml(cargo_toml) => {
- let cargo_version = utf8_stdout({
- let mut cmd = Command::new(toolchain::cargo());
- cmd.envs(&config.extra_env);
- cmd.arg("--version");
- cmd
- })?;
- let toolchain = cargo_version
- .get("cargo ".len()..)
- .and_then(|it| Version::parse(it.split_whitespace().next()?).ok());
-
+ let toolchain = version(cargo_toml.parent(), toolchain::cargo(), "cargo ")?;
let meta = CargoWorkspace::fetch_metadata(
&cargo_toml,
cargo_toml.parent(),
@@ -274,7 +280,7 @@ impl ProjectWorkspace {
let rustc_cfg =
rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env);
- let cfg_overrides = config.cfg_overrides();
+ let cfg_overrides = config.cfg_overrides.clone();
let data_layout = target_data_layout::get(
Some(&cargo_toml),
config.target.as_deref(),
@@ -303,6 +309,7 @@ impl ProjectWorkspace {
project_json: ProjectJson,
target: Option<&str>,
extra_env: &FxHashMap<String, String>,
+ toolchain: Option<Version>,
) -> ProjectWorkspace {
let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
(Some(sysroot), Some(sysroot_src)) => Ok(Sysroot::load(sysroot, sysroot_src)),
@@ -327,7 +334,7 @@ impl ProjectWorkspace {
}
let rustc_cfg = rustc_cfg::get(None, target, extra_env);
- ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }
+ ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg, toolchain }
}
pub fn load_detached_files(
@@ -403,7 +410,7 @@ impl ProjectWorkspace {
let outputs = &mut match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress) {
Ok(it) => Ok(it.into_iter()),
// io::Error is not Clone?
- Err(e) => Err(Arc::new(e)),
+ Err(e) => Err(sync::Arc::new(e)),
};
workspaces
@@ -440,18 +447,35 @@ impl ProjectWorkspace {
}
}
- pub fn find_sysroot_proc_macro_srv(&self) -> Option<AbsPathBuf> {
+ pub fn find_sysroot_proc_macro_srv(&self) -> Result<AbsPathBuf> {
match self {
ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. }
- | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. } => {
+ | ProjectWorkspace::Json { sysroot: Ok(sysroot), .. }
+ | ProjectWorkspace::DetachedFiles { sysroot: Ok(sysroot), .. } => {
let standalone_server_name =
format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
["libexec", "lib"]
.into_iter()
.map(|segment| sysroot.root().join(segment).join(&standalone_server_name))
.find(|server_path| std::fs::metadata(server_path).is_ok())
+ .ok_or_else(|| {
+ anyhow::anyhow!(
+ "cannot find proc-macro server in sysroot `{}`",
+ sysroot.root().display()
+ )
+ })
+ }
+ ProjectWorkspace::DetachedFiles { .. } => {
+ Err(anyhow::anyhow!("cannot find proc-macro server, no sysroot was found"))
}
- _ => None,
+ ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::anyhow!(
+ "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot",
+ cargo.workspace_root().display()
+ )),
+ ProjectWorkspace::Json { project, .. } => Err(anyhow::anyhow!(
+ "cannot find proc-macro-srv, the workspace `{}` is missing a sysroot",
+ project.path().display()
+ )),
}
}
@@ -469,7 +493,7 @@ impl ProjectWorkspace {
})
};
match self {
- ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
+ ProjectWorkspace::Json { project, sysroot, rustc_cfg: _, toolchain: _ } => project
.crates()
.map(|(_, krate)| PackageRoot {
is_local: krate.is_workspace_member,
@@ -570,22 +594,23 @@ impl ProjectWorkspace {
pub fn to_crate_graph(
&self,
- load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
extra_env: &FxHashMap<String, String>,
- ) -> CrateGraph {
+ ) -> (CrateGraph, ProcMacroPaths) {
let _p = profile::span("ProjectWorkspace::to_crate_graph");
- let mut crate_graph = match self {
- ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
- rustc_cfg.clone(),
- load_proc_macro,
- load,
- project,
- sysroot.as_ref().ok(),
- extra_env,
- Err("rust-project.json projects have no target layout set".into()),
- ),
+ let (mut crate_graph, proc_macros) = match self {
+ ProjectWorkspace::Json { project, sysroot, rustc_cfg, toolchain } => {
+ project_json_to_crate_graph(
+ rustc_cfg.clone(),
+ load,
+ project,
+ sysroot.as_ref().ok(),
+ extra_env,
+ Err("rust-project.json projects have no target layout set".into()),
+ toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())),
+ )
+ }
ProjectWorkspace::Cargo {
cargo,
sysroot,
@@ -593,21 +618,22 @@ impl ProjectWorkspace {
rustc_cfg,
cfg_overrides,
build_scripts,
- toolchain: _,
+ toolchain,
target_layout,
} => cargo_to_crate_graph(
- load_proc_macro,
load,
rustc.as_ref().ok(),
cargo,
sysroot.as_ref().ok(),
rustc_cfg.clone(),
cfg_overrides,
+ None,
build_scripts,
match target_layout.as_ref() {
Ok(it) => Ok(Arc::from(it.as_str())),
Err(it) => Err(Arc::from(it.as_str())),
},
+ toolchain.as_ref().and_then(|it| ReleaseChannel::from_str(it.pre.as_str())),
),
ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
detached_files_to_crate_graph(
@@ -624,7 +650,7 @@ impl ProjectWorkspace {
} else {
tracing::debug!("Did not patch std to depend on cfg-if")
}
- crate_graph
+ (crate_graph, proc_macros)
}
pub fn eq_ignore_build_data(&self, other: &Self) -> bool {
@@ -659,9 +685,19 @@ impl ProjectWorkspace {
&& sysroot == o_sysroot
}
(
- Self::Json { project, sysroot, rustc_cfg },
- Self::Json { project: o_project, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg },
- ) => project == o_project && rustc_cfg == o_rustc_cfg && sysroot == o_sysroot,
+ Self::Json { project, sysroot, rustc_cfg, toolchain },
+ Self::Json {
+ project: o_project,
+ sysroot: o_sysroot,
+ rustc_cfg: o_rustc_cfg,
+ toolchain: o_toolchain,
+ },
+ ) => {
+ project == o_project
+ && rustc_cfg == o_rustc_cfg
+ && sysroot == o_sysroot
+ && toolchain == o_toolchain
+ }
(
Self::DetachedFiles { files, sysroot, rustc_cfg },
Self::DetachedFiles { files: o_files, sysroot: o_sysroot, rustc_cfg: o_rustc_cfg },
@@ -669,130 +705,146 @@ impl ProjectWorkspace {
_ => false,
}
}
+
+ /// Returns `true` if the project workspace is [`Json`].
+ ///
+ /// [`Json`]: ProjectWorkspace::Json
+ #[must_use]
+ pub fn is_json(&self) -> bool {
+ matches!(self, Self::Json { .. })
+ }
}
fn project_json_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
- load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
project: &ProjectJson,
sysroot: Option<&Sysroot>,
extra_env: &FxHashMap<String, String>,
target_layout: TargetLayoutLoadResult,
-) -> CrateGraph {
- let mut crate_graph = CrateGraph::default();
+ channel: Option<ReleaseChannel>,
+) -> (CrateGraph, ProcMacroPaths) {
+ let mut res = (CrateGraph::default(), ProcMacroPaths::default());
+ let (crate_graph, proc_macros) = &mut res;
let sysroot_deps = sysroot.as_ref().map(|sysroot| {
sysroot_to_crate_graph(
- &mut crate_graph,
+ crate_graph,
sysroot,
rustc_cfg.clone(),
target_layout.clone(),
load,
+ channel,
)
});
let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
- let crates: NoHashHashMap<CrateId, CrateId> = project
+ let crates: FxHashMap<CrateId, CrateId> = project
.crates()
- .filter_map(|(crate_id, krate)| {
- let file_path = &krate.root_module;
- let file_id = load(file_path)?;
- Some((crate_id, krate, file_id))
- })
- .map(|(crate_id, krate, file_id)| {
- let env = krate.env.clone().into_iter().collect();
- let proc_macro = match krate.proc_macro_dylib_path.clone() {
- Some(it) => load_proc_macro(
- krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
- &it,
- ),
- None => Err("no proc macro dylib present".into()),
- };
-
- let target_cfgs = match krate.target.as_deref() {
- Some(target) => cfg_cache
- .entry(target)
- .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)),
- None => &rustc_cfg,
- };
-
- let mut cfg_options = CfgOptions::default();
- cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
- (
+ .filter_map(|(crate_id, krate)| Some((crate_id, krate, load(&krate.root_module)?)))
+ .map(
+ |(
crate_id,
- crate_graph.add_crate_root(
+ Crate {
+ display_name,
+ edition,
+ version,
+ cfg,
+ target,
+ env,
+ proc_macro_dylib_path,
+ is_proc_macro,
+ repository,
+ ..
+ },
+ file_id,
+ )| {
+ let env = env.clone().into_iter().collect();
+
+ let target_cfgs = match target.as_deref() {
+ Some(target) => cfg_cache
+ .entry(target)
+ .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)),
+ None => &rustc_cfg,
+ };
+
+ let crate_graph_crate_id = crate_graph.add_crate_root(
file_id,
- krate.edition,
- krate.display_name.clone(),
- krate.version.clone(),
- cfg_options.clone(),
- cfg_options,
+ *edition,
+ display_name.clone(),
+ version.clone(),
+ target_cfgs.iter().chain(cfg.iter()).cloned().collect(),
+ None,
env,
- proc_macro,
- krate.is_proc_macro,
- if krate.display_name.is_some() {
- CrateOrigin::CratesIo {
- repo: krate.repository.clone(),
- name: krate
- .display_name
- .clone()
- .map(|n| n.canonical_name().to_string()),
+ *is_proc_macro,
+ if let Some(name) = display_name.clone() {
+ CrateOrigin::Local {
+ repo: repository.clone(),
+ name: Some(name.canonical_name().to_string()),
}
} else {
- CrateOrigin::CratesIo { repo: None, name: None }
+ CrateOrigin::Local { repo: None, name: None }
},
target_layout.clone(),
- ),
- )
- })
+ channel,
+ );
+ if *is_proc_macro {
+ if let Some(path) = proc_macro_dylib_path.clone() {
+ let node = Ok((
+ display_name.as_ref().map(|it| it.canonical_name().to_owned()),
+ path,
+ ));
+ proc_macros.insert(crate_graph_crate_id, node);
+ }
+ }
+ (crate_id, crate_graph_crate_id)
+ },
+ )
.collect();
for (from, krate) in project.crates() {
if let Some(&from) = crates.get(&from) {
if let Some((public_deps, libproc_macro)) = &sysroot_deps {
- public_deps.add_to_crate_graph(&mut crate_graph, from);
- if krate.is_proc_macro {
- if let Some(proc_macro) = libproc_macro {
- add_dep(
- &mut crate_graph,
- from,
- CrateName::new("proc_macro").unwrap(),
- *proc_macro,
- );
- }
+ public_deps.add_to_crate_graph(crate_graph, from);
+ if let Some(proc_macro) = libproc_macro {
+ add_proc_macro_dep(crate_graph, from, *proc_macro, krate.is_proc_macro);
}
}
for dep in &krate.deps {
if let Some(&to) = crates.get(&dep.crate_id) {
- add_dep(&mut crate_graph, from, dep.name.clone(), to)
+ add_dep(crate_graph, from, dep.name.clone(), to)
}
}
}
}
- crate_graph
+ res
}
fn cargo_to_crate_graph(
- load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
rustc: Option<&(CargoWorkspace, WorkspaceBuildScripts)>,
cargo: &CargoWorkspace,
sysroot: Option<&Sysroot>,
rustc_cfg: Vec<CfgFlag>,
override_cfg: &CfgOverrides,
+ // Don't compute cfg and use this if present
+ forced_cfg: Option<CfgOptions>,
build_scripts: &WorkspaceBuildScripts,
target_layout: TargetLayoutLoadResult,
-) -> CrateGraph {
+ channel: Option<ReleaseChannel>,
+) -> (CrateGraph, ProcMacroPaths) {
let _p = profile::span("cargo_to_crate_graph");
- let mut crate_graph = CrateGraph::default();
+ let mut res = (CrateGraph::default(), ProcMacroPaths::default());
+ let crate_graph = &mut res.0;
+ let proc_macros = &mut res.1;
let (public_deps, libproc_macro) = match sysroot {
Some(sysroot) => sysroot_to_crate_graph(
- &mut crate_graph,
+ crate_graph,
sysroot,
rustc_cfg.clone(),
target_layout.clone(),
load,
+ channel,
),
None => (SysrootPublicDeps::default(), None),
};
@@ -804,37 +856,40 @@ fn cargo_to_crate_graph(
cfg_options
};
+ // Mapping of a package to its library target
let mut pkg_to_lib_crate = FxHashMap::default();
-
let mut pkg_crates = FxHashMap::default();
// Does any crate signal to rust-analyzer that they need the rustc_private crates?
let mut has_private = false;
+
// Next, create crates for each package, target pair
for pkg in cargo.packages() {
- let mut cfg_options = cfg_options.clone();
+ has_private |= cargo[pkg].metadata.rustc_private;
- let overrides = match override_cfg {
- CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
- CfgOverrides::Selective(cfg_overrides) => cfg_overrides.get(&cargo[pkg].name),
- };
+ let cfg_options = forced_cfg.clone().unwrap_or_else(|| {
+ let mut cfg_options = cfg_options.clone();
- // Add test cfg for local crates
- if cargo[pkg].is_local {
- cfg_options.insert_atom("test".into());
- }
+ // Add test cfg for local crates
+ if cargo[pkg].is_local {
+ cfg_options.insert_atom("test".into());
+ }
- if let Some(overrides) = overrides {
- // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
- // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
- // working on rust-lang/rust as that's the only time it appears outside sysroot).
- //
- // A more ideal solution might be to reanalyze crates based on where the cursor is and
- // figure out the set of cfgs that would have to apply to make it active.
+ if !override_cfg.global.is_empty() {
+ cfg_options.apply_diff(override_cfg.global.clone());
+ };
+ if let Some(diff) = override_cfg.selective.get(&cargo[pkg].name) {
+ // FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
+ // in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
+ // working on rust-lang/rust as that's the only time it appears outside sysroot).
+ //
+ // A more ideal solution might be to reanalyze crates based on where the cursor is and
+ // figure out the set of cfgs that would have to apply to make it active.
- cfg_options.apply_diff(overrides.clone());
- };
+ cfg_options.apply_diff(diff.clone());
+ };
+ cfg_options
+ });
- has_private |= cargo[pkg].metadata.rustc_private;
let mut lib_tgt = None;
for &tgt in cargo[pkg].targets.iter() {
if cargo[tgt].kind != TargetKind::Lib && !cargo[pkg].is_member {
@@ -845,44 +900,57 @@ fn cargo_to_crate_graph(
// https://github.com/rust-lang/rust-analyzer/issues/11300
continue;
}
+ let &TargetData { ref name, kind, is_proc_macro, ref root, .. } = &cargo[tgt];
- if let Some(file_id) = load(&cargo[tgt].root) {
- let crate_id = add_target_crate_root(
- &mut crate_graph,
- &cargo[pkg],
- build_scripts.get_output(pkg),
- cfg_options.clone(),
- &mut |path| load_proc_macro(&cargo[tgt].name, path),
- file_id,
- &cargo[tgt].name,
- cargo[tgt].is_proc_macro,
- target_layout.clone(),
- );
- if cargo[tgt].kind == TargetKind::Lib {
- lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
+ if kind == TargetKind::Lib
+ && sysroot.map_or(false, |sysroot| root.starts_with(sysroot.src_root()))
+ {
+ if let Some(&(_, crate_id, _)) =
+ public_deps.deps.iter().find(|(dep_name, ..)| dep_name.as_smol_str() == name)
+ {
+ pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind));
+
+ lib_tgt = Some((crate_id, name.clone()));
pkg_to_lib_crate.insert(pkg, crate_id);
+ // sysroot is inside the workspace, prevent the sysroot crates from being duplicated here
+ continue;
}
- // Even crates that don't set proc-macro = true are allowed to depend on proc_macro
- // (just none of the APIs work when called outside of a proc macro).
- if let Some(proc_macro) = libproc_macro {
- add_dep_with_prelude(
- &mut crate_graph,
- crate_id,
- CrateName::new("proc_macro").unwrap(),
- proc_macro,
- cargo[tgt].is_proc_macro,
- );
- }
+ }
+
+ let Some(file_id) = load(root) else { continue };
- pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, cargo[tgt].kind));
+ let crate_id = add_target_crate_root(
+ crate_graph,
+ proc_macros,
+ &cargo[pkg],
+ build_scripts.get_output(pkg),
+ cfg_options.clone(),
+ file_id,
+ name,
+ is_proc_macro,
+ target_layout.clone(),
+ false,
+ channel,
+ );
+ if kind == TargetKind::Lib {
+ lib_tgt = Some((crate_id, name.clone()));
+ pkg_to_lib_crate.insert(pkg, crate_id);
+ }
+ // Even crates that don't set proc-macro = true are allowed to depend on proc_macro
+ // (just none of the APIs work when called outside of a proc macro).
+ if let Some(proc_macro) = libproc_macro {
+ add_proc_macro_dep(crate_graph, crate_id, proc_macro, is_proc_macro);
}
+
+ pkg_crates.entry(pkg).or_insert_with(Vec::new).push((crate_id, kind));
}
// Set deps to the core, std and to the lib target of the current package
for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
// Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
- public_deps.add_to_crate_graph(&mut crate_graph, from);
+ public_deps.add_to_crate_graph(crate_graph, from);
+ // Add dep edge of all targets to the package's lib target
if let Some((to, name)) = lib_tgt.clone() {
if to != from && kind != TargetKind::BuildScript {
// (build script can not depend on its library target)
@@ -891,7 +959,7 @@ fn cargo_to_crate_graph(
// cargo metadata does not do any normalization,
// so we do it ourselves currently
let name = CrateName::normalize_dashes(&name);
- add_dep(&mut crate_graph, from, name, to);
+ add_dep(crate_graph, from, name, to);
}
}
}
@@ -900,21 +968,18 @@ fn cargo_to_crate_graph(
// Now add a dep edge from all targets of upstream to the lib
// target of downstream.
for pkg in cargo.packages() {
- for dep in cargo[pkg].dependencies.iter() {
- let name = CrateName::new(&dep.name).unwrap();
- if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
- for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
- if dep.kind == DepKind::Build && kind != TargetKind::BuildScript {
- // Only build scripts may depend on build dependencies.
- continue;
- }
- if dep.kind != DepKind::Build && kind == TargetKind::BuildScript {
- // Build scripts may only depend on build dependencies.
- continue;
- }
+ for dep in &cargo[pkg].dependencies {
+ let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) else { continue };
+ let Some(targets) = pkg_crates.get(&pkg) else { continue };
- add_dep(&mut crate_graph, from, name.clone(), to)
+ let name = CrateName::new(&dep.name).unwrap();
+ for &(from, kind) in targets {
+ // Build scripts may only depend on build dependencies.
+ if (dep.kind == DepKind::Build) != (kind == TargetKind::BuildScript) {
+ continue;
}
+
+ add_dep(crate_graph, from, name.clone(), to)
}
}
}
@@ -924,10 +989,10 @@ fn cargo_to_crate_graph(
// and create dependencies on them for the crates which opt-in to that
if let Some((rustc_workspace, rustc_build_scripts)) = rustc {
handle_rustc_crates(
- &mut crate_graph,
+ crate_graph,
+ proc_macros,
&mut pkg_to_lib_crate,
load,
- load_proc_macro,
rustc_workspace,
cargo,
&public_deps,
@@ -943,10 +1008,11 @@ fn cargo_to_crate_graph(
rustc_build_scripts
},
target_layout,
+ channel,
);
}
}
- crate_graph
+ res
}
fn detached_files_to_crate_graph(
@@ -955,7 +1021,7 @@ fn detached_files_to_crate_graph(
detached_files: &[AbsPathBuf],
sysroot: Option<&Sysroot>,
target_layout: TargetLayoutLoadResult,
-) -> CrateGraph {
+) -> (CrateGraph, ProcMacroPaths) {
let _p = profile::span("detached_files_to_crate_graph");
let mut crate_graph = CrateGraph::default();
let (public_deps, _libproc_macro) = match sysroot {
@@ -965,6 +1031,7 @@ fn detached_files_to_crate_graph(
rustc_cfg.clone(),
target_layout.clone(),
load,
+ None,
),
None => (SysrootPublicDeps::default(), None),
};
@@ -990,27 +1057,27 @@ fn detached_files_to_crate_graph(
display_name.clone(),
None,
cfg_options.clone(),
- cfg_options.clone(),
+ None,
Env::default(),
- Ok(Vec::new()),
false,
- CrateOrigin::CratesIo {
+ CrateOrigin::Local {
repo: None,
name: display_name.map(|n| n.canonical_name().to_string()),
},
target_layout.clone(),
+ None,
);
public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
}
- crate_graph
+ (crate_graph, FxHashMap::default())
}
fn handle_rustc_crates(
crate_graph: &mut CrateGraph,
+ proc_macros: &mut ProcMacroPaths,
pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
- load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
rustc_workspace: &CargoWorkspace,
cargo: &CargoWorkspace,
public_deps: &SysrootPublicDeps,
@@ -1020,6 +1087,7 @@ fn handle_rustc_crates(
override_cfg: &CfgOverrides,
build_scripts: &WorkspaceBuildScripts,
target_layout: TargetLayoutLoadResult,
+ channel: Option<ReleaseChannel>,
) {
let mut rustc_pkg_crates = FxHashMap::default();
// The root package of the rustc-dev component is rustc_driver, so we match that
@@ -1044,14 +1112,10 @@ fn handle_rustc_crates(
let mut cfg_options = cfg_options.clone();
- let overrides = match override_cfg {
- CfgOverrides::Wildcard(cfg_diff) => Some(cfg_diff),
- CfgOverrides::Selective(cfg_overrides) => {
- cfg_overrides.get(&rustc_workspace[pkg].name)
- }
+ if !override_cfg.global.is_empty() {
+ cfg_options.apply_diff(override_cfg.global.clone());
};
-
- if let Some(overrides) = overrides {
+ if let Some(diff) = override_cfg.selective.get(&rustc_workspace[pkg].name) {
// FIXME: this is sort of a hack to deal with #![cfg(not(test))] vanishing such as seen
// in ed25519_dalek (#7243), and libcore (#9203) (although you only hit that one while
// working on rust-lang/rust as that's the only time it appears outside sysroot).
@@ -1059,7 +1123,7 @@ fn handle_rustc_crates(
// A more ideal solution might be to reanalyze crates based on where the cursor is and
// figure out the set of cfgs that would have to apply to make it active.
- cfg_options.apply_diff(overrides.clone());
+ cfg_options.apply_diff(diff.clone());
};
for &tgt in rustc_workspace[pkg].targets.iter() {
@@ -1069,23 +1133,24 @@ fn handle_rustc_crates(
if let Some(file_id) = load(&rustc_workspace[tgt].root) {
let crate_id = add_target_crate_root(
crate_graph,
+ proc_macros,
&rustc_workspace[pkg],
build_scripts.get_output(pkg),
cfg_options.clone(),
- &mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
file_id,
&rustc_workspace[tgt].name,
rustc_workspace[tgt].is_proc_macro,
target_layout.clone(),
+ true,
+ channel,
);
pkg_to_lib_crate.insert(pkg, crate_id);
// Add dependencies on core / std / alloc for this crate
public_deps.add_to_crate_graph(crate_graph, crate_id);
if let Some(proc_macro) = libproc_macro {
- add_dep_with_prelude(
+ add_proc_macro_dep(
crate_graph,
crate_id,
- CrateName::new("proc_macro").unwrap(),
proc_macro,
rustc_workspace[tgt].is_proc_macro,
);
@@ -1134,22 +1199,29 @@ fn handle_rustc_crates(
fn add_target_crate_root(
crate_graph: &mut CrateGraph,
+ proc_macros: &mut ProcMacroPaths,
pkg: &PackageData,
build_data: Option<&BuildScriptOutput>,
cfg_options: CfgOptions,
- load_proc_macro: &mut dyn FnMut(&AbsPath) -> ProcMacroLoadResult,
file_id: FileId,
cargo_name: &str,
is_proc_macro: bool,
target_layout: TargetLayoutLoadResult,
+ rustc_crate: bool,
+ channel: Option<ReleaseChannel>,
) -> CrateId {
let edition = pkg.edition;
- let mut potential_cfg_options = cfg_options.clone();
- potential_cfg_options.extend(
- pkg.features
- .iter()
- .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
- );
+ let potential_cfg_options = if pkg.features.is_empty() {
+ None
+ } else {
+ let mut potential_cfg_options = cfg_options.clone();
+ potential_cfg_options.extend(
+ pkg.features
+ .iter()
+ .map(|feat| CfgFlag::KeyValue { key: "feature".into(), value: feat.0.into() }),
+ );
+ Some(potential_cfg_options)
+ };
let cfg_options = {
let mut opts = cfg_options;
for feature in pkg.active_features.iter() {
@@ -1170,14 +1242,8 @@ fn add_target_crate_root(
}
}
- let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
- Some(Some(it)) => load_proc_macro(it),
- Some(None) => Err("no proc macro dylib present".into()),
- None => Err("crate has not (yet) been built".into()),
- };
-
let display_name = CrateDisplayName::from_canonical_name(cargo_name.to_string());
- crate_graph.add_crate_root(
+ let crate_id = crate_graph.add_crate_root(
file_id,
edition,
Some(display_name),
@@ -1185,11 +1251,28 @@ fn add_target_crate_root(
cfg_options,
potential_cfg_options,
env,
- proc_macro,
is_proc_macro,
- CrateOrigin::CratesIo { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) },
+ if rustc_crate {
+ CrateOrigin::Rustc { name: pkg.name.clone() }
+ } else if pkg.is_member {
+ CrateOrigin::Local { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) }
+ } else {
+ CrateOrigin::Library { repo: pkg.repository.clone(), name: pkg.name.clone() }
+ },
target_layout,
- )
+ channel,
+ );
+ if is_proc_macro {
+ let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) {
+ Some(it) => it.cloned().map(|path| Ok((Some(cargo_name.to_owned()), path))),
+ None => Some(Err("crate has not yet been built".to_owned())),
+ };
+ if let Some(proc_macro) = proc_macro {
+ proc_macros.insert(crate_id, proc_macro);
+ }
+ }
+
+ crate_id
}
#[derive(Default)]
@@ -1212,34 +1295,47 @@ fn sysroot_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
target_layout: TargetLayoutLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+ channel: Option<ReleaseChannel>,
) -> (SysrootPublicDeps, Option<CrateId>) {
let _p = profile::span("sysroot_to_crate_graph");
let mut cfg_options = CfgOptions::default();
- cfg_options.extend(rustc_cfg);
- let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = sysroot
- .crates()
- .filter_map(|krate| {
- let file_id = load(&sysroot[krate].root)?;
-
- let env = Env::default();
- let display_name = CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
- let crate_id = crate_graph.add_crate_root(
- file_id,
- Edition::CURRENT,
- Some(display_name),
- None,
- cfg_options.clone(),
- cfg_options.clone(),
- env,
- Err("no proc macro loaded for sysroot crate".into()),
- false,
- CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
- target_layout.clone(),
- );
- Some((krate, crate_id))
- })
- .collect();
-
+ cfg_options.extend(rustc_cfg.clone());
+ let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = match &sysroot.hack_cargo_workspace {
+ Some(cargo) => handle_hack_cargo_workspace(
+ load,
+ cargo,
+ rustc_cfg,
+ cfg_options,
+ target_layout,
+ channel,
+ crate_graph,
+ sysroot,
+ ),
+ None => sysroot
+ .crates()
+ .filter_map(|krate| {
+ let file_id = load(&sysroot[krate].root)?;
+
+ let env = Env::default();
+ let display_name =
+ CrateDisplayName::from_canonical_name(sysroot[krate].name.clone());
+ let crate_id = crate_graph.add_crate_root(
+ file_id,
+ Edition::CURRENT,
+ Some(display_name),
+ None,
+ cfg_options.clone(),
+ None,
+ env,
+ false,
+ CrateOrigin::Lang(LangCrateOrigin::from(&*sysroot[krate].name)),
+ target_layout.clone(),
+ channel,
+ );
+ Some((krate, crate_id))
+ })
+ .collect(),
+ };
for from in sysroot.crates() {
for &to in sysroot[from].deps.iter() {
let name = CrateName::new(&sysroot[to].name).unwrap();
@@ -1260,6 +1356,69 @@ fn sysroot_to_crate_graph(
(public_deps, libproc_macro)
}
+fn handle_hack_cargo_workspace(
+ load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+ cargo: &CargoWorkspace,
+ rustc_cfg: Vec<CfgFlag>,
+ cfg_options: CfgOptions,
+ target_layout: Result<Arc<str>, Arc<str>>,
+ channel: Option<ReleaseChannel>,
+ crate_graph: &mut CrateGraph,
+ sysroot: &Sysroot,
+) -> FxHashMap<SysrootCrate, CrateId> {
+ let (cg, mut pm) = cargo_to_crate_graph(
+ load,
+ None,
+ cargo,
+ None,
+ rustc_cfg,
+ &CfgOverrides::default(),
+ Some(cfg_options),
+ &WorkspaceBuildScripts::default(),
+ target_layout,
+ channel,
+ );
+ crate_graph.extend(cg, &mut pm);
+ for crate_name in ["std", "alloc", "core"] {
+ let original = crate_graph
+ .iter()
+ .find(|x| {
+ crate_graph[*x]
+ .display_name
+ .as_ref()
+ .map(|x| x.canonical_name() == crate_name)
+ .unwrap_or(false)
+ })
+ .unwrap();
+ let fake_crate_name = format!("rustc-std-workspace-{}", crate_name);
+ let fake = crate_graph
+ .iter()
+ .find(|x| {
+ crate_graph[*x]
+ .display_name
+ .as_ref()
+ .map(|x| x.canonical_name() == fake_crate_name)
+ .unwrap_or(false)
+ })
+ .unwrap();
+ crate_graph.remove_and_replace(fake, original).unwrap();
+ }
+ for (_, c) in crate_graph.iter_mut() {
+ if c.origin.is_local() {
+ // LangCrateOrigin::Other is good enough for a hack.
+ c.origin = CrateOrigin::Lang(LangCrateOrigin::Other);
+ }
+ }
+ sysroot
+ .crates()
+ .filter_map(|krate| {
+ let file_id = load(&sysroot[krate].root)?;
+ let crate_id = crate_graph.crate_id_for_crate_root(file_id)?;
+ Some((krate, crate_id))
+ })
+ .collect()
+}
+
fn add_dep(graph: &mut CrateGraph, from: CrateId, name: CrateName, to: CrateId) {
add_dep_inner(graph, from, Dependency::new(name, to))
}
@@ -1274,6 +1433,10 @@ fn add_dep_with_prelude(
add_dep_inner(graph, from, Dependency::with_prelude(name, to, prelude))
}
+fn add_proc_macro_dep(crate_graph: &mut CrateGraph, from: CrateId, to: CrateId, prelude: bool) {
+ add_dep_with_prelude(crate_graph, from, CrateName::new("proc_macro").unwrap(), to, prelude);
+}
+
fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) {
if let Err(err) = graph.add_dep(from, dep) {
tracing::error!("{}", err)
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
new file mode 100644
index 000000000..e595cd827
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
@@ -0,0 +1,344 @@
+{
+ 0: CrateData {
+ root_file_id: FileId(
+ 1,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello-world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 1: CrateData {
+ root_file_id: FileId(
+ 2,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello-world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 2: CrateData {
+ root_file_id: FileId(
+ 3,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "an_example",
+ ),
+ canonical_name: "an-example",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 3: CrateData {
+ root_file_id: FileId(
+ 4,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "it",
+ ),
+ canonical_name: "it",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 4: CrateData {
+ root_file_id: FileId(
+ 5,
+ ),
+ edition: Edition2015,
+ version: Some(
+ "0.2.98",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "libc",
+ ),
+ canonical_name: "libc",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "feature=default",
+ "feature=std",
+ ],
+ ),
+ potential_cfg_options: Some(
+ CfgOptions(
+ [
+ "debug_assertions",
+ "feature=align",
+ "feature=const-extern-fn",
+ "feature=default",
+ "feature=extra_traits",
+ "feature=rustc-dep-of-std",
+ "feature=std",
+ "feature=use_std",
+ ],
+ ),
+ ),
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+ "CARGO_PKG_VERSION": "0.2.98",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "libc",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "libc",
+ "CARGO_PKG_VERSION_PATCH": "98",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "2",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [],
+ origin: Library {
+ repo: Some(
+ "https://github.com/rust-lang/libc",
+ ),
+ name: "libc",
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+} \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
new file mode 100644
index 000000000..e595cd827
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
@@ -0,0 +1,344 @@
+{
+ 0: CrateData {
+ root_file_id: FileId(
+ 1,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello-world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 1: CrateData {
+ root_file_id: FileId(
+ 2,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello-world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 2: CrateData {
+ root_file_id: FileId(
+ 3,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "an_example",
+ ),
+ canonical_name: "an-example",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 3: CrateData {
+ root_file_id: FileId(
+ 4,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "it",
+ ),
+ canonical_name: "it",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "test",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 4: CrateData {
+ root_file_id: FileId(
+ 5,
+ ),
+ edition: Edition2015,
+ version: Some(
+ "0.2.98",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "libc",
+ ),
+ canonical_name: "libc",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "feature=default",
+ "feature=std",
+ ],
+ ),
+ potential_cfg_options: Some(
+ CfgOptions(
+ [
+ "debug_assertions",
+ "feature=align",
+ "feature=const-extern-fn",
+ "feature=default",
+ "feature=extra_traits",
+ "feature=rustc-dep-of-std",
+ "feature=std",
+ "feature=use_std",
+ ],
+ ),
+ ),
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+ "CARGO_PKG_VERSION": "0.2.98",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "libc",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "libc",
+ "CARGO_PKG_VERSION_PATCH": "98",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "2",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [],
+ origin: Library {
+ repo: Some(
+ "https://github.com/rust-lang/libc",
+ ),
+ name: "libc",
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+} \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
new file mode 100644
index 000000000..f10c55d04
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
@@ -0,0 +1,340 @@
+{
+ 0: CrateData {
+ root_file_id: FileId(
+ 1,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello-world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 1: CrateData {
+ root_file_id: FileId(
+ 2,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello-world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 2: CrateData {
+ root_file_id: FileId(
+ 3,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "an_example",
+ ),
+ canonical_name: "an-example",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 3: CrateData {
+ root_file_id: FileId(
+ 4,
+ ),
+ edition: Edition2018,
+ version: Some(
+ "0.1.0",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "it",
+ ),
+ canonical_name: "it",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ ],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$hello-world",
+ "CARGO_PKG_VERSION": "0.1.0",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "hello-world",
+ "CARGO_PKG_VERSION_PATCH": "0",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "1",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "hello_world",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "libc",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello-world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+ 4: CrateData {
+ root_file_id: FileId(
+ 5,
+ ),
+ edition: Edition2015,
+ version: Some(
+ "0.2.98",
+ ),
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "libc",
+ ),
+ canonical_name: "libc",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [
+ "debug_assertions",
+ "feature=default",
+ "feature=std",
+ ],
+ ),
+ potential_cfg_options: Some(
+ CfgOptions(
+ [
+ "debug_assertions",
+ "feature=align",
+ "feature=const-extern-fn",
+ "feature=default",
+ "feature=extra_traits",
+ "feature=rustc-dep-of-std",
+ "feature=std",
+ "feature=use_std",
+ ],
+ ),
+ ),
+ env: Env {
+ entries: {
+ "CARGO_PKG_LICENSE": "",
+ "CARGO_PKG_VERSION_MAJOR": "0",
+ "CARGO_MANIFEST_DIR": "$ROOT$.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.98",
+ "CARGO_PKG_VERSION": "0.2.98",
+ "CARGO_PKG_AUTHORS": "",
+ "CARGO_CRATE_NAME": "libc",
+ "CARGO_PKG_LICENSE_FILE": "",
+ "CARGO_PKG_HOMEPAGE": "",
+ "CARGO_PKG_DESCRIPTION": "",
+ "CARGO_PKG_NAME": "libc",
+ "CARGO_PKG_VERSION_PATCH": "98",
+ "CARGO": "cargo",
+ "CARGO_PKG_REPOSITORY": "",
+ "CARGO_PKG_VERSION_MINOR": "2",
+ "CARGO_PKG_VERSION_PRE": "",
+ },
+ },
+ dependencies: [],
+ origin: Library {
+ repo: Some(
+ "https://github.com/rust-lang/libc",
+ ),
+ name: "libc",
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "target_data_layout not loaded",
+ ),
+ channel: None,
+ },
+} \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
new file mode 100644
index 000000000..fb3f5933b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
@@ -0,0 +1,462 @@
+{
+ 0: CrateData {
+ root_file_id: FileId(
+ 1,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "alloc",
+ ),
+ canonical_name: "alloc",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(1),
+ name: CrateName(
+ "core",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Lang(
+ Alloc,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 1: CrateData {
+ root_file_id: FileId(
+ 2,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "core",
+ ),
+ canonical_name: "core",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Core,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 2: CrateData {
+ root_file_id: FileId(
+ 3,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "panic_abort",
+ ),
+ canonical_name: "panic_abort",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Other,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 3: CrateData {
+ root_file_id: FileId(
+ 4,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "panic_unwind",
+ ),
+ canonical_name: "panic_unwind",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Other,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 4: CrateData {
+ root_file_id: FileId(
+ 5,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "proc_macro",
+ ),
+ canonical_name: "proc_macro",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(6),
+ name: CrateName(
+ "std",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(1),
+ name: CrateName(
+ "core",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Lang(
+ Other,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 5: CrateData {
+ root_file_id: FileId(
+ 6,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "profiler_builtins",
+ ),
+ canonical_name: "profiler_builtins",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Other,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 6: CrateData {
+ root_file_id: FileId(
+ 7,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "std",
+ ),
+ canonical_name: "std",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "alloc",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(3),
+ name: CrateName(
+ "panic_unwind",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(2),
+ name: CrateName(
+ "panic_abort",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(1),
+ name: CrateName(
+ "core",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(5),
+ name: CrateName(
+ "profiler_builtins",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(9),
+ name: CrateName(
+ "unwind",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(7),
+ name: CrateName(
+ "std_detect",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(8),
+ name: CrateName(
+ "test",
+ ),
+ prelude: true,
+ },
+ ],
+ origin: Lang(
+ Std,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 7: CrateData {
+ root_file_id: FileId(
+ 8,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "std_detect",
+ ),
+ canonical_name: "std_detect",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Other,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 8: CrateData {
+ root_file_id: FileId(
+ 9,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "test",
+ ),
+ canonical_name: "test",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Test,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 9: CrateData {
+ root_file_id: FileId(
+ 10,
+ ),
+ edition: Edition2021,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "unwind",
+ ),
+ canonical_name: "unwind",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [],
+ origin: Lang(
+ Other,
+ ),
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+ 10: CrateData {
+ root_file_id: FileId(
+ 11,
+ ),
+ edition: Edition2018,
+ version: None,
+ display_name: Some(
+ CrateDisplayName {
+ crate_name: CrateName(
+ "hello_world",
+ ),
+ canonical_name: "hello_world",
+ },
+ ),
+ cfg_options: CfgOptions(
+ [],
+ ),
+ potential_cfg_options: None,
+ env: Env {
+ entries: {},
+ },
+ dependencies: [
+ Dependency {
+ crate_id: Idx::<CrateData>(1),
+ name: CrateName(
+ "core",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(0),
+ name: CrateName(
+ "alloc",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(6),
+ name: CrateName(
+ "std",
+ ),
+ prelude: true,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(8),
+ name: CrateName(
+ "test",
+ ),
+ prelude: false,
+ },
+ Dependency {
+ crate_id: Idx::<CrateData>(4),
+ name: CrateName(
+ "proc_macro",
+ ),
+ prelude: false,
+ },
+ ],
+ origin: Local {
+ repo: None,
+ name: Some(
+ "hello_world",
+ ),
+ },
+ is_proc_macro: false,
+ target_layout: Err(
+ "rust-project.json projects have no target layout set",
+ ),
+ channel: None,
+ },
+} \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json
new file mode 100644
index 000000000..371464dd2
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json
@@ -0,0 +1,6420 @@
+{
+ "packages": [
+ {
+ "name": "aho-corasick",
+ "version": "0.7.20",
+ "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Fast multiple substring searching.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "aho_corasick",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "default": [
+ "std"
+ ],
+ "std": [
+ "memchr/std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [
+ "string",
+ "search",
+ "text",
+ "aho",
+ "multi"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/aho-corasick",
+ "homepage": "https://github.com/BurntSushi/aho-corasick",
+ "documentation": null,
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "cc",
+ "version": "1.0.79",
+ "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "jobserver",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.16",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tempfile",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "cc",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "gcc-shim",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cc_env",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cflags",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cxxflags",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "jobserver": [
+ "dep:jobserver"
+ ],
+ "parallel": [
+ "jobserver"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [
+ "development-tools::build-utils"
+ ],
+ "keywords": [
+ "build-dependencies"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/cc-rs",
+ "homepage": "https://github.com/rust-lang/cc-rs",
+ "documentation": "https://docs.rs/cc",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "cfg-if",
+ "version": "0.1.10",
+ "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "cfg-if",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "xcrate",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/tests/xcrate.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/alexcrichton/cfg-if",
+ "homepage": "https://github.com/alexcrichton/cfg-if",
+ "documentation": "https://docs.rs/cfg-if",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "cfg-if",
+ "version": "1.0.0",
+ "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "cfg-if",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "xcrate",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/alexcrichton/cfg-if",
+ "homepage": "https://github.com/alexcrichton/cfg-if",
+ "documentation": "https://docs.rs/cfg-if",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "docopt",
+ "version": "1.1.1",
+ "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Command line argument parsing.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "std",
+ "unicode"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "strsim",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.10",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "docopt",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "docopt-wordlist",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/wordlist.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cargo",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cargo.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cp",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cp.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "decode",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/decode.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "hashmap",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/hashmap.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "optional_command",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/optional_command.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "verbose_multiple",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/verbose_multiple.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "command-line-interface"
+ ],
+ "keywords": [
+ "docopt",
+ "argument",
+ "command",
+ "argv"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/docopt/docopt.rs",
+ "homepage": "https://github.com/docopt/docopt.rs",
+ "documentation": "http://burntsushi.net/rustdoc/docopt/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "getrandom",
+ "version": "0.2.9",
+ "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A small cross-platform library for retrieving random data from system source",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "js-sys",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))",
+ "registry": null
+ },
+ {
+ "name": "wasm-bindgen",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.62",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))",
+ "registry": null
+ },
+ {
+ "name": "wasm-bindgen-test",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.18",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))",
+ "registry": null
+ },
+ {
+ "name": "wasi",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.11",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": "cfg(target_os = \"wasi\")",
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.139",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": "cfg(unix)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "getrandom",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "custom",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/custom.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "normal",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/normal.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "rdrand",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/rdrand.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "buffer",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/benches/buffer.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "custom": [],
+ "js": [
+ "wasm-bindgen",
+ "js-sys"
+ ],
+ "js-sys": [
+ "dep:js-sys"
+ ],
+ "rdrand": [],
+ "rustc-dep-of-std": [
+ "compiler_builtins",
+ "core",
+ "libc/rustc-dep-of-std",
+ "wasi/rustc-dep-of-std"
+ ],
+ "std": [],
+ "test-in-browser": [],
+ "wasm-bindgen": [
+ "dep:wasm-bindgen"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "std",
+ "custom"
+ ],
+ "rustdoc-args": [
+ "--cfg",
+ "docsrs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rand Project Developers"
+ ],
+ "categories": [
+ "os",
+ "no-std"
+ ],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-random/getrandom",
+ "homepage": null,
+ "documentation": "https://docs.rs/getrandom",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "lazy_static",
+ "version": "1.4.0",
+ "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A macro for declaring lazily evaluated statics in Rust.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "spin",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "doc-comment",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "lazy_static",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "no_std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "spin": [
+ "dep:spin"
+ ],
+ "spin_no_std": [
+ "spin"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Marvin Löbel <loebel.marvin@gmail.com>"
+ ],
+ "categories": [
+ "no-std",
+ "rust-patterns",
+ "memory-management"
+ ],
+ "keywords": [
+ "macro",
+ "lazy",
+ "static"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang-nursery/lazy-static.rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/lazy_static",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "libc",
+ "version": "0.2.142",
+ "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Raw FFI bindings to platform libraries like libc.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "libc",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "const_fn",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "align": [],
+ "const-extern-fn": [],
+ "default": [
+ "std"
+ ],
+ "extra_traits": [],
+ "rustc-dep-of-std": [
+ "align",
+ "rustc-std-workspace-core"
+ ],
+ "rustc-std-workspace-core": [
+ "dep:rustc-std-workspace-core"
+ ],
+ "std": [],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "const-extern-fn",
+ "extra_traits"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "external-ffi-bindings",
+ "no-std",
+ "os"
+ ],
+ "keywords": [
+ "libc",
+ "ffi",
+ "bindings",
+ "operating",
+ "system"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/libc",
+ "homepage": "https://github.com/rust-lang/libc",
+ "documentation": "https://docs.rs/libc/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "memchr",
+ "version": "2.5.0",
+ "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Safe interface to memchr.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.18",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "memchr",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "default": [
+ "std"
+ ],
+ "libc": [
+ "dep:libc"
+ ],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins"
+ ],
+ "std": [],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>",
+ "bluss"
+ ],
+ "categories": [],
+ "keywords": [
+ "memchr",
+ "char",
+ "scan",
+ "strchr",
+ "string"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/memchr",
+ "homepage": "https://github.com/BurntSushi/memchr",
+ "documentation": "https://docs.rs/memchr/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "memmap",
+ "version": "0.6.2",
+ "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Cross-platform Rust API for memory-mapped file IO",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "tempdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(unix)",
+ "registry": null
+ },
+ {
+ "name": "winapi",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "basetsd",
+ "handleapi",
+ "memoryapi",
+ "minwindef",
+ "std",
+ "sysinfoapi"
+ ],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "memmap",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/examples/cat.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Dan Burkert <dan@danburkert.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "mmap",
+ "memory-map",
+ "io",
+ "file"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/danburkert/memmap-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/memmap",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "pkg-config",
+ "version": "0.3.26",
+ "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "pkg-config",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "build-dependencies"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/pkg-config-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/pkg-config",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "proc-macro2",
+ "version": "1.0.56",
+ "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "unicode-ident",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quote",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "proc-macro2",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "comments",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "features",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "marker",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_fmt",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "proc-macro"
+ ],
+ "nightly": [],
+ "proc-macro": [],
+ "span-locations": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "rustc-args": [
+ "--cfg",
+ "procmacro2_semver_exempt"
+ ],
+ "rustdoc-args": [
+ "--cfg",
+ "procmacro2_semver_exempt",
+ "--cfg",
+ "doc_cfg"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "span-locations"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>",
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers"
+ ],
+ "keywords": [
+ "macros",
+ "syn"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/proc-macro2",
+ "homepage": null,
+ "documentation": "https://docs.rs/proc-macro2",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.31"
+ },
+ {
+ "name": "quickcheck",
+ "version": "1.0.3",
+ "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Automatic property based testing with shrinking.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "env_logger",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "getrandom",
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "quickcheck",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "btree_set_range",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/btree_set_range.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "out_of_bounds",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/out_of_bounds.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "reverse",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "reverse_single",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse_single.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "sieve",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sieve.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "sort",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sort.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "regex",
+ "use_logging"
+ ],
+ "env_logger": [
+ "dep:env_logger"
+ ],
+ "log": [
+ "dep:log"
+ ],
+ "regex": [
+ "env_logger/regex"
+ ],
+ "use_logging": [
+ "log",
+ "env_logger"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::testing"
+ ],
+ "keywords": [
+ "testing",
+ "quickcheck",
+ "property",
+ "shrinking",
+ "fuzz"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/quickcheck",
+ "homepage": "https://github.com/BurntSushi/quickcheck",
+ "documentation": "https://docs.rs/quickcheck",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "quote",
+ "version": "1.0.26",
+ "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Quasi-quoting macro quote!(...)",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "proc-macro2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.52",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "trybuild",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.66",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "diff"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "quote",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "compiletest",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "proc-macro"
+ ],
+ "proc-macro": [
+ "proc-macro2/proc-macro"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers"
+ ],
+ "keywords": [
+ "macros",
+ "syn"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/quote",
+ "homepage": null,
+ "documentation": "https://docs.rs/quote/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.31"
+ },
+ {
+ "name": "rand",
+ "version": "0.8.5",
+ "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Random number generators and other randomness functionality.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.4",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "packed_simd_2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.7",
+ "kind": null,
+ "rename": "packed_simd",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [
+ "into_bits"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand_chacha",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand_core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.103",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bincode",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.2.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand_pcg",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.22",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": "cfg(unix)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "rand",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "alloc": [
+ "rand_core/alloc"
+ ],
+ "default": [
+ "std",
+ "std_rng"
+ ],
+ "getrandom": [
+ "rand_core/getrandom"
+ ],
+ "libc": [
+ "dep:libc"
+ ],
+ "log": [
+ "dep:log"
+ ],
+ "min_const_gen": [],
+ "nightly": [],
+ "packed_simd": [
+ "dep:packed_simd"
+ ],
+ "rand_chacha": [
+ "dep:rand_chacha"
+ ],
+ "serde": [
+ "dep:serde"
+ ],
+ "serde1": [
+ "serde",
+ "rand_core/serde1"
+ ],
+ "simd_support": [
+ "packed_simd"
+ ],
+ "small_rng": [],
+ "std": [
+ "rand_core/std",
+ "rand_chacha/std",
+ "alloc",
+ "getrandom",
+ "libc"
+ ],
+ "std_rng": [
+ "rand_chacha"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "doc_cfg"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "small_rng",
+ "serde1"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rand Project Developers",
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "algorithms",
+ "no-std"
+ ],
+ "keywords": [
+ "random",
+ "rng"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-random/rand",
+ "homepage": "https://rust-random.github.io/book",
+ "documentation": "https://docs.rs/rand",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "rand_core",
+ "version": "0.6.4",
+ "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Core random number generator traits and tools for implementation.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "getrandom",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "rand_core",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "alloc": [],
+ "getrandom": [
+ "dep:getrandom"
+ ],
+ "serde": [
+ "dep:serde"
+ ],
+ "serde1": [
+ "serde"
+ ],
+ "std": [
+ "alloc",
+ "getrandom",
+ "getrandom/std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "doc_cfg"
+ ]
+ }
+ },
+ "playground": {
+ "all-features": true
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rand Project Developers",
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "algorithms",
+ "no-std"
+ ],
+ "keywords": [
+ "random",
+ "rng"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-random/rand",
+ "homepage": "https://rust-random.github.io/book",
+ "documentation": "https://docs.rs/rand_core",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex",
+ "version": "1.7.1",
+ "id": "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "aho-corasick",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.7.18",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex-syntax",
+ "source": null,
+ "req": "^0.6.27",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$regex/regex-syntax"
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "getrandom",
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex",
+ "src_path": "$ROOT$regex/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-bytes",
+ "src_path": "$ROOT$regex/examples/shootout-regex-dna-bytes.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-cheat",
+ "src_path": "$ROOT$regex/examples/shootout-regex-dna-cheat.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-replace",
+ "src_path": "$ROOT$regex/examples/shootout-regex-dna-replace.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-single-cheat",
+ "src_path": "$ROOT$regex/examples/shootout-regex-dna-single-cheat.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-single",
+ "src_path": "$ROOT$regex/examples/shootout-regex-dna-single.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna",
+ "src_path": "$ROOT$regex/examples/shootout-regex-dna.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default",
+ "src_path": "$ROOT$regex/tests/test_default.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default-bytes",
+ "src_path": "$ROOT$regex/tests/test_default_bytes.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa",
+ "src_path": "$ROOT$regex/tests/test_nfa.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa-utf8bytes",
+ "src_path": "$ROOT$regex/tests/test_nfa_utf8bytes.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa-bytes",
+ "src_path": "$ROOT$regex/tests/test_nfa_bytes.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack",
+ "src_path": "$ROOT$regex/tests/test_backtrack.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack-utf8bytes",
+ "src_path": "$ROOT$regex/tests/test_backtrack_utf8bytes.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack-bytes",
+ "src_path": "$ROOT$regex/tests/test_backtrack_bytes.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "crates-regex",
+ "src_path": "$ROOT$regex/tests/test_crates_regex.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "aho-corasick": [
+ "dep:aho-corasick"
+ ],
+ "default": [
+ "std",
+ "perf",
+ "unicode",
+ "regex-syntax/default"
+ ],
+ "memchr": [
+ "dep:memchr"
+ ],
+ "pattern": [],
+ "perf": [
+ "perf-cache",
+ "perf-dfa",
+ "perf-inline",
+ "perf-literal"
+ ],
+ "perf-cache": [],
+ "perf-dfa": [],
+ "perf-inline": [],
+ "perf-literal": [
+ "aho-corasick",
+ "memchr"
+ ],
+ "std": [],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment",
+ "regex-syntax/unicode"
+ ],
+ "unicode-age": [
+ "regex-syntax/unicode-age"
+ ],
+ "unicode-bool": [
+ "regex-syntax/unicode-bool"
+ ],
+ "unicode-case": [
+ "regex-syntax/unicode-case"
+ ],
+ "unicode-gencat": [
+ "regex-syntax/unicode-gencat"
+ ],
+ "unicode-perl": [
+ "regex-syntax/unicode-perl"
+ ],
+ "unicode-script": [
+ "regex-syntax/unicode-script"
+ ],
+ "unicode-segment": [
+ "regex-syntax/unicode-segment"
+ ],
+ "unstable": [
+ "pattern"
+ ],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$regex/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex",
+ "version": "1.8.1",
+ "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "aho-corasick",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.5.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex-syntax",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.7.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "getrandom",
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-cheat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-replace",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-single-cheat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-single",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa-utf8bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack-utf8bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "crates-regex",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "aho-corasick": [
+ "dep:aho-corasick"
+ ],
+ "default": [
+ "std",
+ "perf",
+ "unicode",
+ "regex-syntax/default"
+ ],
+ "memchr": [
+ "dep:memchr"
+ ],
+ "pattern": [],
+ "perf": [
+ "perf-cache",
+ "perf-dfa",
+ "perf-inline",
+ "perf-literal"
+ ],
+ "perf-cache": [],
+ "perf-dfa": [],
+ "perf-inline": [],
+ "perf-literal": [
+ "aho-corasick",
+ "memchr"
+ ],
+ "std": [],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment",
+ "regex-syntax/unicode"
+ ],
+ "unicode-age": [
+ "regex-syntax/unicode-age"
+ ],
+ "unicode-bool": [
+ "regex-syntax/unicode-bool"
+ ],
+ "unicode-case": [
+ "regex-syntax/unicode-case"
+ ],
+ "unicode-gencat": [
+ "regex-syntax/unicode-gencat"
+ ],
+ "unicode-perl": [
+ "regex-syntax/unicode-perl"
+ ],
+ "unicode-script": [
+ "regex-syntax/unicode-script"
+ ],
+ "unicode-segment": [
+ "regex-syntax/unicode-segment"
+ ],
+ "unstable": [
+ "pattern"
+ ],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.60.0"
+ },
+ {
+ "name": "regex-benchmark",
+ "version": "0.1.0",
+ "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Regex benchmarks for Rust's and other engines.",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "docopt",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libpcre-sys",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memmap",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "onig",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^3",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": null,
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$regex"
+ },
+ {
+ "name": "regex-syntax",
+ "source": null,
+ "req": "^0.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$regex/regex-syntax"
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "cc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "pkg-config",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.9",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "regex-run-one",
+ "src_path": "$ROOT$regex/bench/src/main.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$regex/bench/src/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$regex/bench/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "libpcre-sys": [
+ "dep:libpcre-sys"
+ ],
+ "onig": [
+ "dep:onig"
+ ],
+ "re-onig": [
+ "onig"
+ ],
+ "re-pcre1": [
+ "libpcre-sys"
+ ],
+ "re-pcre2": [],
+ "re-re2": [],
+ "re-rust": [],
+ "re-rust-bytes": [],
+ "re-tcl": []
+ },
+ "manifest_path": "$ROOT$regex/bench/Cargo.toml",
+ "metadata": null,
+ "publish": [],
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": null,
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex-debug",
+ "version": "0.1.0",
+ "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A tool useful for debugging regular expressions.",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "docopt",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": null,
+ "req": "^1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$regex"
+ },
+ {
+ "name": "regex-syntax",
+ "source": null,
+ "req": "^0.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$regex/regex-syntax"
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "regex-debug",
+ "src_path": "$ROOT$regex/regex-debug/src/main.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$regex/regex-debug/Cargo.toml",
+ "metadata": null,
+ "publish": [],
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": null,
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex-syntax",
+ "version": "0.6.28",
+ "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A regular expression parser.",
+ "source": null,
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex-syntax",
+ "src_path": "$ROOT$regex/regex-syntax/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$regex/regex-syntax/benches/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "unicode"
+ ],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ],
+ "unicode-age": [],
+ "unicode-bool": [],
+ "unicode-case": [],
+ "unicode-gencat": [],
+ "unicode-perl": [],
+ "unicode-script": [],
+ "unicode-segment": []
+ },
+ "manifest_path": "$ROOT$regex/regex-syntax/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex-syntax",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex-syntax",
+ "version": "0.7.1",
+ "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A regular expression parser.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex-syntax",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "std",
+ "unicode"
+ ],
+ "std": [],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ],
+ "unicode-age": [],
+ "unicode-bool": [],
+ "unicode-case": [],
+ "unicode-gencat": [],
+ "unicode-perl": [],
+ "unicode-script": [],
+ "unicode-segment": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "docsrs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex-syntax",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.60.0"
+ },
+ {
+ "name": "rure",
+ "version": "0.2.2",
+ "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A C API for Rust's regular expression library.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": null,
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$regex"
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "staticlib",
+ "cdylib",
+ "rlib"
+ ],
+ "crate_types": [
+ "staticlib",
+ "cdylib",
+ "rlib"
+ ],
+ "name": "rure",
+ "src_path": "$ROOT$regex/regex-capi/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$regex/regex-capi/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://github.com/rust-lang/regex/tree/master/regex-capi",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "serde",
+ "version": "1.0.160",
+ "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A generic serialization/deserialization framework",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "=1.0.160",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "serde",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "alloc": [],
+ "default": [
+ "std"
+ ],
+ "derive": [
+ "serde_derive"
+ ],
+ "rc": [],
+ "serde_derive": [
+ "dep:serde_derive"
+ ],
+ "std": [],
+ "unstable": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "derive"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "derive",
+ "rc"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Erick Tryzelaar <erick.tryzelaar@gmail.com>",
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "encoding",
+ "no-std"
+ ],
+ "keywords": [
+ "serde",
+ "serialization",
+ "no_std"
+ ],
+ "readme": "crates-io.md",
+ "repository": "https://github.com/serde-rs/serde",
+ "homepage": "https://serde.rs",
+ "documentation": "https://docs.rs/serde",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.19"
+ },
+ {
+ "name": "serde_derive",
+ "version": "1.0.160",
+ "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "proc-macro2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quote",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "syn",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.0.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "proc-macro"
+ ],
+ "crate_types": [
+ "proc-macro"
+ ],
+ "name": "serde_derive",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [],
+ "deserialize_in_place": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Erick Tryzelaar <erick.tryzelaar@gmail.com>",
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "no-std"
+ ],
+ "keywords": [
+ "serde",
+ "serialization",
+ "no_std",
+ "derive"
+ ],
+ "readme": "crates-io.md",
+ "repository": "https://github.com/serde-rs/serde",
+ "homepage": "https://serde.rs",
+ "documentation": "https://serde.rs/derive.html",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.56"
+ },
+ {
+ "name": "strsim",
+ "version": "0.10.0",
+ "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT",
+ "license_file": null,
+ "description": "Implementations of string similarity metrics. Includes Hamming, Levenshtein,\nOSA, Damerau-Levenshtein, Jaro, Jaro-Winkler, and Sørensen-Dice.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "strsim",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "lib",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/tests/lib.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "benches",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/benches/benches.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Danny Guo <danny@dannyguo.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "string",
+ "similarity",
+ "Hamming",
+ "Levenshtein",
+ "Jaro"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dguo/strsim-rs",
+ "homepage": "https://github.com/dguo/strsim-rs",
+ "documentation": "https://docs.rs/strsim/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "syn",
+ "version": "2.0.15",
+ "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Parser for Rust source code",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "proc-macro2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.55",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quote",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.25",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-ident",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "anyhow",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "automod",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "flate2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "insta",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rayon",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ref-cast",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "reqwest",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.11",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "blocking"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "syn-test-suite",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tar",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.16",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "termcolor",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "walkdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.3.2",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "syn",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "regression",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_asyncness",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_attribute",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_derive_input",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_expr",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_generics",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_grouping",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_ident",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_item",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_iterators",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_lit",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_meta",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_parse_buffer",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_parse_stream",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_pat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_path",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_precedence",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_receiver",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_round_trip",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_shebang",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_should_parse",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_stmt",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_token_trees",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_ty",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_visibility",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "zzz_stable",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "rust",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs",
+ "edition": "2021",
+ "required-features": [
+ "full",
+ "parsing"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "file",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs",
+ "edition": "2021",
+ "required-features": [
+ "full",
+ "parsing"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "clone-impls": [],
+ "default": [
+ "derive",
+ "parsing",
+ "printing",
+ "clone-impls",
+ "proc-macro"
+ ],
+ "derive": [],
+ "extra-traits": [],
+ "fold": [],
+ "full": [],
+ "parsing": [],
+ "printing": [
+ "quote"
+ ],
+ "proc-macro": [
+ "proc-macro2/proc-macro",
+ "quote/proc-macro"
+ ],
+ "quote": [
+ "dep:quote"
+ ],
+ "test": [
+ "syn-test-suite/all-features"
+ ],
+ "visit": [],
+ "visit-mut": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "doc_cfg"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "full",
+ "visit",
+ "visit-mut",
+ "fold",
+ "extra-traits"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers",
+ "parser-implementations"
+ ],
+ "keywords": [
+ "macros",
+ "syn"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/syn",
+ "homepage": null,
+ "documentation": "https://docs.rs/syn",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.56"
+ },
+ {
+ "name": "unicode-ident",
+ "version": "1.0.8",
+ "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016",
+ "license_file": null,
+ "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "criterion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "fst",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "roaring",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.10",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ucd-trie",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-xid",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "unicode-ident",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "compare",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "static_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "xid",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers",
+ "no-std"
+ ],
+ "keywords": [
+ "unicode",
+ "xid"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/unicode-ident",
+ "homepage": null,
+ "documentation": "https://docs.rs/unicode-ident",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.31"
+ },
+ {
+ "name": "wasi",
+ "version": "0.11.0+wasi-snapshot-preview1",
+ "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT",
+ "license_file": null,
+ "description": "Experimental WASI API bindings for Rust",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-alloc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "wasi",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "default": [
+ "std"
+ ],
+ "rustc-dep-of-std": [
+ "compiler_builtins",
+ "core",
+ "rustc-std-workspace-alloc"
+ ],
+ "rustc-std-workspace-alloc": [
+ "dep:rustc-std-workspace-alloc"
+ ],
+ "std": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Cranelift Project Developers"
+ ],
+ "categories": [
+ "no-std",
+ "wasm"
+ ],
+ "keywords": [
+ "webassembly",
+ "wasm"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/bytecodealliance/wasi",
+ "homepage": null,
+ "documentation": "https://docs.rs/wasi",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi",
+ "version": "0.3.9",
+ "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Raw FFI bindings for all of Windows API.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "winapi-i686-pc-windows-gnu",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "i686-pc-windows-gnu",
+ "registry": null
+ },
+ {
+ "name": "winapi-x86_64-pc-windows-gnu",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "x86_64-pc-windows-gnu",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "accctrl": [],
+ "aclapi": [],
+ "activation": [],
+ "adhoc": [],
+ "appmgmt": [],
+ "audioclient": [],
+ "audiosessiontypes": [],
+ "avrt": [],
+ "basetsd": [],
+ "bcrypt": [],
+ "bits": [],
+ "bits10_1": [],
+ "bits1_5": [],
+ "bits2_0": [],
+ "bits2_5": [],
+ "bits3_0": [],
+ "bits4_0": [],
+ "bits5_0": [],
+ "bitscfg": [],
+ "bitsmsg": [],
+ "bluetoothapis": [],
+ "bluetoothleapis": [],
+ "bthdef": [],
+ "bthioctl": [],
+ "bthledef": [],
+ "bthsdpdef": [],
+ "bugcodes": [],
+ "cderr": [],
+ "cfg": [],
+ "cfgmgr32": [],
+ "cguid": [],
+ "combaseapi": [],
+ "coml2api": [],
+ "commapi": [],
+ "commctrl": [],
+ "commdlg": [],
+ "commoncontrols": [],
+ "consoleapi": [],
+ "corecrt": [],
+ "corsym": [],
+ "d2d1": [],
+ "d2d1_1": [],
+ "d2d1_2": [],
+ "d2d1_3": [],
+ "d2d1effectauthor": [],
+ "d2d1effects": [],
+ "d2d1effects_1": [],
+ "d2d1effects_2": [],
+ "d2d1svg": [],
+ "d2dbasetypes": [],
+ "d3d": [],
+ "d3d10": [],
+ "d3d10_1": [],
+ "d3d10_1shader": [],
+ "d3d10effect": [],
+ "d3d10misc": [],
+ "d3d10sdklayers": [],
+ "d3d10shader": [],
+ "d3d11": [],
+ "d3d11_1": [],
+ "d3d11_2": [],
+ "d3d11_3": [],
+ "d3d11_4": [],
+ "d3d11on12": [],
+ "d3d11sdklayers": [],
+ "d3d11shader": [],
+ "d3d11tokenizedprogramformat": [],
+ "d3d12": [],
+ "d3d12sdklayers": [],
+ "d3d12shader": [],
+ "d3d9": [],
+ "d3d9caps": [],
+ "d3d9types": [],
+ "d3dcommon": [],
+ "d3dcompiler": [],
+ "d3dcsx": [],
+ "d3dkmdt": [],
+ "d3dkmthk": [],
+ "d3dukmdt": [],
+ "d3dx10core": [],
+ "d3dx10math": [],
+ "d3dx10mesh": [],
+ "datetimeapi": [],
+ "davclnt": [],
+ "dbghelp": [],
+ "dbt": [],
+ "dcommon": [],
+ "dcomp": [],
+ "dcompanimation": [],
+ "dcomptypes": [],
+ "dde": [],
+ "ddraw": [],
+ "ddrawi": [],
+ "ddrawint": [],
+ "debug": [
+ "impl-debug"
+ ],
+ "debugapi": [],
+ "devguid": [],
+ "devicetopology": [],
+ "devpkey": [],
+ "devpropdef": [],
+ "dinput": [],
+ "dinputd": [],
+ "dispex": [],
+ "dmksctl": [],
+ "dmusicc": [],
+ "docobj": [],
+ "documenttarget": [],
+ "dot1x": [],
+ "dpa_dsa": [],
+ "dpapi": [],
+ "dsgetdc": [],
+ "dsound": [],
+ "dsrole": [],
+ "dvp": [],
+ "dwmapi": [],
+ "dwrite": [],
+ "dwrite_1": [],
+ "dwrite_2": [],
+ "dwrite_3": [],
+ "dxdiag": [],
+ "dxfile": [],
+ "dxgi": [],
+ "dxgi1_2": [],
+ "dxgi1_3": [],
+ "dxgi1_4": [],
+ "dxgi1_5": [],
+ "dxgi1_6": [],
+ "dxgidebug": [],
+ "dxgiformat": [],
+ "dxgitype": [],
+ "dxva2api": [],
+ "dxvahd": [],
+ "eaptypes": [],
+ "enclaveapi": [],
+ "endpointvolume": [],
+ "errhandlingapi": [],
+ "everything": [],
+ "evntcons": [],
+ "evntprov": [],
+ "evntrace": [],
+ "excpt": [],
+ "exdisp": [],
+ "fibersapi": [],
+ "fileapi": [],
+ "functiondiscoverykeys_devpkey": [],
+ "gl-gl": [],
+ "guiddef": [],
+ "handleapi": [],
+ "heapapi": [],
+ "hidclass": [],
+ "hidpi": [],
+ "hidsdi": [],
+ "hidusage": [],
+ "highlevelmonitorconfigurationapi": [],
+ "hstring": [],
+ "http": [],
+ "ifdef": [],
+ "ifmib": [],
+ "imm": [],
+ "impl-debug": [],
+ "impl-default": [],
+ "in6addr": [],
+ "inaddr": [],
+ "inspectable": [],
+ "interlockedapi": [],
+ "intsafe": [],
+ "ioapiset": [],
+ "ipexport": [],
+ "iphlpapi": [],
+ "ipifcons": [],
+ "ipmib": [],
+ "iprtrmib": [],
+ "iptypes": [],
+ "jobapi": [],
+ "jobapi2": [],
+ "knownfolders": [],
+ "ks": [],
+ "ksmedia": [],
+ "ktmtypes": [],
+ "ktmw32": [],
+ "l2cmn": [],
+ "libloaderapi": [],
+ "limits": [],
+ "lmaccess": [],
+ "lmalert": [],
+ "lmapibuf": [],
+ "lmat": [],
+ "lmcons": [],
+ "lmdfs": [],
+ "lmerrlog": [],
+ "lmjoin": [],
+ "lmmsg": [],
+ "lmremutl": [],
+ "lmrepl": [],
+ "lmserver": [],
+ "lmshare": [],
+ "lmstats": [],
+ "lmsvc": [],
+ "lmuse": [],
+ "lmwksta": [],
+ "lowlevelmonitorconfigurationapi": [],
+ "lsalookup": [],
+ "memoryapi": [],
+ "minschannel": [],
+ "minwinbase": [],
+ "minwindef": [],
+ "mmdeviceapi": [],
+ "mmeapi": [],
+ "mmreg": [],
+ "mmsystem": [],
+ "mprapidef": [],
+ "msaatext": [],
+ "mscat": [],
+ "mschapp": [],
+ "mssip": [],
+ "mstcpip": [],
+ "mswsock": [],
+ "mswsockdef": [],
+ "namedpipeapi": [],
+ "namespaceapi": [],
+ "nb30": [],
+ "ncrypt": [],
+ "netioapi": [],
+ "nldef": [],
+ "ntddndis": [],
+ "ntddscsi": [],
+ "ntddser": [],
+ "ntdef": [],
+ "ntlsa": [],
+ "ntsecapi": [],
+ "ntstatus": [],
+ "oaidl": [],
+ "objbase": [],
+ "objidl": [],
+ "objidlbase": [],
+ "ocidl": [],
+ "ole2": [],
+ "oleauto": [],
+ "olectl": [],
+ "oleidl": [],
+ "opmapi": [],
+ "pdh": [],
+ "perflib": [],
+ "physicalmonitorenumerationapi": [],
+ "playsoundapi": [],
+ "portabledevice": [],
+ "portabledeviceapi": [],
+ "portabledevicetypes": [],
+ "powerbase": [],
+ "powersetting": [],
+ "powrprof": [],
+ "processenv": [],
+ "processsnapshot": [],
+ "processthreadsapi": [],
+ "processtopologyapi": [],
+ "profileapi": [],
+ "propidl": [],
+ "propkey": [],
+ "propkeydef": [],
+ "propsys": [],
+ "prsht": [],
+ "psapi": [],
+ "qos": [],
+ "realtimeapiset": [],
+ "reason": [],
+ "restartmanager": [],
+ "restrictederrorinfo": [],
+ "rmxfguid": [],
+ "roapi": [],
+ "robuffer": [],
+ "roerrorapi": [],
+ "rpc": [],
+ "rpcdce": [],
+ "rpcndr": [],
+ "rtinfo": [],
+ "sapi": [],
+ "sapi51": [],
+ "sapi53": [],
+ "sapiddk": [],
+ "sapiddk51": [],
+ "schannel": [],
+ "sddl": [],
+ "securityappcontainer": [],
+ "securitybaseapi": [],
+ "servprov": [],
+ "setupapi": [],
+ "shellapi": [],
+ "shellscalingapi": [],
+ "shlobj": [],
+ "shobjidl": [],
+ "shobjidl_core": [],
+ "shtypes": [],
+ "softpub": [],
+ "spapidef": [],
+ "spellcheck": [],
+ "sporder": [],
+ "sql": [],
+ "sqlext": [],
+ "sqltypes": [],
+ "sqlucode": [],
+ "sspi": [],
+ "std": [],
+ "stralign": [],
+ "stringapiset": [],
+ "strmif": [],
+ "subauth": [],
+ "synchapi": [],
+ "sysinfoapi": [],
+ "systemtopologyapi": [],
+ "taskschd": [],
+ "tcpestats": [],
+ "tcpmib": [],
+ "textstor": [],
+ "threadpoolapiset": [],
+ "threadpoollegacyapiset": [],
+ "timeapi": [],
+ "timezoneapi": [],
+ "tlhelp32": [],
+ "transportsettingcommon": [],
+ "tvout": [],
+ "udpmib": [],
+ "unknwnbase": [],
+ "urlhist": [],
+ "urlmon": [],
+ "usb": [],
+ "usbioctl": [],
+ "usbiodef": [],
+ "usbscan": [],
+ "usbspec": [],
+ "userenv": [],
+ "usp10": [],
+ "utilapiset": [],
+ "uxtheme": [],
+ "vadefs": [],
+ "vcruntime": [],
+ "vsbackup": [],
+ "vss": [],
+ "vsserror": [],
+ "vswriter": [],
+ "wbemads": [],
+ "wbemcli": [],
+ "wbemdisp": [],
+ "wbemprov": [],
+ "wbemtran": [],
+ "wct": [],
+ "werapi": [],
+ "winbase": [],
+ "wincodec": [],
+ "wincodecsdk": [],
+ "wincon": [],
+ "wincontypes": [],
+ "wincred": [],
+ "wincrypt": [],
+ "windef": [],
+ "windot11": [],
+ "windowsceip": [],
+ "windowsx": [],
+ "winefs": [],
+ "winerror": [],
+ "winevt": [],
+ "wingdi": [],
+ "winhttp": [],
+ "wininet": [],
+ "winineti": [],
+ "winioctl": [],
+ "winnetwk": [],
+ "winnls": [],
+ "winnt": [],
+ "winreg": [],
+ "winsafer": [],
+ "winscard": [],
+ "winsmcrd": [],
+ "winsock2": [],
+ "winspool": [],
+ "winstring": [],
+ "winsvc": [],
+ "wintrust": [],
+ "winusb": [],
+ "winusbio": [],
+ "winuser": [],
+ "winver": [],
+ "wlanapi": [],
+ "wlanihv": [],
+ "wlanihvtypes": [],
+ "wlantypes": [],
+ "wlclient": [],
+ "wmistr": [],
+ "wnnc": [],
+ "wow64apiset": [],
+ "wpdmtpextensions": [],
+ "ws2bth": [],
+ "ws2def": [],
+ "ws2ipdef": [],
+ "ws2spi": [],
+ "ws2tcpip": [],
+ "wtsapi32": [],
+ "wtypes": [],
+ "wtypesbase": [],
+ "xinput": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "default-target": "x86_64-pc-windows-msvc",
+ "features": [
+ "everything",
+ "impl-debug",
+ "impl-default"
+ ],
+ "targets": [
+ "aarch64-pc-windows-msvc",
+ "i686-pc-windows-msvc",
+ "x86_64-pc-windows-msvc"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Peter Atashian <retep998@gmail.com>"
+ ],
+ "categories": [
+ "external-ffi-bindings",
+ "no-std",
+ "os::windows-apis"
+ ],
+ "keywords": [
+ "windows",
+ "ffi",
+ "win32",
+ "com",
+ "directx"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/retep998/winapi-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/winapi/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi-i686-pc-windows-gnu",
+ "version": "0.4.0",
+ "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi-i686-pc-windows-gnu",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Peter Atashian <retep998@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "windows"
+ ],
+ "readme": null,
+ "repository": "https://github.com/retep998/winapi-rs",
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi-x86_64-pc-windows-gnu",
+ "version": "0.4.0",
+ "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi-x86_64-pc-windows-gnu",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Peter Atashian <retep998@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "windows"
+ ],
+ "readme": null,
+ "repository": "https://github.com/retep998/winapi-rs",
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ }
+ ],
+ "workspace_members": [
+ "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)",
+ "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)",
+ "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)"
+ ],
+ "resolve": {
+ "nodes": [
+ {
+ "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "strsim",
+ "pkg": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(unix)"
+ }
+ ]
+ },
+ {
+ "name": "wasi",
+ "pkg": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(target_os = \"wasi\")"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(unix)"
+ }
+ ]
+ },
+ {
+ "name": "winapi",
+ "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "unicode_ident",
+ "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "proc-macro"
+ ]
+ },
+ {
+ "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "rand",
+ "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "proc_macro2",
+ "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "proc-macro"
+ ]
+ },
+ {
+ "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "rand_core",
+ "pkg": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "getrandom",
+ "small_rng"
+ ]
+ },
+ {
+ "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "getrandom",
+ "pkg": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "getrandom"
+ ]
+ },
+ {
+ "id": "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "dependencies": [
+ "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)"
+ ],
+ "deps": [
+ {
+ "name": "aho_corasick",
+ "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "quickcheck",
+ "pkg": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "rand",
+ "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex_syntax",
+ "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "aho-corasick",
+ "default",
+ "memchr",
+ "perf",
+ "perf-cache",
+ "perf-dfa",
+ "perf-inline",
+ "perf-literal",
+ "std",
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "regex_syntax",
+ "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "std",
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)",
+ "dependencies": [
+ "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cc",
+ "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "docopt",
+ "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "memmap",
+ "pkg": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "pkg_config",
+ "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex_syntax",
+ "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)",
+ "dependencies": [
+ "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "docopt",
+ "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex_syntax",
+ "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)",
+ "dependencies": [
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.7.1 (path+file:///$ROOT$regex)"
+ ],
+ "deps": [
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "serde_derive",
+ "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "derive",
+ "serde_derive",
+ "std"
+ ]
+ },
+ {
+ "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "proc_macro2",
+ "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "quote",
+ "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "syn",
+ "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default"
+ ]
+ },
+ {
+ "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "proc_macro2",
+ "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "quote",
+ "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "unicode_ident",
+ "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "clone-impls",
+ "default",
+ "derive",
+ "parsing",
+ "printing",
+ "proc-macro",
+ "quote"
+ ]
+ },
+ {
+ "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "winapi_i686_pc_windows_gnu",
+ "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "i686-pc-windows-gnu"
+ }
+ ]
+ },
+ {
+ "name": "winapi_x86_64_pc_windows_gnu",
+ "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "x86_64-pc-windows-gnu"
+ }
+ ]
+ }
+ ],
+ "features": [
+ "basetsd",
+ "handleapi",
+ "memoryapi",
+ "minwindef",
+ "std",
+ "sysinfoapi"
+ ]
+ },
+ {
+ "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ }
+ ],
+ "root": "regex 1.7.1 (path+file:///$ROOT$regex)"
+ },
+ "target_directory": "$ROOT$regex/target",
+ "version": 1,
+ "workspace_root": "$ROOT$regex",
+ "metadata": null
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json
new file mode 100644
index 000000000..131ff5dd7
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json
@@ -0,0 +1,12816 @@
+{
+ "packages": [
+ {
+ "name": "aho-corasick",
+ "version": "0.7.20",
+ "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Fast multiple substring searching.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "aho_corasick",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "default": [
+ "std"
+ ],
+ "std": [
+ "memchr/std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [
+ "string",
+ "search",
+ "text",
+ "aho",
+ "multi"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/aho-corasick",
+ "homepage": "https://github.com/BurntSushi/aho-corasick",
+ "documentation": null,
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "aho-corasick",
+ "version": "1.0.1",
+ "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Fast multiple substring searching.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.17",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "doc-comment",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "aho_corasick",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "default": [
+ "std",
+ "perf-literal"
+ ],
+ "logging": [
+ "dep:log"
+ ],
+ "perf-literal": [
+ "dep:memchr"
+ ],
+ "std": [
+ "memchr?/std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "docsrs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [
+ "string",
+ "search",
+ "text",
+ "pattern",
+ "multi"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/aho-corasick",
+ "homepage": "https://github.com/BurntSushi/aho-corasick",
+ "documentation": null,
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.60.0"
+ },
+ {
+ "name": "atty",
+ "version": "0.2.14",
+ "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT",
+ "license_file": null,
+ "description": "A simple interface for querying atty",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "hermit-abi",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(target_os = \"hermit\")",
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": "cfg(unix)",
+ "registry": null
+ },
+ {
+ "name": "winapi",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "consoleapi",
+ "processenv",
+ "minwinbase",
+ "minwindef",
+ "winbase"
+ ],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "atty",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "atty",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/examples/atty.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "softprops <d.tangren@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "terminal",
+ "tty",
+ "isatty"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/softprops/atty",
+ "homepage": "https://github.com/softprops/atty",
+ "documentation": "http://softprops.github.io/atty",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "base64",
+ "version": "0.20.0",
+ "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "encodes and decodes base64 as bytes or utf8",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "criterion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.5",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rstest",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.12.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rstest_reuse",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "structopt",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.26",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "base64",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "base64",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/examples/base64.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "encode",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/encode.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "tests",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/tests.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "benchmarks",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/benches/benchmarks.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "alloc": [],
+ "default": [
+ "std"
+ ],
+ "std": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alice Maz <alice@alicemaz.com>",
+ "Marshall Pierce <marshall@mpierce.org>"
+ ],
+ "categories": [
+ "encoding"
+ ],
+ "keywords": [
+ "base64",
+ "utf8",
+ "encode",
+ "decode",
+ "no_std"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/marshallpierce/rust-base64",
+ "homepage": null,
+ "documentation": "https://docs.rs/base64",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.57.0"
+ },
+ {
+ "name": "bitflags",
+ "version": "1.3.2",
+ "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A macro to generate structures which behave like bitflags.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_json",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "trybuild",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "walkdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "bitflags",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "basic",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/basic.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "compile",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/compile.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "default": [],
+ "example_generated": [],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "example_generated"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "no-std"
+ ],
+ "keywords": [
+ "bit",
+ "bitmask",
+ "bitflags",
+ "flags"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/bitflags/bitflags",
+ "homepage": "https://github.com/bitflags/bitflags",
+ "documentation": "https://docs.rs/bitflags",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "bstr",
+ "version": "1.4.0",
+ "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A string type that is not required to be valid UTF-8.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "once_cell",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.14.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex-automata",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.5",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.85",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ucd-parse",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-segmentation",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.2.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "bstr",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "graphemes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes.rs",
+ "edition": "2021",
+ "required-features": [
+ "std",
+ "unicode"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "lines",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "uppercase",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase.rs",
+ "edition": "2021",
+ "required-features": [
+ "std",
+ "unicode"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "words",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words.rs",
+ "edition": "2021",
+ "required-features": [
+ "std",
+ "unicode"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "graphemes-std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes-std.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "lines-std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines-std.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "uppercase-std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase-std.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "words-std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words-std.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "alloc": [
+ "serde?/alloc"
+ ],
+ "default": [
+ "std",
+ "unicode"
+ ],
+ "serde": [
+ "dep:serde"
+ ],
+ "std": [
+ "alloc",
+ "memchr/std",
+ "serde?/std"
+ ],
+ "unicode": [
+ "dep:once_cell",
+ "dep:regex-automata"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "docsrs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing",
+ "encoding"
+ ],
+ "keywords": [
+ "string",
+ "str",
+ "byte",
+ "bytes",
+ "text"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/bstr",
+ "homepage": "https://github.com/BurntSushi/bstr",
+ "documentation": "https://docs.rs/bstr",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.60"
+ },
+ {
+ "name": "bytecount",
+ "version": "0.6.3",
+ "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Apache-2.0/MIT",
+ "license_file": null,
+ "description": "count occurrences of a given byte, or the number of UTF-8 code points, in a byte slice, fast",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "packed_simd_2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.8",
+ "kind": null,
+ "rename": "packed_simd",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "criterion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "bytecount",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "check",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/tests/check.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/benches/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "generic-simd": [
+ "packed_simd"
+ ],
+ "html_report": [],
+ "packed_simd": [
+ "dep:packed_simd"
+ ],
+ "runtime-dispatch-simd": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andre Bogus <bogusandre@gmail.de>",
+ "Joshua Landau <joshua@landau.ws>"
+ ],
+ "categories": [
+ "algorithms",
+ "no-std"
+ ],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/llogiq/bytecount",
+ "homepage": null,
+ "documentation": null,
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "cc",
+ "version": "1.0.79",
+ "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "jobserver",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.16",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tempfile",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "cc",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "gcc-shim",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cc_env",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cflags",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cxxflags",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "jobserver": [
+ "dep:jobserver"
+ ],
+ "parallel": [
+ "jobserver"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [
+ "development-tools::build-utils"
+ ],
+ "keywords": [
+ "build-dependencies"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/cc-rs",
+ "homepage": "https://github.com/rust-lang/cc-rs",
+ "documentation": "https://docs.rs/cc",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "cfg-if",
+ "version": "1.0.0",
+ "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "cfg-if",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "xcrate",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/alexcrichton/cfg-if",
+ "homepage": "https://github.com/alexcrichton/cfg-if",
+ "documentation": "https://docs.rs/cfg-if",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "clap",
+ "version": "2.34.0",
+ "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT",
+ "license_file": null,
+ "description": "A simple to use, efficient, and full-featured Command Line Argument Parser\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "atty",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bitflags",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "clippy",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "~0.0.166",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "strsim",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "term_size",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "textwrap",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.11.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-width",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "vec_map",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "yaml-rust",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.5",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "version-sync",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ansi_term",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.12",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(not(windows))",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "clap",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "ansi_term": [
+ "dep:ansi_term"
+ ],
+ "atty": [
+ "dep:atty"
+ ],
+ "clippy": [
+ "dep:clippy"
+ ],
+ "color": [
+ "ansi_term",
+ "atty"
+ ],
+ "debug": [],
+ "default": [
+ "suggestions",
+ "color",
+ "vec_map"
+ ],
+ "doc": [
+ "yaml"
+ ],
+ "nightly": [],
+ "no_cargo": [],
+ "strsim": [
+ "dep:strsim"
+ ],
+ "suggestions": [
+ "strsim"
+ ],
+ "term_size": [
+ "dep:term_size"
+ ],
+ "unstable": [],
+ "vec_map": [
+ "dep:vec_map"
+ ],
+ "wrap_help": [
+ "term_size",
+ "textwrap/term_size"
+ ],
+ "yaml": [
+ "yaml-rust"
+ ],
+ "yaml-rust": [
+ "dep:yaml-rust"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "doc"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Kevin K. <kbknapp@gmail.com>"
+ ],
+ "categories": [
+ "command-line-interface"
+ ],
+ "keywords": [
+ "argument",
+ "cli",
+ "arg",
+ "parser",
+ "parse"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/clap-rs/clap",
+ "homepage": "https://clap.rs/",
+ "documentation": "https://docs.rs/clap/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "crossbeam-channel",
+ "version": "0.5.8",
+ "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Multi-producer multi-consumer channels for message passing",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "crossbeam-utils",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "num_cpus",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.13.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "signal-hook",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "crossbeam-channel",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "fibonacci",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/fibonacci.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "matching",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/matching.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "stopwatch",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/stopwatch.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "after",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/after.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "array",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/array.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "golang",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/golang.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "iter",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/iter.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "list",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/list.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "mpsc",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/mpsc.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "never",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/never.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "ready",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/ready.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "same_channel",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/same_channel.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "select",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "select_macro",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select_macro.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "thread_locals",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/thread_locals.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "tick",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/tick.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "zero",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/zero.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "crossbeam",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/benches/crossbeam.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "crossbeam-utils": [
+ "dep:crossbeam-utils"
+ ],
+ "default": [
+ "std"
+ ],
+ "std": [
+ "crossbeam-utils/std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [],
+ "categories": [
+ "algorithms",
+ "concurrency",
+ "data-structures"
+ ],
+ "keywords": [
+ "channel",
+ "mpmc",
+ "select",
+ "golang",
+ "message"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/crossbeam-rs/crossbeam",
+ "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel",
+ "documentation": null,
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.38"
+ },
+ {
+ "name": "crossbeam-utils",
+ "version": "0.8.15",
+ "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Utilities for concurrent programming",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "loom",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(crossbeam_loom)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "crossbeam-utils",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "atomic_cell",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/atomic_cell.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cache_padded",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/cache_padded.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "parker",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/parker.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "sharded_lock",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/sharded_lock.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "thread",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/thread.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "wait_group",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/wait_group.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "atomic_cell",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/benches/atomic_cell.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "std"
+ ],
+ "loom": [
+ "dep:loom"
+ ],
+ "nightly": [],
+ "std": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [],
+ "categories": [
+ "algorithms",
+ "concurrency",
+ "data-structures",
+ "no-std"
+ ],
+ "keywords": [
+ "scoped",
+ "thread",
+ "atomic",
+ "cache"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/crossbeam-rs/crossbeam",
+ "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils",
+ "documentation": null,
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.38"
+ },
+ {
+ "name": "encoding_rs",
+ "version": "0.8.32",
+ "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "(Apache-2.0 OR MIT) AND BSD-3-Clause",
+ "license_file": null,
+ "description": "A Gecko-oriented implementation of the Encoding Standard",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "packed_simd_2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.4",
+ "kind": null,
+ "rename": "packed_simd",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bincode",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_json",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "encoding_rs",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "alloc": [],
+ "default": [
+ "alloc"
+ ],
+ "fast-big5-hanzi-encode": [],
+ "fast-gb-hanzi-encode": [],
+ "fast-hangul-encode": [],
+ "fast-hanja-encode": [],
+ "fast-kanji-encode": [],
+ "fast-legacy-encode": [
+ "fast-hangul-encode",
+ "fast-hanja-encode",
+ "fast-kanji-encode",
+ "fast-gb-hanzi-encode",
+ "fast-big5-hanzi-encode"
+ ],
+ "less-slow-big5-hanzi-encode": [],
+ "less-slow-gb-hanzi-encode": [],
+ "less-slow-kanji-encode": [],
+ "packed_simd": [
+ "dep:packed_simd"
+ ],
+ "serde": [
+ "dep:serde"
+ ],
+ "simd-accel": [
+ "packed_simd",
+ "packed_simd/into_bits"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Henri Sivonen <hsivonen@hsivonen.fi>"
+ ],
+ "categories": [
+ "text-processing",
+ "encoding",
+ "web-programming",
+ "internationalization"
+ ],
+ "keywords": [
+ "encoding",
+ "web",
+ "unicode",
+ "charset"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/hsivonen/encoding_rs",
+ "homepage": "https://docs.rs/encoding_rs/",
+ "documentation": "https://docs.rs/encoding_rs/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "encoding_rs_io",
+ "version": "0.1.7",
+ "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Streaming transcoding for encoding_rs",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "encoding_rs",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "encoding_rs_io",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing",
+ "encoding",
+ "web-programming",
+ "email"
+ ],
+ "keywords": [
+ "encoding",
+ "transcoding",
+ "stream",
+ "io",
+ "read"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/encoding_rs_io",
+ "homepage": null,
+ "documentation": "https://docs.rs/encoding_rs_io",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "fnv",
+ "version": "1.0.7",
+ "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Apache-2.0 / MIT",
+ "license_file": null,
+ "description": "Fowler–Noll–Vo hash function",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "fnv",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "default": [
+ "std"
+ ],
+ "std": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/servo/rust-fnv",
+ "homepage": null,
+ "documentation": "https://doc.servo.org/fnv/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "glob",
+ "version": "0.3.1",
+ "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Support for matching file paths against Unix shell style patterns.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "doc-comment",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tempdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "glob",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "glob-std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/tests/glob-std.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "filesystem"
+ ],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/glob",
+ "homepage": "https://github.com/rust-lang/glob",
+ "documentation": "https://docs.rs/glob/0.3.1",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "globset",
+ "version": "0.4.10",
+ "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Cross platform single glob and glob set matching. Glob set matching is the\nprocess of matching one or more glob patterns against a single candidate path\nsimultaneously, and returning all of the globs that matched.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "aho-corasick",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.7.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "std"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "fnv",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "perf",
+ "std"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.104",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "glob",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_json",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.45",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "globset",
+ "src_path": "$ROOT$ripgrep/crates/globset/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$ripgrep/crates/globset/benches/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "log"
+ ],
+ "log": [
+ "dep:log"
+ ],
+ "serde": [
+ "dep:serde"
+ ],
+ "serde1": [
+ "serde"
+ ],
+ "simd-accel": []
+ },
+ "manifest_path": "$ROOT$ripgrep/crates/globset/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "glob",
+ "multiple",
+ "set",
+ "pattern"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset",
+ "documentation": "https://docs.rs/globset",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep",
+ "version": "0.2.11",
+ "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Fast line oriented regex searching as a library.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "grep-cli",
+ "source": null,
+ "req": "^0.1.7",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/cli"
+ },
+ {
+ "name": "grep-matcher",
+ "source": null,
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/matcher"
+ },
+ {
+ "name": "grep-pcre2",
+ "source": null,
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/pcre2"
+ },
+ {
+ "name": "grep-printer",
+ "source": null,
+ "req": "^0.1.7",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/printer"
+ },
+ {
+ "name": "grep-regex",
+ "source": null,
+ "req": "^0.1.11",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/regex"
+ },
+ {
+ "name": "grep-searcher",
+ "source": null,
+ "req": "^0.1.11",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/searcher"
+ },
+ {
+ "name": "termcolor",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "walkdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.2.7",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep",
+ "src_path": "$ROOT$ripgrep/crates/grep/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "simplegrep",
+ "src_path": "$ROOT$ripgrep/crates/grep/examples/simplegrep.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "avx-accel": [],
+ "grep-pcre2": [
+ "dep:grep-pcre2"
+ ],
+ "pcre2": [
+ "grep-pcre2"
+ ],
+ "simd-accel": [
+ "grep-searcher/simd-accel"
+ ]
+ },
+ "manifest_path": "$ROOT$ripgrep/crates/grep/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "grep",
+ "egrep",
+ "search",
+ "pattern"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep",
+ "documentation": "https://docs.rs/grep",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep-cli",
+ "version": "0.1.7",
+ "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Utilities for search oriented command line applications.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "atty",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.11",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "globset",
+ "source": null,
+ "req": "^0.4.10",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/globset"
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "same-file",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "termcolor",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "winapi-util",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep-cli",
+ "src_path": "$ROOT$ripgrep/crates/cli/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$ripgrep/crates/cli/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "grep",
+ "cli",
+ "utility",
+ "util"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli",
+ "documentation": "https://docs.rs/grep-cli",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep-matcher",
+ "version": "0.1.6",
+ "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "A trait for regular expressions, with a focus on line oriented search.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep-matcher",
+ "src_path": "$ROOT$ripgrep/crates/matcher/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "integration",
+ "src_path": "$ROOT$ripgrep/crates/matcher/tests/tests.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$ripgrep/crates/matcher/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "pattern",
+ "trait"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher",
+ "documentation": "https://docs.rs/grep-matcher",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep-pcre2",
+ "version": "0.1.6",
+ "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Use PCRE2 with the 'grep' crate.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "grep-matcher",
+ "source": null,
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/matcher"
+ },
+ {
+ "name": "pcre2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep-pcre2",
+ "src_path": "$ROOT$ripgrep/crates/pcre2/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$ripgrep/crates/pcre2/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "grep",
+ "pcre",
+ "backreference",
+ "look"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2",
+ "documentation": "https://docs.rs/grep-pcre2",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep-printer",
+ "version": "0.1.7",
+ "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "An implementation of the grep crate's Sink trait that provides standard\nprinting of search results, similar to grep itself.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "base64",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.20.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "grep-matcher",
+ "source": null,
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/matcher"
+ },
+ {
+ "name": "grep-searcher",
+ "source": null,
+ "req": "^0.1.11",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/searcher"
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.77",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_json",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.27",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "termcolor",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "grep-regex",
+ "source": null,
+ "req": "^0.1.11",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/regex"
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep-printer",
+ "src_path": "$ROOT$ripgrep/crates/printer/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "base64": [
+ "dep:base64"
+ ],
+ "default": [
+ "serde1"
+ ],
+ "serde": [
+ "dep:serde"
+ ],
+ "serde1": [
+ "base64",
+ "serde",
+ "serde_json"
+ ],
+ "serde_json": [
+ "dep:serde_json"
+ ]
+ },
+ "manifest_path": "$ROOT$ripgrep/crates/printer/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "grep",
+ "pattern",
+ "print",
+ "printer",
+ "sink"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer",
+ "documentation": "https://docs.rs/grep-printer",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep-regex",
+ "version": "0.1.11",
+ "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Use Rust's regex library with the 'grep' crate.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "aho-corasick",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.7.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "grep-matcher",
+ "source": null,
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/matcher"
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex-syntax",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "thread_local",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep-regex",
+ "src_path": "$ROOT$ripgrep/crates/regex/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$ripgrep/crates/regex/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "grep",
+ "search",
+ "pattern",
+ "line"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex",
+ "documentation": "https://docs.rs/grep-regex",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "grep-searcher",
+ "version": "0.1.11",
+ "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "Fast line oriented regex searching as a library.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "std"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bytecount",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "encoding_rs",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.14",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "encoding_rs_io",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "grep-matcher",
+ "source": null,
+ "req": "^0.1.6",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/matcher"
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memmap2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5.3",
+ "kind": null,
+ "rename": "memmap",
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "grep-regex",
+ "source": null,
+ "req": "^0.1.11",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/regex"
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "grep-searcher",
+ "src_path": "$ROOT$ripgrep/crates/searcher/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "search-stdin",
+ "src_path": "$ROOT$ripgrep/crates/searcher/examples/search-stdin.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "avx-accel": [],
+ "default": [
+ "bytecount/runtime-dispatch-simd"
+ ],
+ "simd-accel": [
+ "encoding_rs/simd-accel"
+ ]
+ },
+ "manifest_path": "$ROOT$ripgrep/crates/searcher/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "regex",
+ "grep",
+ "egrep",
+ "search",
+ "pattern"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher",
+ "documentation": "https://docs.rs/grep-searcher",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "hermit-abi",
+ "version": "0.1.19",
+ "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "hermit-abi is small interface to call functions from the unikernel RustyHermit.\nIt is used to build the target `x86_64-unknown-hermit`.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.51",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "hermit-abi",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "default": [],
+ "docs": [],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins/rustc-dep-of-std",
+ "libc/rustc-dep-of-std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "default-target": "x86_64-unknown-hermit",
+ "features": [
+ "docs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Stefan Lankes"
+ ],
+ "categories": [
+ "os"
+ ],
+ "keywords": [
+ "unikernel",
+ "libos"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/hermitcore/libhermit-rs",
+ "homepage": null,
+ "documentation": "https://hermitcore.github.io/rusty-hermit/hermit_abi",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "ignore",
+ "version": "0.4.20",
+ "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "A fast library for efficiently matching ignore files such as `.gitignore`\nagainst file paths.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "globset",
+ "source": null,
+ "req": "^0.4.10",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/globset"
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "same-file",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "thread_local",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "walkdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.2.7",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "crossbeam-channel",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "winapi-util",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "ignore",
+ "src_path": "$ROOT$ripgrep/crates/ignore/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "walk",
+ "src_path": "$ROOT$ripgrep/crates/ignore/examples/walk.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "gitignore_matched_path_or_any_parents_tests",
+ "src_path": "$ROOT$ripgrep/crates/ignore/tests/gitignore_matched_path_or_any_parents_tests.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "simd-accel": [
+ "globset/simd-accel"
+ ]
+ },
+ "manifest_path": "$ROOT$ripgrep/crates/ignore/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "glob",
+ "ignore",
+ "gitignore",
+ "pattern",
+ "file"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore",
+ "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore",
+ "documentation": "https://docs.rs/ignore",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "itoa",
+ "version": "1.0.6",
+ "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Fast integer primitive to string conversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "no-panic",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "itoa",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/benches/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "no-panic": [
+ "dep:no-panic"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "value-formatting",
+ "no-std"
+ ],
+ "keywords": [
+ "integer"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/itoa",
+ "homepage": null,
+ "documentation": "https://docs.rs/itoa",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.36"
+ },
+ {
+ "name": "jemalloc-sys",
+ "version": "0.5.3+5.3.0-patched",
+ "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Rust FFI bindings to jemalloc\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.8",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "cc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.13",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "jemalloc-sys",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "malloc_conf_empty",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_empty.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "malloc_conf_set",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_set.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "unprefixed_malloc",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/unprefixed_malloc.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "background_threads": [
+ "background_threads_runtime_support"
+ ],
+ "background_threads_runtime_support": [],
+ "debug": [],
+ "default": [
+ "background_threads_runtime_support"
+ ],
+ "disable_initial_exec_tls": [],
+ "profiling": [],
+ "stats": [],
+ "unprefixed_malloc_on_supported_platforms": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "rustdoc-args": [
+ "--cfg",
+ "jemallocator_docs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>",
+ "Gonzalo Brito Gadeschi <gonzalobg88@gmail.com>",
+ "The TiKV Project Developers"
+ ],
+ "categories": [],
+ "keywords": [
+ "allocator",
+ "jemalloc"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/tikv/jemallocator",
+ "homepage": "https://github.com/tikv/jemallocator",
+ "documentation": "https://docs.rs/jemallocator-sys",
+ "edition": "2018",
+ "links": "jemalloc",
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "jemallocator",
+ "version": "0.5.0",
+ "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A Rust allocator backed by jemalloc\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "jemalloc-sys",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.8",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "paste",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "jemallocator",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "background_thread_defaults",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_defaults.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "background_thread_enabled",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_enabled.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "ffi",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/ffi.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "grow_in_place",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/grow_in_place.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "malloctl",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/malloctl.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shrink_in_place",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/shrink_in_place.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "smoke",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "smoke_ffi",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke_ffi.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "usable_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/usable_size.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "roundtrip",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/benches/roundtrip.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "alloc_trait": [],
+ "background_threads": [
+ "jemalloc-sys/background_threads"
+ ],
+ "background_threads_runtime_support": [
+ "jemalloc-sys/background_threads_runtime_support"
+ ],
+ "debug": [
+ "jemalloc-sys/debug"
+ ],
+ "default": [
+ "background_threads_runtime_support"
+ ],
+ "disable_initial_exec_tls": [
+ "jemalloc-sys/disable_initial_exec_tls"
+ ],
+ "profiling": [
+ "jemalloc-sys/profiling"
+ ],
+ "stats": [
+ "jemalloc-sys/stats"
+ ],
+ "unprefixed_malloc_on_supported_platforms": [
+ "jemalloc-sys/unprefixed_malloc_on_supported_platforms"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [],
+ "rustdoc-args": [
+ "--cfg",
+ "jemallocator_docs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>",
+ "Gonzalo Brito Gadeschi <gonzalobg88@gmail.com>",
+ "Simon Sapin <simon.sapin@exyr.org>",
+ "Steven Fackler <sfackler@gmail.com>",
+ "The TiKV Project Developers"
+ ],
+ "categories": [
+ "memory-management",
+ "api-bindings"
+ ],
+ "keywords": [
+ "allocator",
+ "jemalloc"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/tikv/jemallocator",
+ "homepage": "https://github.com/tikv/jemallocator",
+ "documentation": "https://docs.rs/jemallocator",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "jobserver",
+ "version": "0.1.26",
+ "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "An implementation of the GNU make jobserver for Rust\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "futures",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "num_cpus",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tempfile",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tokio-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tokio-process",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.50",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(unix)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "jobserver",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "client",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "server",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/server.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "client-of-myself",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client-of-myself.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "make-as-a-client",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/make-as-a-client.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "helper",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/helper.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/alexcrichton/jobserver-rs",
+ "homepage": "https://github.com/alexcrichton/jobserver-rs",
+ "documentation": "https://docs.rs/jobserver",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "lazy_static",
+ "version": "1.4.0",
+ "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "A macro for declaring lazily evaluated statics in Rust.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "spin",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "doc-comment",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "lazy_static",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "no_std",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "spin": [
+ "dep:spin"
+ ],
+ "spin_no_std": [
+ "spin"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Marvin Löbel <loebel.marvin@gmail.com>"
+ ],
+ "categories": [
+ "no-std",
+ "rust-patterns",
+ "memory-management"
+ ],
+ "keywords": [
+ "macro",
+ "lazy",
+ "static"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang-nursery/lazy-static.rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/lazy_static",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "libc",
+ "version": "0.2.142",
+ "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Raw FFI bindings to platform libraries like libc.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "libc",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "const_fn",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "align": [],
+ "const-extern-fn": [],
+ "default": [
+ "std"
+ ],
+ "extra_traits": [],
+ "rustc-dep-of-std": [
+ "align",
+ "rustc-std-workspace-core"
+ ],
+ "rustc-std-workspace-core": [
+ "dep:rustc-std-workspace-core"
+ ],
+ "std": [],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "const-extern-fn",
+ "extra_traits"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "external-ffi-bindings",
+ "no-std",
+ "os"
+ ],
+ "keywords": [
+ "libc",
+ "ffi",
+ "bindings",
+ "operating",
+ "system"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/libc",
+ "homepage": "https://github.com/rust-lang/libc",
+ "documentation": "https://docs.rs/libc/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "log",
+ "version": "0.4.17",
+ "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A lightweight logging facade for Rust\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "sval",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "=1.0.0-alpha.5",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "value-bag",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "=1.0.0-alpha.9",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_test",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "sval",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "=1.0.0-alpha.5",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "value-bag",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "=1.0.0-alpha.9",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "test"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "log",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "filters",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/filters.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "macros",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/macros.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "value",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/benches/value.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "kv_unstable": [
+ "value-bag"
+ ],
+ "kv_unstable_serde": [
+ "kv_unstable_std",
+ "value-bag/serde",
+ "serde"
+ ],
+ "kv_unstable_std": [
+ "std",
+ "kv_unstable",
+ "value-bag/error"
+ ],
+ "kv_unstable_sval": [
+ "kv_unstable",
+ "value-bag/sval",
+ "sval"
+ ],
+ "max_level_debug": [],
+ "max_level_error": [],
+ "max_level_info": [],
+ "max_level_off": [],
+ "max_level_trace": [],
+ "max_level_warn": [],
+ "release_max_level_debug": [],
+ "release_max_level_error": [],
+ "release_max_level_info": [],
+ "release_max_level_off": [],
+ "release_max_level_trace": [],
+ "release_max_level_warn": [],
+ "serde": [
+ "dep:serde"
+ ],
+ "std": [],
+ "sval": [
+ "dep:sval"
+ ],
+ "value-bag": [
+ "dep:value-bag"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "std",
+ "serde",
+ "kv_unstable_std",
+ "kv_unstable_sval",
+ "kv_unstable_serde"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "development-tools::debugging"
+ ],
+ "keywords": [
+ "logging"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/log",
+ "homepage": null,
+ "documentation": "https://docs.rs/log",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "memchr",
+ "version": "2.5.0",
+ "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Safe interface to memchr.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.18",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "memchr",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "default": [
+ "std"
+ ],
+ "libc": [
+ "dep:libc"
+ ],
+ "rustc-dep-of-std": [
+ "core",
+ "compiler_builtins"
+ ],
+ "std": [],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>",
+ "bluss"
+ ],
+ "categories": [],
+ "keywords": [
+ "memchr",
+ "char",
+ "scan",
+ "strchr",
+ "string"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/memchr",
+ "homepage": "https://github.com/BurntSushi/memchr",
+ "documentation": "https://docs.rs/memchr/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "memmap2",
+ "version": "0.5.10",
+ "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Cross-platform Rust API for memory-mapped file IO",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "stable_deref_trait",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "owning_ref",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tempfile",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(unix)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "memmap2",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "cat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/examples/cat.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "stable_deref_trait": [
+ "dep:stable_deref_trait"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Dan Burkert <dan@danburkert.com>",
+ "Yevhenii Reizner <razrfalcon@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "mmap",
+ "memory-map",
+ "io",
+ "file"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/RazrFalcon/memmap2-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/memmap2",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "once_cell",
+ "version": "1.17.1",
+ "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Single assignment cells and lazy values.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "atomic-polyfill",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": "atomic_polyfill",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "critical-section",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": "critical_section",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "parking_lot_core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.9.3",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "critical-section",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.1",
+ "kind": "dev",
+ "rename": "critical_section",
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "std"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "crossbeam-utils",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.7",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.2.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "once_cell",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench_acquire",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_acquire.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench_vs_lazy_static",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_vs_lazy_static.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "lazy_static",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/lazy_static.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "reentrant_init_deadlocks",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/reentrant_init_deadlocks.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "regex",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/regex.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_synchronization",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/test_synchronization.rs",
+ "edition": "2021",
+ "required-features": [
+ "std"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "it",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/tests/it.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "alloc": [
+ "race"
+ ],
+ "atomic-polyfill": [
+ "critical-section"
+ ],
+ "atomic_polyfill": [
+ "dep:atomic_polyfill"
+ ],
+ "critical-section": [
+ "critical_section",
+ "atomic_polyfill"
+ ],
+ "critical_section": [
+ "dep:critical_section"
+ ],
+ "default": [
+ "std"
+ ],
+ "parking_lot": [
+ "parking_lot_core"
+ ],
+ "parking_lot_core": [
+ "dep:parking_lot_core"
+ ],
+ "race": [],
+ "std": [
+ "alloc"
+ ],
+ "unstable": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Aleksey Kladov <aleksey.kladov@gmail.com>"
+ ],
+ "categories": [
+ "rust-patterns",
+ "memory-management"
+ ],
+ "keywords": [
+ "lazy",
+ "static"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/matklad/once_cell",
+ "homepage": null,
+ "documentation": "https://docs.rs/once_cell",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.56"
+ },
+ {
+ "name": "pcre2",
+ "version": "0.2.3",
+ "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "High level wrapper library for PCRE2.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.46",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "pcre2-sys",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "thread_local",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "pcre2",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [
+ "pcre",
+ "pcre2",
+ "regex",
+ "jit",
+ "perl"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/rust-pcre2",
+ "homepage": "https://github.com/BurntSushi/rust-pcre2",
+ "documentation": "https://docs.rs/pcre2",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "pcre2-sys",
+ "version": "0.2.5",
+ "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Low level bindings to PCRE2.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "libc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "cc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "parallel"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "pkg-config",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.13",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "pcre2-sys",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "external-ffi-bindings"
+ ],
+ "keywords": [
+ "pcre",
+ "pcre2",
+ "regex",
+ "jit"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/rust-pcre2",
+ "homepage": "https://github.com/BurntSushi/rust-pcre2",
+ "documentation": "https://docs.rs/pcre2-sys",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "pkg-config",
+ "version": "0.3.26",
+ "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "pkg-config",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "build-dependencies"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/pkg-config-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/pkg-config",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "proc-macro2",
+ "version": "1.0.56",
+ "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "unicode-ident",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quote",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "proc-macro2",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "comments",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "features",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "marker",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_fmt",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "proc-macro"
+ ],
+ "nightly": [],
+ "proc-macro": [],
+ "span-locations": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "rustc-args": [
+ "--cfg",
+ "procmacro2_semver_exempt"
+ ],
+ "rustdoc-args": [
+ "--cfg",
+ "procmacro2_semver_exempt",
+ "--cfg",
+ "doc_cfg"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "span-locations"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>",
+ "Alex Crichton <alex@alexcrichton.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers"
+ ],
+ "keywords": [
+ "macros",
+ "syn"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/proc-macro2",
+ "homepage": null,
+ "documentation": "https://docs.rs/proc-macro2",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.31"
+ },
+ {
+ "name": "quote",
+ "version": "1.0.26",
+ "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Quasi-quoting macro quote!(...)",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "proc-macro2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.52",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "trybuild",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.66",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "diff"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "quote",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "compiletest",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "proc-macro"
+ ],
+ "proc-macro": [
+ "proc-macro2/proc-macro"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers"
+ ],
+ "keywords": [
+ "macros",
+ "syn"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/quote",
+ "homepage": null,
+ "documentation": "https://docs.rs/quote/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.31"
+ },
+ {
+ "name": "regex",
+ "version": "1.8.1",
+ "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "aho-corasick",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "memchr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.5.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex-syntax",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.7.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quickcheck",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "getrandom",
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-cheat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-replace",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-single-cheat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna-single",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "shootout-regex-dna",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa-utf8bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "nfa-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack-utf8bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "backtrack-bytes",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "crates-regex",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "aho-corasick": [
+ "dep:aho-corasick"
+ ],
+ "default": [
+ "std",
+ "perf",
+ "unicode",
+ "regex-syntax/default"
+ ],
+ "memchr": [
+ "dep:memchr"
+ ],
+ "pattern": [],
+ "perf": [
+ "perf-cache",
+ "perf-dfa",
+ "perf-inline",
+ "perf-literal"
+ ],
+ "perf-cache": [],
+ "perf-dfa": [],
+ "perf-inline": [],
+ "perf-literal": [
+ "aho-corasick",
+ "memchr"
+ ],
+ "std": [],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment",
+ "regex-syntax/unicode"
+ ],
+ "unicode-age": [
+ "regex-syntax/unicode-age"
+ ],
+ "unicode-bool": [
+ "regex-syntax/unicode-bool"
+ ],
+ "unicode-case": [
+ "regex-syntax/unicode-case"
+ ],
+ "unicode-gencat": [
+ "regex-syntax/unicode-gencat"
+ ],
+ "unicode-perl": [
+ "regex-syntax/unicode-perl"
+ ],
+ "unicode-script": [
+ "regex-syntax/unicode-script"
+ ],
+ "unicode-segment": [
+ "regex-syntax/unicode-segment"
+ ],
+ "unstable": [
+ "pattern"
+ ],
+ "use_std": [
+ "std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.60.0"
+ },
+ {
+ "name": "regex-automata",
+ "version": "0.1.10",
+ "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Automata construction and matching using regular expressions.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "fst",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex-syntax",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6.16",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "std"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.2.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.82",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_bytes",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.11",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.82",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "toml",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.10",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex-automata",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "default",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/tests/tests.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ }
+ ],
+ "features": {
+ "default": [
+ "std"
+ ],
+ "fst": [
+ "dep:fst"
+ ],
+ "regex-syntax": [
+ "dep:regex-syntax"
+ ],
+ "std": [
+ "regex-syntax"
+ ],
+ "transducer": [
+ "std",
+ "fst"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "text-processing"
+ ],
+ "keywords": [
+ "regex",
+ "dfa",
+ "automata",
+ "automaton",
+ "nfa"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/regex-automata",
+ "homepage": "https://github.com/BurntSushi/regex-automata",
+ "documentation": "https://docs.rs/regex-automata",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex-syntax",
+ "version": "0.6.29",
+ "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A regular expression parser.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex-syntax",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/benches/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "unicode"
+ ],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ],
+ "unicode-age": [],
+ "unicode-bool": [],
+ "unicode-case": [],
+ "unicode-gencat": [],
+ "unicode-perl": [],
+ "unicode-script": [],
+ "unicode-segment": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex-syntax",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "regex-syntax",
+ "version": "0.7.1",
+ "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A regular expression parser.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "regex-syntax",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [
+ "std",
+ "unicode"
+ ],
+ "std": [],
+ "unicode": [
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ],
+ "unicode-age": [],
+ "unicode-bool": [],
+ "unicode-case": [],
+ "unicode-gencat": [],
+ "unicode-perl": [],
+ "unicode-script": [],
+ "unicode-segment": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "docsrs"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "The Rust Project Developers"
+ ],
+ "categories": [],
+ "keywords": [],
+ "readme": "README.md",
+ "repository": "https://github.com/rust-lang/regex",
+ "homepage": "https://github.com/rust-lang/regex",
+ "documentation": "https://docs.rs/regex-syntax",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.60.0"
+ },
+ {
+ "name": "ripgrep",
+ "version": "13.0.0",
+ "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "ripgrep is a line-oriented search tool that recursively searches the current\ndirectory for a regex pattern while respecting gitignore rules. ripgrep has\nfirst class support on Windows, macOS and Linux.\n",
+ "source": null,
+ "dependencies": [
+ {
+ "name": "bstr",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "clap",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.33.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "suggestions"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "grep",
+ "source": null,
+ "req": "^0.2.11",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/grep"
+ },
+ {
+ "name": "ignore",
+ "source": null,
+ "req": "^0.4.19",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "$ROOT$ripgrep/crates/ignore"
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "log",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.3.5",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_json",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.23",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "termcolor",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.77",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.77",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "walkdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "clap",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.33.0",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [
+ "suggestions"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lazy_static",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.1.0",
+ "kind": "build",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "jemallocator",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.5.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "bin"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "rg",
+ "src_path": "$ROOT$ripgrep/crates/core/main.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "integration",
+ "src_path": "$ROOT$ripgrep/tests/tests.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$ripgrep/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "pcre2": [
+ "grep/pcre2"
+ ],
+ "simd-accel": [
+ "grep/simd-accel"
+ ]
+ },
+ "manifest_path": "$ROOT$ripgrep/Cargo.toml",
+ "metadata": {
+ "deb": {
+ "assets": [
+ [
+ "target/release/rg",
+ "usr/bin/",
+ "755"
+ ],
+ [
+ "COPYING",
+ "usr/share/doc/ripgrep/",
+ "644"
+ ],
+ [
+ "LICENSE-MIT",
+ "usr/share/doc/ripgrep/",
+ "644"
+ ],
+ [
+ "UNLICENSE",
+ "usr/share/doc/ripgrep/",
+ "644"
+ ],
+ [
+ "CHANGELOG.md",
+ "usr/share/doc/ripgrep/CHANGELOG",
+ "644"
+ ],
+ [
+ "README.md",
+ "usr/share/doc/ripgrep/README",
+ "644"
+ ],
+ [
+ "FAQ.md",
+ "usr/share/doc/ripgrep/FAQ",
+ "644"
+ ],
+ [
+ "deployment/deb/rg.1",
+ "usr/share/man/man1/rg.1",
+ "644"
+ ],
+ [
+ "deployment/deb/rg.bash",
+ "usr/share/bash-completion/completions/rg",
+ "644"
+ ],
+ [
+ "deployment/deb/rg.fish",
+ "usr/share/fish/vendor_completions.d/rg.fish",
+ "644"
+ ],
+ [
+ "deployment/deb/_rg",
+ "usr/share/zsh/vendor-completions/",
+ "644"
+ ]
+ ],
+ "extended-description": "ripgrep (rg) recursively searches your current directory for a regex pattern.\nBy default, ripgrep will respect your .gitignore and automatically skip hidden\nfiles/directories and binary files.\n",
+ "features": [
+ "pcre2"
+ ],
+ "section": "utils"
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "command-line-utilities",
+ "text-processing"
+ ],
+ "keywords": [
+ "regex",
+ "grep",
+ "egrep",
+ "search",
+ "pattern"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/ripgrep",
+ "homepage": "https://github.com/BurntSushi/ripgrep",
+ "documentation": "https://github.com/BurntSushi/ripgrep",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.65"
+ },
+ {
+ "name": "ryu",
+ "version": "1.0.13",
+ "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Apache-2.0 OR BSL-1.0",
+ "license_file": null,
+ "description": "Fast floating point to string conversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "no-panic",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "num_cpus",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand_xorshift",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "ryu",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "upstream_benchmark",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/examples/upstream_benchmark.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "common_test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/common_test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "d2s_table_test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_table_test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "d2s_test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "exhaustive",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/exhaustive.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "f2s_test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/f2s_test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "s2d_test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2d_test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "s2f_test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2f_test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "bench",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/benches/bench.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "no-panic": [
+ "dep:no-panic"
+ ],
+ "small": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "value-formatting",
+ "no-std"
+ ],
+ "keywords": [
+ "float"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/ryu",
+ "homepage": null,
+ "documentation": "https://docs.rs/ryu",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.36"
+ },
+ {
+ "name": "same-file",
+ "version": "1.0.6",
+ "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "A simple crate for determining whether two file paths point to the same file.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "doc-comment",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "winapi-util",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "same-file",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "is_same_file",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_same_file.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "is_stderr",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_stderr.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "same",
+ "file",
+ "equal",
+ "inode"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/same-file",
+ "homepage": "https://github.com/BurntSushi/same-file",
+ "documentation": "https://docs.rs/same-file",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "serde",
+ "version": "1.0.160",
+ "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A generic serialization/deserialization framework",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "=1.0.160",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "serde",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "alloc": [],
+ "default": [
+ "std"
+ ],
+ "derive": [
+ "serde_derive"
+ ],
+ "rc": [],
+ "serde_derive": [
+ "dep:serde_derive"
+ ],
+ "std": [],
+ "unstable": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "derive"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "derive",
+ "rc"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Erick Tryzelaar <erick.tryzelaar@gmail.com>",
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "encoding",
+ "no-std"
+ ],
+ "keywords": [
+ "serde",
+ "serialization",
+ "no_std"
+ ],
+ "readme": "crates-io.md",
+ "repository": "https://github.com/serde-rs/serde",
+ "homepage": "https://serde.rs",
+ "documentation": "https://docs.rs/serde",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.19"
+ },
+ {
+ "name": "serde_derive",
+ "version": "1.0.160",
+ "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "proc-macro2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quote",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "syn",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.0.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "proc-macro"
+ ],
+ "crate_types": [
+ "proc-macro"
+ ],
+ "name": "serde_derive",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "default": [],
+ "deserialize_in_place": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Erick Tryzelaar <erick.tryzelaar@gmail.com>",
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "no-std"
+ ],
+ "keywords": [
+ "serde",
+ "serialization",
+ "no_std",
+ "derive"
+ ],
+ "readme": "crates-io.md",
+ "repository": "https://github.com/serde-rs/serde",
+ "homepage": "https://serde.rs",
+ "documentation": "https://serde.rs/derive.html",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.56"
+ },
+ {
+ "name": "serde_json",
+ "version": "1.0.96",
+ "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "A JSON serialization file format",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "indexmap",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.5.2",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [
+ "std"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "itoa",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ryu",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.100",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "automod",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "indoc",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ref-cast",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.100",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "derive"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_bytes",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.11",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_derive",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "serde_stacker",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "trybuild",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.49",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "diff"
+ ],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "serde_json",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "compiletest",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/compiletest.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "debug",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/debug.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "lexical",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/lexical.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "map",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/map.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "regression",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/regression.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "stream",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/stream.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/test.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/build.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "alloc": [
+ "serde/alloc"
+ ],
+ "arbitrary_precision": [],
+ "default": [
+ "std"
+ ],
+ "float_roundtrip": [],
+ "indexmap": [
+ "dep:indexmap"
+ ],
+ "preserve_order": [
+ "indexmap",
+ "std"
+ ],
+ "raw_value": [],
+ "std": [
+ "serde/std"
+ ],
+ "unbounded_depth": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "features": [
+ "raw_value",
+ "unbounded_depth"
+ ],
+ "rustdoc-args": [
+ "--cfg",
+ "docsrs"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "raw_value"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Erick Tryzelaar <erick.tryzelaar@gmail.com>",
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "encoding",
+ "parser-implementations",
+ "no-std"
+ ],
+ "keywords": [
+ "json",
+ "serde",
+ "serialization"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/serde-rs/json",
+ "homepage": null,
+ "documentation": "https://docs.rs/serde_json",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.36"
+ },
+ {
+ "name": "strsim",
+ "version": "0.8.0",
+ "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT",
+ "license_file": null,
+ "description": "Implementations of string similarity metrics.\nIncludes Hamming, Levenshtein, OSA, Damerau-Levenshtein, Jaro, and Jaro-Winkler.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "strsim",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "lib",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/tests/lib.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "benches",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/benches/benches.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Danny Guo <dannyguo91@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "string",
+ "similarity",
+ "Hamming",
+ "Levenshtein",
+ "Jaro"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dguo/strsim-rs",
+ "homepage": "https://github.com/dguo/strsim-rs",
+ "documentation": "https://docs.rs/strsim/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "syn",
+ "version": "2.0.15",
+ "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Parser for Rust source code",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "proc-macro2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.55",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "quote",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.25",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-ident",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "anyhow",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "automod",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "flate2",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "insta",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rayon",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ref-cast",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "regex",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "reqwest",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.11",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "blocking"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustversion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "syn-test-suite",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "tar",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.16",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "termcolor",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "walkdir",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^2.3.2",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "syn",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "regression",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_asyncness",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_attribute",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_derive_input",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_expr",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_generics",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_grouping",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_ident",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_item",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_iterators",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_lit",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_meta",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_parse_buffer",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_parse_stream",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_pat",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_path",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_precedence",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_receiver",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_round_trip",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_shebang",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_should_parse",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_stmt",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_token_trees",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_ty",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "test_visibility",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "zzz_stable",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "rust",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs",
+ "edition": "2021",
+ "required-features": [
+ "full",
+ "parsing"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "file",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs",
+ "edition": "2021",
+ "required-features": [
+ "full",
+ "parsing"
+ ],
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "clone-impls": [],
+ "default": [
+ "derive",
+ "parsing",
+ "printing",
+ "clone-impls",
+ "proc-macro"
+ ],
+ "derive": [],
+ "extra-traits": [],
+ "fold": [],
+ "full": [],
+ "parsing": [],
+ "printing": [
+ "quote"
+ ],
+ "proc-macro": [
+ "proc-macro2/proc-macro",
+ "quote/proc-macro"
+ ],
+ "quote": [
+ "dep:quote"
+ ],
+ "test": [
+ "syn-test-suite/all-features"
+ ],
+ "visit": [],
+ "visit-mut": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true,
+ "rustdoc-args": [
+ "--cfg",
+ "doc_cfg"
+ ],
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ },
+ "playground": {
+ "features": [
+ "full",
+ "visit",
+ "visit-mut",
+ "fold",
+ "extra-traits"
+ ]
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers",
+ "parser-implementations"
+ ],
+ "keywords": [
+ "macros",
+ "syn"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/syn",
+ "homepage": null,
+ "documentation": "https://docs.rs/syn",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.56"
+ },
+ {
+ "name": "termcolor",
+ "version": "1.2.0",
+ "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense OR MIT",
+ "license_file": null,
+ "description": "A simple cross platform library for writing colored text to a terminal.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "winapi-util",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "termcolor",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "windows",
+ "win",
+ "color",
+ "ansi",
+ "console"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/termcolor",
+ "homepage": "https://github.com/BurntSushi/termcolor",
+ "documentation": "https://docs.rs/termcolor",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "textwrap",
+ "version": "0.11.0",
+ "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT",
+ "license_file": null,
+ "description": "Textwrap is a small library for word wrapping, indenting, and\ndedenting strings.\n\nYou can use it to format strings (such as help and error messages) for\ndisplay in commandline applications. It is designed to be efficient\nand handle Unicode characters correctly.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "hyphenation",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.7.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [
+ "embed_all"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "term_size",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3.0",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-width",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "lipsum",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand_xorshift",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "version-sync",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.6",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "textwrap",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "layout",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/layout.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "example"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "termwidth",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/termwidth.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "version-numbers",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/tests/version-numbers.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "linear",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/benches/linear.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "hyphenation": [
+ "dep:hyphenation"
+ ],
+ "term_size": [
+ "dep:term_size"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "all-features": true
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Martin Geisler <martin@geisler.net>"
+ ],
+ "categories": [
+ "text-processing",
+ "command-line-interface"
+ ],
+ "keywords": [
+ "text",
+ "formatting",
+ "wrap",
+ "typesetting",
+ "hyphenation"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/mgeisler/textwrap",
+ "homepage": null,
+ "documentation": "https://docs.rs/textwrap/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "thread_local",
+ "version": "1.1.7",
+ "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT OR Apache-2.0",
+ "license_file": null,
+ "description": "Per-object thread-local storage",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "cfg-if",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.0",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "once_cell",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.5.2",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "criterion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4.0",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "thread_local",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/src/lib.rs",
+ "edition": "2021",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "thread_local",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/benches/thread_local.rs",
+ "edition": "2021",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "nightly": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Amanieu d'Antras <amanieu@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "thread_local",
+ "concurrent",
+ "thread"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/Amanieu/thread_local-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/thread_local/",
+ "edition": "2021",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "unicode-ident",
+ "version": "1.0.8",
+ "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016",
+ "license_file": null,
+ "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "criterion",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "fst",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rand",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.8",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "small_rng"
+ ],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "roaring",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.10",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "ucd-trie",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": false,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "unicode-xid",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.2.4",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "unicode-ident",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "compare",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "test"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "static_size",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": true
+ },
+ {
+ "kind": [
+ "bench"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "xid",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs",
+ "edition": "2018",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-unknown-linux-gnu"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "David Tolnay <dtolnay@gmail.com>"
+ ],
+ "categories": [
+ "development-tools::procedural-macro-helpers",
+ "no-std"
+ ],
+ "keywords": [
+ "unicode",
+ "xid"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/dtolnay/unicode-ident",
+ "homepage": null,
+ "documentation": "https://docs.rs/unicode-ident",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": "1.31"
+ },
+ {
+ "name": "unicode-width",
+ "version": "0.1.10",
+ "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Determine displayed width of `char` and `str` types\naccording to Unicode Standard Annex #11 rules.\n",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "compiler_builtins",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1",
+ "kind": null,
+ "rename": null,
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-core",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": "core",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "rustc-std-workspace-std",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0",
+ "kind": null,
+ "rename": "std",
+ "optional": true,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "unicode-width",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {
+ "bench": [],
+ "compiler_builtins": [
+ "dep:compiler_builtins"
+ ],
+ "core": [
+ "dep:core"
+ ],
+ "default": [],
+ "no_std": [],
+ "rustc-dep-of-std": [
+ "std",
+ "core",
+ "compiler_builtins"
+ ],
+ "std": [
+ "dep:std"
+ ]
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "kwantam <kwantam@gmail.com>",
+ "Manish Goregaokar <manishsmail@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "text",
+ "width",
+ "unicode"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/unicode-rs/unicode-width",
+ "homepage": "https://github.com/unicode-rs/unicode-width",
+ "documentation": "https://unicode-rs.github.io/unicode-width",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "walkdir",
+ "version": "2.3.3",
+ "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "Recursively walk a directory.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "same-file",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^1.0.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "doc-comment",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": "dev",
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ },
+ {
+ "name": "winapi-util",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.1.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "walkdir",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "filesystem"
+ ],
+ "keywords": [
+ "directory",
+ "recursive",
+ "walk",
+ "iterator"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/walkdir",
+ "homepage": "https://github.com/BurntSushi/walkdir",
+ "documentation": "https://docs.rs/walkdir/",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi",
+ "version": "0.3.9",
+ "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Raw FFI bindings for all of Windows API.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "winapi-i686-pc-windows-gnu",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "i686-pc-windows-gnu",
+ "registry": null
+ },
+ {
+ "name": "winapi-x86_64-pc-windows-gnu",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.4",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": "x86_64-pc-windows-gnu",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {
+ "accctrl": [],
+ "aclapi": [],
+ "activation": [],
+ "adhoc": [],
+ "appmgmt": [],
+ "audioclient": [],
+ "audiosessiontypes": [],
+ "avrt": [],
+ "basetsd": [],
+ "bcrypt": [],
+ "bits": [],
+ "bits10_1": [],
+ "bits1_5": [],
+ "bits2_0": [],
+ "bits2_5": [],
+ "bits3_0": [],
+ "bits4_0": [],
+ "bits5_0": [],
+ "bitscfg": [],
+ "bitsmsg": [],
+ "bluetoothapis": [],
+ "bluetoothleapis": [],
+ "bthdef": [],
+ "bthioctl": [],
+ "bthledef": [],
+ "bthsdpdef": [],
+ "bugcodes": [],
+ "cderr": [],
+ "cfg": [],
+ "cfgmgr32": [],
+ "cguid": [],
+ "combaseapi": [],
+ "coml2api": [],
+ "commapi": [],
+ "commctrl": [],
+ "commdlg": [],
+ "commoncontrols": [],
+ "consoleapi": [],
+ "corecrt": [],
+ "corsym": [],
+ "d2d1": [],
+ "d2d1_1": [],
+ "d2d1_2": [],
+ "d2d1_3": [],
+ "d2d1effectauthor": [],
+ "d2d1effects": [],
+ "d2d1effects_1": [],
+ "d2d1effects_2": [],
+ "d2d1svg": [],
+ "d2dbasetypes": [],
+ "d3d": [],
+ "d3d10": [],
+ "d3d10_1": [],
+ "d3d10_1shader": [],
+ "d3d10effect": [],
+ "d3d10misc": [],
+ "d3d10sdklayers": [],
+ "d3d10shader": [],
+ "d3d11": [],
+ "d3d11_1": [],
+ "d3d11_2": [],
+ "d3d11_3": [],
+ "d3d11_4": [],
+ "d3d11on12": [],
+ "d3d11sdklayers": [],
+ "d3d11shader": [],
+ "d3d11tokenizedprogramformat": [],
+ "d3d12": [],
+ "d3d12sdklayers": [],
+ "d3d12shader": [],
+ "d3d9": [],
+ "d3d9caps": [],
+ "d3d9types": [],
+ "d3dcommon": [],
+ "d3dcompiler": [],
+ "d3dcsx": [],
+ "d3dkmdt": [],
+ "d3dkmthk": [],
+ "d3dukmdt": [],
+ "d3dx10core": [],
+ "d3dx10math": [],
+ "d3dx10mesh": [],
+ "datetimeapi": [],
+ "davclnt": [],
+ "dbghelp": [],
+ "dbt": [],
+ "dcommon": [],
+ "dcomp": [],
+ "dcompanimation": [],
+ "dcomptypes": [],
+ "dde": [],
+ "ddraw": [],
+ "ddrawi": [],
+ "ddrawint": [],
+ "debug": [
+ "impl-debug"
+ ],
+ "debugapi": [],
+ "devguid": [],
+ "devicetopology": [],
+ "devpkey": [],
+ "devpropdef": [],
+ "dinput": [],
+ "dinputd": [],
+ "dispex": [],
+ "dmksctl": [],
+ "dmusicc": [],
+ "docobj": [],
+ "documenttarget": [],
+ "dot1x": [],
+ "dpa_dsa": [],
+ "dpapi": [],
+ "dsgetdc": [],
+ "dsound": [],
+ "dsrole": [],
+ "dvp": [],
+ "dwmapi": [],
+ "dwrite": [],
+ "dwrite_1": [],
+ "dwrite_2": [],
+ "dwrite_3": [],
+ "dxdiag": [],
+ "dxfile": [],
+ "dxgi": [],
+ "dxgi1_2": [],
+ "dxgi1_3": [],
+ "dxgi1_4": [],
+ "dxgi1_5": [],
+ "dxgi1_6": [],
+ "dxgidebug": [],
+ "dxgiformat": [],
+ "dxgitype": [],
+ "dxva2api": [],
+ "dxvahd": [],
+ "eaptypes": [],
+ "enclaveapi": [],
+ "endpointvolume": [],
+ "errhandlingapi": [],
+ "everything": [],
+ "evntcons": [],
+ "evntprov": [],
+ "evntrace": [],
+ "excpt": [],
+ "exdisp": [],
+ "fibersapi": [],
+ "fileapi": [],
+ "functiondiscoverykeys_devpkey": [],
+ "gl-gl": [],
+ "guiddef": [],
+ "handleapi": [],
+ "heapapi": [],
+ "hidclass": [],
+ "hidpi": [],
+ "hidsdi": [],
+ "hidusage": [],
+ "highlevelmonitorconfigurationapi": [],
+ "hstring": [],
+ "http": [],
+ "ifdef": [],
+ "ifmib": [],
+ "imm": [],
+ "impl-debug": [],
+ "impl-default": [],
+ "in6addr": [],
+ "inaddr": [],
+ "inspectable": [],
+ "interlockedapi": [],
+ "intsafe": [],
+ "ioapiset": [],
+ "ipexport": [],
+ "iphlpapi": [],
+ "ipifcons": [],
+ "ipmib": [],
+ "iprtrmib": [],
+ "iptypes": [],
+ "jobapi": [],
+ "jobapi2": [],
+ "knownfolders": [],
+ "ks": [],
+ "ksmedia": [],
+ "ktmtypes": [],
+ "ktmw32": [],
+ "l2cmn": [],
+ "libloaderapi": [],
+ "limits": [],
+ "lmaccess": [],
+ "lmalert": [],
+ "lmapibuf": [],
+ "lmat": [],
+ "lmcons": [],
+ "lmdfs": [],
+ "lmerrlog": [],
+ "lmjoin": [],
+ "lmmsg": [],
+ "lmremutl": [],
+ "lmrepl": [],
+ "lmserver": [],
+ "lmshare": [],
+ "lmstats": [],
+ "lmsvc": [],
+ "lmuse": [],
+ "lmwksta": [],
+ "lowlevelmonitorconfigurationapi": [],
+ "lsalookup": [],
+ "memoryapi": [],
+ "minschannel": [],
+ "minwinbase": [],
+ "minwindef": [],
+ "mmdeviceapi": [],
+ "mmeapi": [],
+ "mmreg": [],
+ "mmsystem": [],
+ "mprapidef": [],
+ "msaatext": [],
+ "mscat": [],
+ "mschapp": [],
+ "mssip": [],
+ "mstcpip": [],
+ "mswsock": [],
+ "mswsockdef": [],
+ "namedpipeapi": [],
+ "namespaceapi": [],
+ "nb30": [],
+ "ncrypt": [],
+ "netioapi": [],
+ "nldef": [],
+ "ntddndis": [],
+ "ntddscsi": [],
+ "ntddser": [],
+ "ntdef": [],
+ "ntlsa": [],
+ "ntsecapi": [],
+ "ntstatus": [],
+ "oaidl": [],
+ "objbase": [],
+ "objidl": [],
+ "objidlbase": [],
+ "ocidl": [],
+ "ole2": [],
+ "oleauto": [],
+ "olectl": [],
+ "oleidl": [],
+ "opmapi": [],
+ "pdh": [],
+ "perflib": [],
+ "physicalmonitorenumerationapi": [],
+ "playsoundapi": [],
+ "portabledevice": [],
+ "portabledeviceapi": [],
+ "portabledevicetypes": [],
+ "powerbase": [],
+ "powersetting": [],
+ "powrprof": [],
+ "processenv": [],
+ "processsnapshot": [],
+ "processthreadsapi": [],
+ "processtopologyapi": [],
+ "profileapi": [],
+ "propidl": [],
+ "propkey": [],
+ "propkeydef": [],
+ "propsys": [],
+ "prsht": [],
+ "psapi": [],
+ "qos": [],
+ "realtimeapiset": [],
+ "reason": [],
+ "restartmanager": [],
+ "restrictederrorinfo": [],
+ "rmxfguid": [],
+ "roapi": [],
+ "robuffer": [],
+ "roerrorapi": [],
+ "rpc": [],
+ "rpcdce": [],
+ "rpcndr": [],
+ "rtinfo": [],
+ "sapi": [],
+ "sapi51": [],
+ "sapi53": [],
+ "sapiddk": [],
+ "sapiddk51": [],
+ "schannel": [],
+ "sddl": [],
+ "securityappcontainer": [],
+ "securitybaseapi": [],
+ "servprov": [],
+ "setupapi": [],
+ "shellapi": [],
+ "shellscalingapi": [],
+ "shlobj": [],
+ "shobjidl": [],
+ "shobjidl_core": [],
+ "shtypes": [],
+ "softpub": [],
+ "spapidef": [],
+ "spellcheck": [],
+ "sporder": [],
+ "sql": [],
+ "sqlext": [],
+ "sqltypes": [],
+ "sqlucode": [],
+ "sspi": [],
+ "std": [],
+ "stralign": [],
+ "stringapiset": [],
+ "strmif": [],
+ "subauth": [],
+ "synchapi": [],
+ "sysinfoapi": [],
+ "systemtopologyapi": [],
+ "taskschd": [],
+ "tcpestats": [],
+ "tcpmib": [],
+ "textstor": [],
+ "threadpoolapiset": [],
+ "threadpoollegacyapiset": [],
+ "timeapi": [],
+ "timezoneapi": [],
+ "tlhelp32": [],
+ "transportsettingcommon": [],
+ "tvout": [],
+ "udpmib": [],
+ "unknwnbase": [],
+ "urlhist": [],
+ "urlmon": [],
+ "usb": [],
+ "usbioctl": [],
+ "usbiodef": [],
+ "usbscan": [],
+ "usbspec": [],
+ "userenv": [],
+ "usp10": [],
+ "utilapiset": [],
+ "uxtheme": [],
+ "vadefs": [],
+ "vcruntime": [],
+ "vsbackup": [],
+ "vss": [],
+ "vsserror": [],
+ "vswriter": [],
+ "wbemads": [],
+ "wbemcli": [],
+ "wbemdisp": [],
+ "wbemprov": [],
+ "wbemtran": [],
+ "wct": [],
+ "werapi": [],
+ "winbase": [],
+ "wincodec": [],
+ "wincodecsdk": [],
+ "wincon": [],
+ "wincontypes": [],
+ "wincred": [],
+ "wincrypt": [],
+ "windef": [],
+ "windot11": [],
+ "windowsceip": [],
+ "windowsx": [],
+ "winefs": [],
+ "winerror": [],
+ "winevt": [],
+ "wingdi": [],
+ "winhttp": [],
+ "wininet": [],
+ "winineti": [],
+ "winioctl": [],
+ "winnetwk": [],
+ "winnls": [],
+ "winnt": [],
+ "winreg": [],
+ "winsafer": [],
+ "winscard": [],
+ "winsmcrd": [],
+ "winsock2": [],
+ "winspool": [],
+ "winstring": [],
+ "winsvc": [],
+ "wintrust": [],
+ "winusb": [],
+ "winusbio": [],
+ "winuser": [],
+ "winver": [],
+ "wlanapi": [],
+ "wlanihv": [],
+ "wlanihvtypes": [],
+ "wlantypes": [],
+ "wlclient": [],
+ "wmistr": [],
+ "wnnc": [],
+ "wow64apiset": [],
+ "wpdmtpextensions": [],
+ "ws2bth": [],
+ "ws2def": [],
+ "ws2ipdef": [],
+ "ws2spi": [],
+ "ws2tcpip": [],
+ "wtsapi32": [],
+ "wtypes": [],
+ "wtypesbase": [],
+ "xinput": []
+ },
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "default-target": "x86_64-pc-windows-msvc",
+ "features": [
+ "everything",
+ "impl-debug",
+ "impl-default"
+ ],
+ "targets": [
+ "aarch64-pc-windows-msvc",
+ "i686-pc-windows-msvc",
+ "x86_64-pc-windows-msvc"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Peter Atashian <retep998@gmail.com>"
+ ],
+ "categories": [
+ "external-ffi-bindings",
+ "no-std",
+ "os::windows-apis"
+ ],
+ "keywords": [
+ "windows",
+ "ffi",
+ "win32",
+ "com",
+ "directx"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/retep998/winapi-rs",
+ "homepage": null,
+ "documentation": "https://docs.rs/winapi/",
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi-i686-pc-windows-gnu",
+ "version": "0.4.0",
+ "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi-i686-pc-windows-gnu",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Peter Atashian <retep998@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "windows"
+ ],
+ "readme": null,
+ "repository": "https://github.com/retep998/winapi-rs",
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi-util",
+ "version": "0.1.5",
+ "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "Unlicense/MIT",
+ "license_file": null,
+ "description": "A dumping ground for high level safe wrappers over winapi.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [
+ {
+ "name": "winapi",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.3",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [
+ "std",
+ "consoleapi",
+ "errhandlingapi",
+ "fileapi",
+ "minwindef",
+ "processenv",
+ "winbase",
+ "wincon",
+ "winerror",
+ "winnt"
+ ],
+ "target": "cfg(windows)",
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi-util",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/src/lib.rs",
+ "edition": "2018",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/Cargo.toml",
+ "metadata": {
+ "docs": {
+ "rs": {
+ "targets": [
+ "x86_64-pc-windows-msvc"
+ ]
+ }
+ }
+ },
+ "publish": null,
+ "authors": [
+ "Andrew Gallant <jamslam@gmail.com>"
+ ],
+ "categories": [
+ "os::windows-apis",
+ "external-ffi-bindings"
+ ],
+ "keywords": [
+ "windows",
+ "winapi",
+ "util",
+ "win"
+ ],
+ "readme": "README.md",
+ "repository": "https://github.com/BurntSushi/winapi-util",
+ "homepage": "https://github.com/BurntSushi/winapi-util",
+ "documentation": "https://docs.rs/winapi-util",
+ "edition": "2018",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "winapi-x86_64-pc-windows-gnu",
+ "version": "0.4.0",
+ "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "license": "MIT/Apache-2.0",
+ "license_file": null,
+ "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "winapi-x86_64-pc-windows-gnu",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ },
+ {
+ "kind": [
+ "custom-build"
+ ],
+ "crate_types": [
+ "bin"
+ ],
+ "name": "build-script-build",
+ "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs",
+ "edition": "2015",
+ "doc": false,
+ "doctest": false,
+ "test": false
+ }
+ ],
+ "features": {},
+ "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [
+ "Peter Atashian <retep998@gmail.com>"
+ ],
+ "categories": [],
+ "keywords": [
+ "windows"
+ ],
+ "readme": null,
+ "repository": "https://github.com/retep998/winapi-rs",
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ }
+ ],
+ "workspace_members": [
+ "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+ "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+ "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)",
+ "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+ "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+ "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)"
+ ],
+ "resolve": {
+ "nodes": [
+ {
+ "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "perf-literal",
+ "std"
+ ]
+ },
+ {
+ "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "hermit_abi",
+ "pkg": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(target_os = \"hermit\")"
+ }
+ ]
+ },
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(unix)"
+ }
+ ]
+ },
+ {
+ "name": "winapi",
+ "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default"
+ ]
+ },
+ {
+ "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "once_cell",
+ "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex_automata",
+ "pkg": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "alloc",
+ "default",
+ "std",
+ "unicode"
+ ]
+ },
+ {
+ "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "runtime-dispatch-simd"
+ ]
+ },
+ {
+ "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "jobserver",
+ "pkg": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "jobserver",
+ "parallel"
+ ]
+ },
+ {
+ "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "bitflags",
+ "pkg": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "strsim",
+ "pkg": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "textwrap",
+ "pkg": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "unicode_width",
+ "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "strsim",
+ "suggestions"
+ ]
+ },
+ {
+ "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "crossbeam_utils",
+ "pkg": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "crossbeam-utils",
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "std"
+ ]
+ },
+ {
+ "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "alloc",
+ "default"
+ ]
+ },
+ {
+ "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "encoding_rs",
+ "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "dependencies": [
+ "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "aho_corasick",
+ "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "bstr",
+ "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "fnv",
+ "pkg": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "glob",
+ "pkg": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde_json",
+ "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "log"
+ ]
+ },
+ {
+ "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+ "dependencies": [
+ "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+ "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+ "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "grep_cli",
+ "pkg": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_matcher",
+ "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_printer",
+ "pkg": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_regex",
+ "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_searcher",
+ "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "termcolor",
+ "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "walkdir",
+ "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)",
+ "dependencies": [
+ "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "atty",
+ "pkg": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "bstr",
+ "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "globset",
+ "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "same_file",
+ "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "termcolor",
+ "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "winapi_util",
+ "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "dependencies": [
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)",
+ "dependencies": [
+ "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "grep_matcher",
+ "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "pcre2",
+ "pkg": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)",
+ "dependencies": [
+ "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "base64",
+ "pkg": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "bstr",
+ "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_matcher",
+ "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_regex",
+ "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_searcher",
+ "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde_json",
+ "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "termcolor",
+ "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "base64",
+ "default",
+ "serde",
+ "serde1",
+ "serde_json"
+ ]
+ },
+ {
+ "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "dependencies": [
+ "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "aho_corasick",
+ "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "bstr",
+ "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_matcher",
+ "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex_syntax",
+ "pkg": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "thread_local",
+ "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)",
+ "dependencies": [
+ "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "bstr",
+ "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "bytecount",
+ "pkg": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "encoding_rs",
+ "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "encoding_rs_io",
+ "pkg": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_matcher",
+ "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep_regex",
+ "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "memmap",
+ "pkg": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default"
+ ]
+ },
+ {
+ "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default"
+ ]
+ },
+ {
+ "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+ "dependencies": [
+ "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "crossbeam_channel",
+ "pkg": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "globset",
+ "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "same_file",
+ "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "thread_local",
+ "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "walkdir",
+ "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "winapi_util",
+ "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cc",
+ "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "background_threads_runtime_support"
+ ]
+ },
+ {
+ "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "jemalloc_sys",
+ "pkg": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "background_threads_runtime_support",
+ "default"
+ ]
+ },
+ {
+ "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(unix)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(unix)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "alloc",
+ "default",
+ "race",
+ "std"
+ ]
+ },
+ {
+ "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "pcre2_sys",
+ "pkg": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "thread_local",
+ "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cc",
+ "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "libc",
+ "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "pkg_config",
+ "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "unicode_ident",
+ "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "proc-macro"
+ ]
+ },
+ {
+ "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "proc_macro2",
+ "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "proc-macro"
+ ]
+ },
+ {
+ "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "aho_corasick",
+ "pkg": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "memchr",
+ "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex_syntax",
+ "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "aho-corasick",
+ "default",
+ "memchr",
+ "perf",
+ "perf-cache",
+ "perf-dfa",
+ "perf-inline",
+ "perf-literal",
+ "std",
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default",
+ "std",
+ "unicode",
+ "unicode-age",
+ "unicode-bool",
+ "unicode-case",
+ "unicode-gencat",
+ "unicode-perl",
+ "unicode-script",
+ "unicode-segment"
+ ]
+ },
+ {
+ "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)",
+ "dependencies": [
+ "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+ "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+ "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "bstr",
+ "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "clap",
+ "pkg": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ },
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "grep",
+ "pkg": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "ignore",
+ "pkg": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "jemallocator",
+ "pkg": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))"
+ }
+ ]
+ },
+ {
+ "name": "lazy_static",
+ "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ },
+ {
+ "kind": "build",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "log",
+ "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "regex",
+ "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde_derive",
+ "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde_json",
+ "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "termcolor",
+ "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "walkdir",
+ "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": "dev",
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "winapi_util",
+ "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "serde_derive",
+ "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "alloc",
+ "default",
+ "derive",
+ "serde_derive",
+ "std"
+ ]
+ },
+ {
+ "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "proc_macro2",
+ "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "quote",
+ "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "syn",
+ "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default"
+ ]
+ },
+ {
+ "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "itoa",
+ "pkg": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "ryu",
+ "pkg": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "serde",
+ "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "default",
+ "std"
+ ]
+ },
+ {
+ "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "proc_macro2",
+ "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "quote",
+ "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "unicode_ident",
+ "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": [
+ "clone-impls",
+ "default",
+ "derive",
+ "parsing",
+ "printing",
+ "proc-macro",
+ "quote"
+ ]
+ },
+ {
+ "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "winapi_util",
+ "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "unicode_width",
+ "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "cfg_if",
+ "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "once_cell",
+ "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": [
+ "default"
+ ]
+ },
+ {
+ "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "same_file",
+ "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "winapi_util",
+ "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "winapi_i686_pc_windows_gnu",
+ "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "i686-pc-windows-gnu"
+ }
+ ]
+ },
+ {
+ "name": "winapi_x86_64_pc_windows_gnu",
+ "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "x86_64-pc-windows-gnu"
+ }
+ ]
+ }
+ ],
+ "features": [
+ "consoleapi",
+ "errhandlingapi",
+ "fileapi",
+ "minwinbase",
+ "minwindef",
+ "processenv",
+ "std",
+ "winbase",
+ "wincon",
+ "winerror",
+ "winnt"
+ ]
+ },
+ {
+ "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ },
+ {
+ "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)"
+ ],
+ "deps": [
+ {
+ "name": "winapi",
+ "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": "cfg(windows)"
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ }
+ ],
+ "root": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)"
+ },
+ "target_directory": "$ROOT$ripgrep/target",
+ "version": 1,
+ "workspace_root": "$ROOT$ripgrep",
+ "metadata": null
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index f0f1900c7..5b72d5756 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -29,9 +29,8 @@ parking_lot = "0.12.1"
xflags = "0.3.0"
oorandom = "11.1.3"
rustc-hash = "1.1.0"
-serde = { version = "1.0.137", features = ["derive"] }
-serde_json = { version = "1.0.81", features = ["preserve_order"] }
-threadpool = "1.8.1"
+serde_json = { workspace = true, features = ["preserve_order"] }
+serde.workspace = true
rayon = "1.6.1"
num_cpus = "1.15.0"
mimalloc = { version = "0.1.30", default-features = false, optional = true }
@@ -45,8 +44,20 @@ tracing-subscriber = { version = "0.3.16", default-features = false, features =
] }
tracing-log = "0.1.3"
tracing-tree = "0.2.1"
+triomphe.workspace = true
+nohash-hasher.workspace = true
always-assert = "0.1.2"
+# These dependencies are unused, but we pin them to a version here to restrict them for our transitive dependencies
+# so that we don't pull in duplicates of their dependencies like windows-sys and syn 1 vs 2
+# these would pull in serde 2
+thiserror = "=1.0.39"
+serde_repr = "=0.1.11"
+# these would pull in windows-sys 0.45.0
+mio = "=0.8.5"
+filetime = "=0.2.19"
+parking_lot_core = "=0.9.6"
+
cfg.workspace = true
flycheck.workspace = true
hir-def.workspace = true
@@ -57,7 +68,6 @@ ide-db.workspace = true
ide-ssr.workspace = true
ide.workspace = true
proc-macro-api.workspace = true
-proc-macro-srv.workspace = true
profile.workspace = true
project-model.workspace = true
stdx.workspace = true
@@ -75,7 +85,6 @@ jemallocator = { version = "0.5.0", package = "tikv-jemallocator", optional = tr
[dev-dependencies]
expect-test = "1.4.0"
-jod-thread = "0.1.2"
xshell = "0.2.2"
test-utils.workspace = true
@@ -85,8 +94,5 @@ mbe.workspace = true
[features]
jemalloc = ["jemallocator", "profile/jemalloc"]
force-always-assert = ["always-assert/force"]
-in-rust-tree = [
- "proc-macro-srv/sysroot-abi",
- "ide/in-rust-tree",
- "syntax/in-rust-tree",
-]
+sysroot-abi = []
+in-rust-tree = ["sysroot-abi", "ide/in-rust-tree", "syntax/in-rust-tree"]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
index 4de022b6e..91911dd18 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
@@ -7,7 +7,11 @@
mod logger;
mod rustc_wrapper;
-use std::{env, fs, path::Path, process};
+use std::{
+ env, fs,
+ path::{Path, PathBuf},
+ process,
+};
use lsp_server::Connection;
use rust_analyzer::{cli::flags, config::Config, from_json, Result};
@@ -74,10 +78,16 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> {
println!("rust-analyzer {}", rust_analyzer::version());
return Ok(());
}
- with_extra_thread("LspServer", run_server)?;
- }
- flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => {
- with_extra_thread("MacroExpander", || proc_macro_srv::cli::run().map_err(Into::into))?;
+
+ // rust-analyzer’s “main thread” is actually
+ // a secondary latency-sensitive thread with an increased stack size.
+ // We use this thread intent because any delay in the main loop
+ // will make actions like hitting enter in the editor slow.
+ with_extra_thread(
+ "LspServer",
+ stdx::thread::ThreadIntent::LatencySensitive,
+ run_server,
+ )?;
}
flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?,
flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?,
@@ -135,14 +145,17 @@ const STACK_SIZE: usize = 1024 * 1024 * 8;
/// space.
fn with_extra_thread(
thread_name: impl Into<String>,
+ thread_intent: stdx::thread::ThreadIntent,
f: impl FnOnce() -> Result<()> + Send + 'static,
) -> Result<()> {
- let handle =
- std::thread::Builder::new().name(thread_name.into()).stack_size(STACK_SIZE).spawn(f)?;
- match handle.join() {
- Ok(res) => res,
- Err(panic) => std::panic::resume_unwind(panic),
- }
+ let handle = stdx::thread::Builder::new(thread_intent)
+ .name(thread_name.into())
+ .stack_size(STACK_SIZE)
+ .spawn(f)?;
+
+ handle.join()?;
+
+ Ok(())
}
fn run_server() -> Result<()> {
@@ -152,12 +165,18 @@ fn run_server() -> Result<()> {
let (initialize_id, initialize_params) = connection.initialize_start()?;
tracing::info!("InitializeParams: {}", initialize_params);
- let initialize_params =
- from_json::<lsp_types::InitializeParams>("InitializeParams", &initialize_params)?;
-
- let root_path = match initialize_params
- .root_uri
+ let lsp_types::InitializeParams {
+ root_uri,
+ capabilities,
+ workspace_folders,
+ initialization_options,
+ client_info,
+ ..
+ } = from_json::<lsp_types::InitializeParams>("InitializeParams", &initialize_params)?;
+
+ let root_path = match root_uri
.and_then(|it| it.to_file_path().ok())
+ .map(patch_path_prefix)
.and_then(|it| AbsPathBuf::try_from(it).ok())
{
Some(it) => it,
@@ -167,19 +186,19 @@ fn run_server() -> Result<()> {
}
};
- let workspace_roots = initialize_params
- .workspace_folders
+ let workspace_roots = workspace_folders
.map(|workspaces| {
workspaces
.into_iter()
.filter_map(|it| it.uri.to_file_path().ok())
+ .map(patch_path_prefix)
.filter_map(|it| AbsPathBuf::try_from(it).ok())
.collect::<Vec<_>>()
})
.filter(|workspaces| !workspaces.is_empty())
.unwrap_or_else(|| vec![root_path.clone()]);
- let mut config = Config::new(root_path, initialize_params.capabilities, workspace_roots);
- if let Some(json) = initialize_params.initialization_options {
+ let mut config = Config::new(root_path, capabilities, workspace_roots);
+ if let Some(json) = initialization_options {
if let Err(e) = config.update(json) {
use lsp_types::{
notification::{Notification, ShowMessage},
@@ -208,7 +227,7 @@ fn run_server() -> Result<()> {
connection.initialize_finish(initialize_id, initialize_result)?;
- if let Some(client_info) = initialize_params.client_info {
+ if let Some(client_info) = client_info {
tracing::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
}
@@ -222,3 +241,42 @@ fn run_server() -> Result<()> {
tracing::info!("server did shut down");
Ok(())
}
+
+fn patch_path_prefix(path: PathBuf) -> PathBuf {
+ use std::path::{Component, Prefix};
+ if cfg!(windows) {
+ // VSCode might report paths with the file drive in lowercase, but this can mess
+ // with env vars set by tools and build scripts executed by r-a such that it invalidates
+ // cargo's compilations unnecessarily. https://github.com/rust-lang/rust-analyzer/issues/14683
+ // So we just uppercase the drive letter here unconditionally.
+ // (doing it conditionally is a pain because std::path::Prefix always reports uppercase letters on windows)
+ let mut comps = path.components();
+ match comps.next() {
+ Some(Component::Prefix(prefix)) => {
+ let prefix = match prefix.kind() {
+ Prefix::Disk(d) => {
+ format!("{}:", d.to_ascii_uppercase() as char)
+ }
+ Prefix::VerbatimDisk(d) => {
+ format!(r"\\?\{}:", d.to_ascii_uppercase() as char)
+ }
+ _ => return path,
+ };
+ let mut path = PathBuf::new();
+ path.push(prefix);
+ path.extend(comps);
+ path
+ }
+ _ => path,
+ }
+ } else {
+ path
+ }
+}
+
+#[test]
+#[cfg(windows)]
+fn patch_path_prefix_works() {
+ assert_eq!(patch_path_prefix(r"c:\foo\bar".into()), PathBuf::from(r"C:\foo\bar"));
+ assert_eq!(patch_path_prefix(r"\\?\c:\foo\bar".into()), PathBuf::from(r"\\?\C:\foo\bar"));
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
index 3628670ac..ab06b9681 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
@@ -23,13 +23,14 @@ use crate::semantic_tokens;
pub fn server_capabilities(config: &Config) -> ServerCapabilities {
ServerCapabilities {
- position_encoding: Some(match negotiated_encoding(config.caps()) {
- PositionEncoding::Utf8 => PositionEncodingKind::UTF8,
+ position_encoding: match negotiated_encoding(config.caps()) {
+ PositionEncoding::Utf8 => Some(PositionEncodingKind::UTF8),
PositionEncoding::Wide(wide) => match wide {
- WideEncoding::Utf16 => PositionEncodingKind::UTF16,
- WideEncoding::Utf32 => PositionEncodingKind::UTF32,
+ WideEncoding::Utf16 => Some(PositionEncodingKind::UTF16),
+ WideEncoding::Utf32 => Some(PositionEncodingKind::UTF32),
+ _ => None,
},
- }),
+ },
text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
open_close: Some(true),
change: Some(TextDocumentSyncKind::INCREMENTAL),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
index cf51cf15a..c7b84c41b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -3,8 +3,9 @@
use std::mem;
use cfg::{CfgAtom, CfgExpr};
-use ide::{Cancellable, FileId, RunnableKind, TestId};
+use ide::{Cancellable, CrateId, FileId, RunnableKind, TestId};
use project_model::{self, CargoFeatures, ManifestPath, TargetKind};
+use rustc_hash::FxHashSet;
use vfs::AbsPathBuf;
use crate::global_state::GlobalStateSnapshot;
@@ -20,7 +21,9 @@ pub(crate) struct CargoTargetSpec {
pub(crate) package: String,
pub(crate) target: String,
pub(crate) target_kind: TargetKind,
+ pub(crate) crate_id: CrateId,
pub(crate) required_features: Vec<String>,
+ pub(crate) features: FxHashSet<String>,
}
impl CargoTargetSpec {
@@ -73,12 +76,13 @@ impl CargoTargetSpec {
}
}
- let target_required_features = if let Some(mut spec) = spec {
+ let (allowed_features, target_required_features) = if let Some(mut spec) = spec {
+ let allowed_features = mem::take(&mut spec.features);
let required_features = mem::take(&mut spec.required_features);
spec.push_to(&mut args, kind);
- required_features
+ (allowed_features, required_features)
} else {
- Vec::new()
+ (Default::default(), Default::default())
};
let cargo_config = snap.config.cargo();
@@ -97,7 +101,9 @@ impl CargoTargetSpec {
required_features(cfg, &mut feats);
}
- feats.extend(features.iter().cloned());
+ feats.extend(
+ features.iter().filter(|&feat| allowed_features.contains(feat)).cloned(),
+ );
feats.extend(target_required_features);
feats.dedup();
@@ -136,6 +142,8 @@ impl CargoTargetSpec {
target: target_data.name.clone(),
target_kind: target_data.kind,
required_features: target_data.required_features.clone(),
+ features: package_data.features.keys().cloned().collect(),
+ crate_id,
};
Ok(Some(res))
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
index d5d877680..e35201921 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs
@@ -50,21 +50,24 @@ fn report_metric(metric: &str, value: u64, unit: &str) {
}
fn print_memory_usage(mut host: AnalysisHost, vfs: Vfs) {
- let mut mem = host.per_query_memory_usage();
+ let mem = host.per_query_memory_usage();
let before = profile::memory_usage();
drop(vfs);
let vfs = before.allocated - profile::memory_usage().allocated;
- mem.push(("VFS".into(), vfs));
let before = profile::memory_usage();
drop(host);
- mem.push(("Unaccounted".into(), before.allocated - profile::memory_usage().allocated));
+ let unaccounted = before.allocated - profile::memory_usage().allocated;
+ let remaining = profile::memory_usage().allocated;
- mem.push(("Remaining".into(), profile::memory_usage().allocated));
-
- for (name, bytes) in mem {
+ for (name, bytes, entries) in mem {
// NOTE: Not a debug print, so avoid going through the `eprintln` defined above.
- eprintln!("{bytes:>8} {name}");
+ eprintln!("{bytes:>8} {entries:>6} {name}");
}
+ eprintln!("{vfs:>8} VFS");
+
+ eprintln!("{unaccounted:>8} Unaccounted");
+
+ eprintln!("{remaining:>8} Remaining");
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 6ce1de5d3..4cb917ce2 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -8,18 +8,20 @@ use std::{
use hir::{
db::{DefDatabase, ExpandDatabase, HirDatabase},
- AssocItem, Crate, Function, HasSource, HirDisplay, ModuleDef,
+ Adt, AssocItem, Crate, DefWithBody, HasCrate, HasSource, HirDisplay, ModuleDef, Name,
};
use hir_def::{
body::{BodySourceMap, SyntheticSyntax},
- expr::{ExprId, PatId},
- FunctionId,
+ hir::{ExprId, PatId},
};
-use hir_ty::{Interner, TyExt, TypeFlags};
-use ide::{Analysis, AnalysisHost, LineCol, RootDatabase};
-use ide_db::base_db::{
- salsa::{self, debug::DebugQueryTable, ParallelDatabase},
- SourceDatabase, SourceDatabaseExt,
+use hir_ty::{Interner, Substitution, TyExt, TypeFlags};
+use ide::{LineCol, RootDatabase};
+use ide_db::{
+ base_db::{
+ salsa::{self, debug::DebugQueryTable, ParallelDatabase},
+ SourceDatabase, SourceDatabaseExt,
+ },
+ LineIndexDatabase,
};
use itertools::Itertools;
use oorandom::Rand32;
@@ -27,7 +29,6 @@ use profile::{Bytes, StopWatch};
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource};
use rayon::prelude::*;
use rustc_hash::FxHashSet;
-use stdx::format_to;
use syntax::{AstNode, SyntaxNode};
use vfs::{AbsPathBuf, Vfs, VfsPath};
@@ -120,37 +121,80 @@ impl flags::AnalysisStats {
eprint!(" crates: {num_crates}");
let mut num_decls = 0;
- let mut funcs = Vec::new();
+ let mut bodies = Vec::new();
+ let mut adts = Vec::new();
+ let mut consts = Vec::new();
while let Some(module) = visit_queue.pop() {
if visited_modules.insert(module) {
visit_queue.extend(module.children(db));
for decl in module.declarations(db) {
num_decls += 1;
- if let ModuleDef::Function(f) = decl {
- funcs.push(f);
- }
+ match decl {
+ ModuleDef::Function(f) => bodies.push(DefWithBody::from(f)),
+ ModuleDef::Adt(a) => {
+ if let Adt::Enum(e) = a {
+ for v in e.variants(db) {
+ bodies.push(DefWithBody::from(v));
+ }
+ }
+ adts.push(a)
+ }
+ ModuleDef::Const(c) => {
+ bodies.push(DefWithBody::from(c));
+ consts.push(c)
+ }
+ ModuleDef::Static(s) => bodies.push(DefWithBody::from(s)),
+ _ => (),
+ };
}
for impl_def in module.impl_defs(db) {
for item in impl_def.items(db) {
num_decls += 1;
- if let AssocItem::Function(f) = item {
- funcs.push(f);
+ match item {
+ AssocItem::Function(f) => bodies.push(DefWithBody::from(f)),
+ AssocItem::Const(c) => {
+ bodies.push(DefWithBody::from(c));
+ consts.push(c);
+ }
+ _ => (),
}
}
}
}
}
- eprintln!(", mods: {}, decls: {num_decls}, fns: {}", visited_modules.len(), funcs.len());
+ eprintln!(
+ ", mods: {}, decls: {num_decls}, bodies: {}, adts: {}, consts: {}",
+ visited_modules.len(),
+ bodies.len(),
+ adts.len(),
+ consts.len(),
+ );
eprintln!("{:<20} {}", "Item Collection:", analysis_sw.elapsed());
if self.randomize {
- shuffle(&mut rng, &mut funcs);
+ shuffle(&mut rng, &mut bodies);
+ }
+
+ if !self.skip_lowering {
+ self.run_body_lowering(db, &vfs, &bodies, verbosity);
}
if !self.skip_inference {
- self.run_inference(&host, db, &vfs, &funcs, verbosity);
+ self.run_inference(db, &vfs, &bodies, verbosity);
+ }
+
+ if !self.skip_mir_stats {
+ self.run_mir_lowering(db, &bodies, verbosity);
+ }
+
+ if !self.skip_data_layout {
+ self.run_data_layout(db, &adts, verbosity);
+ }
+
+ if !self.skip_const_eval {
+ self.run_const_eval(db, &consts, verbosity);
}
let total_span = analysis_sw.elapsed();
@@ -175,9 +219,8 @@ impl flags::AnalysisStats {
let mut total_macro_file_size = Bytes::default();
for e in hir::db::ParseMacroExpansionQuery.in_db(db).entries::<Vec<_>>() {
- if let Some((val, _)) = db.parse_macro_expansion(e.key).value {
- total_macro_file_size += syntax_len(val.syntax_node())
- }
+ let val = db.parse_macro_expansion(e.key).value.0;
+ total_macro_file_size += syntax_len(val.syntax_node())
}
eprintln!("source files: {total_file_size}, macro files: {total_macro_file_size}");
}
@@ -189,29 +232,122 @@ impl flags::AnalysisStats {
Ok(())
}
+ fn run_data_layout(&self, db: &RootDatabase, adts: &[hir::Adt], verbosity: Verbosity) {
+ let mut sw = self.stop_watch();
+ let mut all = 0;
+ let mut fail = 0;
+ for &a in adts {
+ if db.generic_params(a.into()).iter().next().is_some() {
+ // Data types with generics don't have layout.
+ continue;
+ }
+ all += 1;
+ let Err(e)
+ = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner), a.krate(db).into())
+ else {
+ continue
+ };
+ if verbosity.is_spammy() {
+ let full_name = a
+ .module(db)
+ .path_to_root(db)
+ .into_iter()
+ .rev()
+ .filter_map(|it| it.name(db))
+ .chain(Some(a.name(db)))
+ .map(|it| it.display(db).to_string())
+ .join("::");
+ println!("Data layout for {full_name} failed due {e:?}");
+ }
+ fail += 1;
+ }
+ let data_layout_time = sw.elapsed();
+ eprintln!("{:<20} {}", "Data layouts:", data_layout_time);
+ eprintln!("Failed data layouts: {fail} ({}%)", percentage(fail, all));
+ report_metric("failed data layouts", fail, "#");
+ report_metric("data layout time", data_layout_time.time.as_millis() as u64, "ms");
+ }
+
+ fn run_const_eval(&self, db: &RootDatabase, consts: &[hir::Const], verbosity: Verbosity) {
+ let mut sw = self.stop_watch();
+ let mut all = 0;
+ let mut fail = 0;
+ for &c in consts {
+ all += 1;
+ let Err(e) = c.render_eval(db) else {
+ continue;
+ };
+ if verbosity.is_spammy() {
+ let full_name = c
+ .module(db)
+ .path_to_root(db)
+ .into_iter()
+ .rev()
+ .filter_map(|it| it.name(db))
+ .chain(c.name(db))
+ .map(|it| it.display(db).to_string())
+ .join("::");
+ println!("Const eval for {full_name} failed due {e:?}");
+ }
+ fail += 1;
+ }
+ let const_eval_time = sw.elapsed();
+ eprintln!("{:<20} {}", "Const evaluation:", const_eval_time);
+ eprintln!("Failed const evals: {fail} ({}%)", percentage(fail, all));
+ report_metric("failed const evals", fail, "#");
+ report_metric("const eval time", const_eval_time.time.as_millis() as u64, "ms");
+ }
+
+ fn run_mir_lowering(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) {
+ let mut sw = self.stop_watch();
+ let all = bodies.len() as u64;
+ let mut fail = 0;
+ for &body in bodies {
+ let Err(e) = db.mir_body(body.into()) else {
+ continue;
+ };
+ if verbosity.is_spammy() {
+ let full_name = body
+ .module(db)
+ .path_to_root(db)
+ .into_iter()
+ .rev()
+ .filter_map(|it| it.name(db))
+ .chain(Some(body.name(db).unwrap_or_else(Name::missing)))
+ .map(|it| it.display(db).to_string())
+ .join("::");
+ println!("Mir body for {full_name} failed due {e:?}");
+ }
+ fail += 1;
+ }
+ let mir_lowering_time = sw.elapsed();
+ eprintln!("{:<20} {}", "MIR lowering:", mir_lowering_time);
+ eprintln!("Mir failed bodies: {fail} ({}%)", percentage(fail, all));
+ report_metric("mir failed bodies", fail, "#");
+ report_metric("mir lowering time", mir_lowering_time.time.as_millis() as u64, "ms");
+ }
+
fn run_inference(
&self,
- host: &AnalysisHost,
db: &RootDatabase,
vfs: &Vfs,
- funcs: &[Function],
+ bodies: &[DefWithBody],
verbosity: Verbosity,
) {
let mut bar = match verbosity {
Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(),
_ if self.parallel || self.output.is_some() => ProgressReport::hidden(),
- _ => ProgressReport::new(funcs.len() as u64),
+ _ => ProgressReport::new(bodies.len() as u64),
};
if self.parallel {
let mut inference_sw = self.stop_watch();
let snap = Snap(db.snapshot());
- funcs
+ bodies
.par_iter()
- .map_with(snap, |snap, &f| {
- let f_id = FunctionId::from(f);
- snap.0.body(f_id.into());
- snap.0.infer(f_id.into());
+ .map_with(snap, |snap, &body| {
+ snap.0.body(body.into());
+ snap.0.infer(body.into());
})
.count();
eprintln!("{:<20} {}", "Parallel Inference:", inference_sw.elapsed());
@@ -227,38 +363,58 @@ impl flags::AnalysisStats {
let mut num_pats_unknown = 0;
let mut num_pats_partially_unknown = 0;
let mut num_pat_type_mismatches = 0;
- let analysis = host.analysis();
- for f in funcs.iter().copied() {
- let name = f.name(db);
- let full_name = f
- .module(db)
- .path_to_root(db)
- .into_iter()
- .rev()
- .filter_map(|it| it.name(db))
- .chain(Some(f.name(db)))
- .join("::");
+ for &body_id in bodies {
+ let name = body_id.name(db).unwrap_or_else(Name::missing);
+ let module = body_id.module(db);
+ let full_name = move || {
+ module
+ .krate()
+ .display_name(db)
+ .map(|it| it.canonical_name().to_string())
+ .into_iter()
+ .chain(
+ module
+ .path_to_root(db)
+ .into_iter()
+ .filter_map(|it| it.name(db))
+ .rev()
+ .chain(Some(body_id.name(db).unwrap_or_else(Name::missing)))
+ .map(|it| it.display(db).to_string()),
+ )
+ .join("::")
+ };
if let Some(only_name) = self.only.as_deref() {
- if name.to_string() != only_name && full_name != only_name {
+ if name.display(db).to_string() != only_name && full_name() != only_name {
continue;
}
}
- let mut msg = format!("processing: {full_name}");
- if verbosity.is_verbose() {
- if let Some(src) = f.source(db) {
- let original_file = src.file_id.original_file(db);
- let path = vfs.file_path(original_file);
- let syntax_range = src.value.syntax().text_range();
- format_to!(msg, " ({} {:?})", path, syntax_range);
+ let msg = move || {
+ if verbosity.is_verbose() {
+ let source = match body_id {
+ DefWithBody::Function(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::Static(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::Const(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::Variant(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::InTypeConst(_) => unimplemented!(),
+ };
+ if let Some(src) = source {
+ let original_file = src.file_id.original_file(db);
+ let path = vfs.file_path(original_file);
+ let syntax_range = src.value.text_range();
+ format!("processing: {} ({} {:?})", full_name(), path, syntax_range)
+ } else {
+ format!("processing: {}", full_name())
+ }
+ } else {
+ format!("processing: {}", full_name())
}
- }
+ };
if verbosity.is_spammy() {
- bar.println(msg.to_string());
+ bar.println(msg());
}
- bar.set_message(&msg);
- let f_id = FunctionId::from(f);
- let (body, sm) = db.body_with_source_map(f_id.into());
- let inference_result = db.infer(f_id.into());
+ bar.set_message(msg);
+ let (body, sm) = db.body_with_source_map(body_id.into());
+ let inference_result = db.infer(body_id.into());
// region:expressions
let (previous_exprs, previous_unknown, previous_partially_unknown) =
@@ -269,9 +425,7 @@ impl flags::AnalysisStats {
let unknown_or_partial = if ty.is_unknown() {
num_exprs_unknown += 1;
if verbosity.is_spammy() {
- if let Some((path, start, end)) =
- expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
- {
+ if let Some((path, start, end)) = expr_syntax_range(db, vfs, &sm, expr_id) {
bar.println(format!(
"{} {}:{}-{}:{}: Unknown type",
path,
@@ -281,7 +435,7 @@ impl flags::AnalysisStats {
end.col,
));
} else {
- bar.println(format!("{name}: Unknown type",));
+ bar.println(format!("{}: Unknown type", name.display(db)));
}
}
true
@@ -295,9 +449,7 @@ impl flags::AnalysisStats {
};
if self.only.is_some() && verbosity.is_spammy() {
// in super-verbose mode for just one function, we print every single expression
- if let Some((_, start, end)) =
- expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
- {
+ if let Some((_, start, end)) = expr_syntax_range(db, vfs, &sm, expr_id) {
bar.println(format!(
"{}:{}-{}:{}: {}",
start.line + 1,
@@ -313,16 +465,14 @@ impl flags::AnalysisStats {
if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
println!(
r#"{},type,"{}""#,
- location_csv_expr(db, &analysis, vfs, &sm, expr_id),
+ location_csv_expr(db, vfs, &sm, expr_id),
ty.display(db)
);
}
if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
num_expr_type_mismatches += 1;
if verbosity.is_verbose() {
- if let Some((path, start, end)) =
- expr_syntax_range(db, &analysis, vfs, &sm, expr_id)
- {
+ if let Some((path, start, end)) = expr_syntax_range(db, vfs, &sm, expr_id) {
bar.println(format!(
"{} {}:{}-{}:{}: Expected {}, got {}",
path,
@@ -336,7 +486,7 @@ impl flags::AnalysisStats {
} else {
bar.println(format!(
"{}: Expected {}, got {}",
- name,
+ name.display(db),
mismatch.expected.display(db),
mismatch.actual.display(db)
));
@@ -345,7 +495,7 @@ impl flags::AnalysisStats {
if self.output == Some(OutputFormat::Csv) {
println!(
r#"{},mismatch,"{}","{}""#,
- location_csv_expr(db, &analysis, vfs, &sm, expr_id),
+ location_csv_expr(db, vfs, &sm, expr_id),
mismatch.expected.display(db),
mismatch.actual.display(db)
);
@@ -355,7 +505,7 @@ impl flags::AnalysisStats {
if verbosity.is_spammy() {
bar.println(format!(
"In {}: {} exprs, {} unknown, {} partial",
- full_name,
+ full_name(),
num_exprs - previous_exprs,
num_exprs_unknown - previous_unknown,
num_exprs_partially_unknown - previous_partially_unknown
@@ -372,9 +522,7 @@ impl flags::AnalysisStats {
let unknown_or_partial = if ty.is_unknown() {
num_pats_unknown += 1;
if verbosity.is_spammy() {
- if let Some((path, start, end)) =
- pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
- {
+ if let Some((path, start, end)) = pat_syntax_range(db, vfs, &sm, pat_id) {
bar.println(format!(
"{} {}:{}-{}:{}: Unknown type",
path,
@@ -384,7 +532,7 @@ impl flags::AnalysisStats {
end.col,
));
} else {
- bar.println(format!("{name}: Unknown type",));
+ bar.println(format!("{}: Unknown type", name.display(db)));
}
}
true
@@ -398,8 +546,7 @@ impl flags::AnalysisStats {
};
if self.only.is_some() && verbosity.is_spammy() {
// in super-verbose mode for just one function, we print every single pattern
- if let Some((_, start, end)) = pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
- {
+ if let Some((_, start, end)) = pat_syntax_range(db, vfs, &sm, pat_id) {
bar.println(format!(
"{}:{}-{}:{}: {}",
start.line + 1,
@@ -415,16 +562,14 @@ impl flags::AnalysisStats {
if unknown_or_partial && self.output == Some(OutputFormat::Csv) {
println!(
r#"{},type,"{}""#,
- location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+ location_csv_pat(db, vfs, &sm, pat_id),
ty.display(db)
);
}
if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) {
num_pat_type_mismatches += 1;
if verbosity.is_verbose() {
- if let Some((path, start, end)) =
- pat_syntax_range(db, &analysis, vfs, &sm, pat_id)
- {
+ if let Some((path, start, end)) = pat_syntax_range(db, vfs, &sm, pat_id) {
bar.println(format!(
"{} {}:{}-{}:{}: Expected {}, got {}",
path,
@@ -438,7 +583,7 @@ impl flags::AnalysisStats {
} else {
bar.println(format!(
"{}: Expected {}, got {}",
- name,
+ name.display(db),
mismatch.expected.display(db),
mismatch.actual.display(db)
));
@@ -447,7 +592,7 @@ impl flags::AnalysisStats {
if self.output == Some(OutputFormat::Csv) {
println!(
r#"{},mismatch,"{}","{}""#,
- location_csv_pat(db, &analysis, vfs, &sm, pat_id),
+ location_csv_pat(db, vfs, &sm, pat_id),
mismatch.expected.display(db),
mismatch.actual.display(db)
);
@@ -457,7 +602,7 @@ impl flags::AnalysisStats {
if verbosity.is_spammy() {
bar.println(format!(
"In {}: {} pats, {} unknown, {} partial",
- full_name,
+ full_name(),
num_pats - previous_pats,
num_pats_unknown - previous_unknown,
num_pats_partially_unknown - previous_partially_unknown
@@ -468,6 +613,7 @@ impl flags::AnalysisStats {
}
bar.finish_and_clear();
+ let inference_time = inference_sw.elapsed();
eprintln!(
" exprs: {}, ??ty: {} ({}%), ?ty: {} ({}%), !ty: {}",
num_exprs,
@@ -486,12 +632,89 @@ impl flags::AnalysisStats {
percentage(num_pats_partially_unknown, num_pats),
num_pat_type_mismatches
);
+ eprintln!("{:<20} {}", "Inference:", inference_time);
report_metric("unknown type", num_exprs_unknown, "#");
report_metric("type mismatches", num_expr_type_mismatches, "#");
report_metric("pattern unknown type", num_pats_unknown, "#");
report_metric("pattern type mismatches", num_pat_type_mismatches, "#");
+ report_metric("inference time", inference_time.time.as_millis() as u64, "ms");
+ }
- eprintln!("{:<20} {}", "Inference:", inference_sw.elapsed());
+ fn run_body_lowering(
+ &self,
+ db: &RootDatabase,
+ vfs: &Vfs,
+ bodies: &[DefWithBody],
+ verbosity: Verbosity,
+ ) {
+ let mut bar = match verbosity {
+ Verbosity::Quiet | Verbosity::Spammy => ProgressReport::hidden(),
+ _ if self.output.is_some() => ProgressReport::hidden(),
+ _ => ProgressReport::new(bodies.len() as u64),
+ };
+
+ let mut sw = self.stop_watch();
+ bar.tick();
+ for &body_id in bodies {
+ let module = body_id.module(db);
+ let full_name = move || {
+ module
+ .krate()
+ .display_name(db)
+ .map(|it| it.canonical_name().to_string())
+ .into_iter()
+ .chain(
+ module
+ .path_to_root(db)
+ .into_iter()
+ .filter_map(|it| it.name(db))
+ .rev()
+ .chain(Some(body_id.name(db).unwrap_or_else(Name::missing)))
+ .map(|it| it.display(db).to_string()),
+ )
+ .join("::")
+ };
+ if let Some(only_name) = self.only.as_deref() {
+ if body_id.name(db).unwrap_or_else(Name::missing).display(db).to_string()
+ != only_name
+ && full_name() != only_name
+ {
+ continue;
+ }
+ }
+ let msg = move || {
+ if verbosity.is_verbose() {
+ let source = match body_id {
+ DefWithBody::Function(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::Static(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::Const(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::Variant(it) => it.source(db).map(|it| it.syntax().cloned()),
+ DefWithBody::InTypeConst(_) => unimplemented!(),
+ };
+ if let Some(src) = source {
+ let original_file = src.file_id.original_file(db);
+ let path = vfs.file_path(original_file);
+ let syntax_range = src.value.text_range();
+ format!("processing: {} ({} {:?})", full_name(), path, syntax_range)
+ } else {
+ format!("processing: {}", full_name())
+ }
+ } else {
+ format!("processing: {}", full_name())
+ }
+ };
+ if verbosity.is_spammy() {
+ bar.println(msg());
+ }
+ bar.set_message(msg);
+ db.body_with_source_map(body_id.into());
+ bar.inc(1);
+ }
+
+ bar.finish_and_clear();
+ let body_lowering_time = sw.elapsed();
+ eprintln!("{:<20} {}", "Body lowering:", body_lowering_time);
+ report_metric("body lowering time", body_lowering_time.time.as_millis() as u64, "ms");
}
fn stop_watch(&self) -> StopWatch {
@@ -499,46 +722,34 @@ impl flags::AnalysisStats {
}
}
-fn location_csv_expr(
- db: &RootDatabase,
- analysis: &Analysis,
- vfs: &Vfs,
- sm: &BodySourceMap,
- expr_id: ExprId,
-) -> String {
+fn location_csv_expr(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, expr_id: ExprId) -> String {
let src = match sm.expr_syntax(expr_id) {
Ok(s) => s,
Err(SyntheticSyntax) => return "synthetic,,".to_string(),
};
- let root = db.parse_or_expand(src.file_id).unwrap();
+ let root = db.parse_or_expand(src.file_id);
let node = src.map(|e| e.to_node(&root).syntax().clone());
let original_range = node.as_ref().original_file_range(db);
let path = vfs.file_path(original_range.file_id);
- let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+ let line_index = db.line_index(original_range.file_id);
let text_range = original_range.range;
let (start, end) =
(line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
format!("{path},{}:{},{}:{}", start.line + 1, start.col, end.line + 1, end.col)
}
-fn location_csv_pat(
- db: &RootDatabase,
- analysis: &Analysis,
- vfs: &Vfs,
- sm: &BodySourceMap,
- pat_id: PatId,
-) -> String {
+fn location_csv_pat(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, pat_id: PatId) -> String {
let src = match sm.pat_syntax(pat_id) {
Ok(s) => s,
Err(SyntheticSyntax) => return "synthetic,,".to_string(),
};
- let root = db.parse_or_expand(src.file_id).unwrap();
+ let root = db.parse_or_expand(src.file_id);
let node = src.map(|e| {
e.either(|it| it.to_node(&root).syntax().clone(), |it| it.to_node(&root).syntax().clone())
});
let original_range = node.as_ref().original_file_range(db);
let path = vfs.file_path(original_range.file_id);
- let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+ let line_index = db.line_index(original_range.file_id);
let text_range = original_range.range;
let (start, end) =
(line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
@@ -547,18 +758,17 @@ fn location_csv_pat(
fn expr_syntax_range(
db: &RootDatabase,
- analysis: &Analysis,
vfs: &Vfs,
sm: &BodySourceMap,
expr_id: ExprId,
) -> Option<(VfsPath, LineCol, LineCol)> {
let src = sm.expr_syntax(expr_id);
if let Ok(src) = src {
- let root = db.parse_or_expand(src.file_id).unwrap();
+ let root = db.parse_or_expand(src.file_id);
let node = src.map(|e| e.to_node(&root).syntax().clone());
let original_range = node.as_ref().original_file_range(db);
let path = vfs.file_path(original_range.file_id);
- let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+ let line_index = db.line_index(original_range.file_id);
let text_range = original_range.range;
let (start, end) =
(line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
@@ -569,14 +779,13 @@ fn expr_syntax_range(
}
fn pat_syntax_range(
db: &RootDatabase,
- analysis: &Analysis,
vfs: &Vfs,
sm: &BodySourceMap,
pat_id: PatId,
) -> Option<(VfsPath, LineCol, LineCol)> {
let src = sm.pat_syntax(pat_id);
if let Ok(src) = src {
- let root = db.parse_or_expand(src.file_id).unwrap();
+ let root = db.parse_or_expand(src.file_id);
let node = src.map(|e| {
e.either(
|it| it.to_node(&root).syntax().clone(),
@@ -585,7 +794,7 @@ fn pat_syntax_range(
});
let original_range = node.as_ref().original_file_range(db);
let path = vfs.file_path(original_range.file_id);
- let line_index = analysis.file_line_index(original_range.file_id).unwrap();
+ let line_index = db.line_index(original_range.file_id);
let text_range = original_range.range;
let (start, end) =
(line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
index 4006d023d..4306d7212 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -17,9 +17,15 @@ impl flags::Diagnostics {
pub fn run(self) -> anyhow::Result<()> {
let mut cargo_config = CargoConfig::default();
cargo_config.sysroot = Some(RustLibSource::Discover);
+ let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv {
+ let path = vfs::AbsPathBuf::assert(std::env::current_dir()?.join(&p));
+ ProcMacroServerChoice::Explicit(path)
+ } else {
+ ProcMacroServerChoice::Sysroot
+ };
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: !self.disable_build_scripts,
- with_proc_macro_server: ProcMacroServerChoice::Sysroot,
+ with_proc_macro_server,
prefill_caches: false,
};
let (host, _vfs, _proc_macro) =
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
index 770612cc9..208a4e6ec 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
@@ -78,8 +78,16 @@ xflags::xflags! {
optional --disable-build-scripts
/// Don't use expand proc macros.
optional --disable-proc-macros
- /// Only resolve names, don't run type inference.
+ /// Skip body lowering.
+ optional --skip-lowering
+ /// Skip type inference.
optional --skip-inference
+ /// Skip lowering to mir
+ optional --skip-mir-stats
+ /// Skip data layout calculation
+ optional --skip-data-layout
+ /// Skip const evaluation
+ optional --skip-const-eval
}
cmd diagnostics {
@@ -90,6 +98,8 @@ xflags::xflags! {
optional --disable-build-scripts
/// Don't use expand proc macros.
optional --disable-proc-macros
+ /// Run a custom proc-macro-srv binary.
+ optional --proc-macro-srv path: PathBuf
}
cmd ssr {
@@ -104,14 +114,15 @@ xflags::xflags! {
optional --debug snippet: String
}
- cmd proc-macro {}
-
cmd lsif {
required path: PathBuf
}
cmd scip {
required path: PathBuf
+
+ /// The output path where the SCIP file will be written to. Defaults to `index.scip`.
+ optional --output path: PathBuf
}
}
}
@@ -139,7 +150,6 @@ pub enum RustAnalyzerCmd {
Diagnostics(Diagnostics),
Ssr(Ssr),
Search(Search),
- ProcMacro(ProcMacro),
Lsif(Lsif),
Scip(Scip),
}
@@ -172,12 +182,16 @@ pub struct AnalysisStats {
pub parallel: bool,
pub memory_usage: bool,
pub source_stats: bool,
+ pub skip_lowering: bool,
+ pub skip_inference: bool,
+ pub skip_mir_stats: bool,
+ pub skip_data_layout: bool,
+ pub skip_const_eval: bool,
pub only: Option<String>,
pub with_deps: bool,
pub no_sysroot: bool,
pub disable_build_scripts: bool,
pub disable_proc_macros: bool,
- pub skip_inference: bool,
}
#[derive(Debug)]
@@ -186,6 +200,7 @@ pub struct Diagnostics {
pub disable_build_scripts: bool,
pub disable_proc_macros: bool,
+ pub proc_macro_srv: Option<PathBuf>,
}
#[derive(Debug)]
@@ -201,9 +216,6 @@ pub struct Search {
}
#[derive(Debug)]
-pub struct ProcMacro;
-
-#[derive(Debug)]
pub struct Lsif {
pub path: PathBuf,
}
@@ -211,6 +223,7 @@ pub struct Lsif {
#[derive(Debug)]
pub struct Scip {
pub path: PathBuf,
+ pub output: Option<PathBuf>,
}
impl RustAnalyzer {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
index 5a958d963..4e8f99971 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -1,14 +1,17 @@
//! Loads a Cargo project into a static instance of analysis, without support
//! for incorporating changes.
-use std::{convert::identity, path::Path, sync::Arc};
+use std::path::Path;
-use anyhow::Result;
+use anyhow::{anyhow, Result};
use crossbeam_channel::{unbounded, Receiver};
-use hir::db::DefDatabase;
use ide::{AnalysisHost, Change};
-use ide_db::{base_db::CrateGraph, FxHashMap};
+use ide_db::{
+ base_db::{CrateGraph, ProcMacros},
+ FxHashMap,
+};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
+use triomphe::Arc;
use vfs::{loader::Handle, AbsPath, AbsPathBuf};
use crate::reload::{load_proc_macro, ProjectFolders, SourceRootConfig};
@@ -24,7 +27,7 @@ pub struct LoadCargoConfig {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProcMacroServerChoice {
Sysroot,
- Explicit(AbsPathBuf, Vec<String>),
+ Explicit(AbsPathBuf),
None,
}
@@ -66,23 +69,17 @@ pub fn load_workspace(
Box::new(loader)
};
- let proc_macro_client = match &load_config.with_proc_macro_server {
+ let proc_macro_server = match &load_config.with_proc_macro_server {
ProcMacroServerChoice::Sysroot => ws
.find_sysroot_proc_macro_srv()
- .ok_or_else(|| "failed to find sysroot proc-macro server".to_owned())
- .and_then(|it| {
- ProcMacroServer::spawn(it, identity::<&[&str]>(&[])).map_err(|e| e.to_string())
- }),
- ProcMacroServerChoice::Explicit(path, args) => {
- ProcMacroServer::spawn(path.clone(), args).map_err(|e| e.to_string())
+ .and_then(|it| ProcMacroServer::spawn(it).map_err(Into::into)),
+ ProcMacroServerChoice::Explicit(path) => {
+ ProcMacroServer::spawn(path.clone()).map_err(Into::into)
}
- ProcMacroServerChoice::None => Err("proc macro server disabled".to_owned()),
+ ProcMacroServerChoice::None => Err(anyhow!("proc macro server disabled")),
};
- let crate_graph = ws.to_crate_graph(
- &mut |_, path: &AbsPath| {
- load_proc_macro(proc_macro_client.as_ref().map_err(|e| &**e), path, &[])
- },
+ let (crate_graph, proc_macros) = ws.to_crate_graph(
&mut |path: &AbsPath| {
let contents = loader.load_sync(path);
let path = vfs::VfsPath::from(path.to_path_buf());
@@ -91,6 +88,28 @@ pub fn load_workspace(
},
extra_env,
);
+ let proc_macros = {
+ let proc_macro_server = match &proc_macro_server {
+ Ok(it) => Ok(it),
+ Err(e) => Err(e.to_string()),
+ };
+ proc_macros
+ .into_iter()
+ .map(|(crate_id, path)| {
+ (
+ crate_id,
+ path.map_or_else(
+ |_| Err("proc macro crate is missing dylib".to_owned()),
+ |(_, path)| {
+ proc_macro_server.as_ref().map_err(Clone::clone).and_then(
+ |proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]),
+ )
+ },
+ ),
+ )
+ })
+ .collect()
+ };
let project_folders = ProjectFolders::new(&[ws], &[]);
loader.set_config(vfs::loader::Config {
@@ -100,17 +119,23 @@ pub fn load_workspace(
});
tracing::debug!("crate graph: {:?}", crate_graph);
- let host =
- load_crate_graph(crate_graph, project_folders.source_root_config, &mut vfs, &receiver);
+ let host = load_crate_graph(
+ crate_graph,
+ proc_macros,
+ project_folders.source_root_config,
+ &mut vfs,
+ &receiver,
+ );
if load_config.prefill_caches {
host.analysis().parallel_prime_caches(1, |_| {})?;
}
- Ok((host, vfs, proc_macro_client.ok()))
+ Ok((host, vfs, proc_macro_server.ok()))
}
fn load_crate_graph(
crate_graph: CrateGraph,
+ proc_macros: ProcMacros,
source_root_config: SourceRootConfig,
vfs: &mut vfs::Vfs,
receiver: &Receiver<vfs::loader::Message>,
@@ -119,7 +144,7 @@ fn load_crate_graph(
let mut host = AnalysisHost::new(lru_cap);
let mut analysis_change = Change::new();
- host.raw_database_mut().set_enable_proc_attr_macros(true);
+ host.raw_database_mut().enable_proc_attr_macros();
// wait until Vfs has loaded all roots
for task in receiver {
@@ -139,9 +164,9 @@ fn load_crate_graph(
let changes = vfs.take_changes();
for file in changes {
if file.exists() {
- let contents = vfs.file_contents(file.file_id).to_vec();
- if let Ok(text) = String::from_utf8(contents) {
- analysis_change.change_file(file.file_id, Some(Arc::new(text)))
+ let contents = vfs.file_contents(file.file_id);
+ if let Ok(text) = std::str::from_utf8(contents) {
+ analysis_change.change_file(file.file_id, Some(Arc::from(text)))
}
}
}
@@ -149,6 +174,7 @@ fn load_crate_graph(
analysis_change.set_roots(source_roots);
analysis_change.set_crate_graph(crate_graph);
+ analysis_change.set_proc_macros(proc_macros);
host.apply_change(analysis_change);
host
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs
index d459dd115..c236f9c7f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/progress_report.rs
@@ -4,41 +4,29 @@
use std::io::{self, Write};
/// A Simple ASCII Progress Bar
-pub(crate) struct ProgressReport {
+pub(crate) struct ProgressReport<'a> {
curr: f32,
text: String,
hidden: bool,
len: u64,
pos: u64,
- msg: String,
+ msg: Option<Box<dyn Fn() -> String + 'a>>,
}
-impl ProgressReport {
- pub(crate) fn new(len: u64) -> ProgressReport {
- ProgressReport {
- curr: 0.0,
- text: String::new(),
- hidden: false,
- len,
- pos: 0,
- msg: String::new(),
- }
+impl<'a> ProgressReport<'a> {
+ pub(crate) fn new(len: u64) -> ProgressReport<'a> {
+ ProgressReport { curr: 0.0, text: String::new(), hidden: false, len, pos: 0, msg: None }
}
- pub(crate) fn hidden() -> ProgressReport {
- ProgressReport {
- curr: 0.0,
- text: String::new(),
- hidden: true,
- len: 0,
- pos: 0,
- msg: String::new(),
- }
+ pub(crate) fn hidden() -> ProgressReport<'a> {
+ ProgressReport { curr: 0.0, text: String::new(), hidden: true, len: 0, pos: 0, msg: None }
}
- pub(crate) fn set_message(&mut self, msg: &str) {
- self.msg = msg.to_string();
+ pub(crate) fn set_message(&mut self, msg: impl Fn() -> String + 'a) {
+ if !self.hidden {
+ self.msg = Some(Box::new(msg));
+ }
self.tick();
}
@@ -67,7 +55,12 @@ impl ProgressReport {
return;
}
let percent = (self.curr * 100.0) as u32;
- let text = format!("{}/{} {percent:3>}% {}", self.pos, self.len, self.msg);
+ let text = format!(
+ "{}/{} {percent:3>}% {}",
+ self.pos,
+ self.len,
+ self.msg.as_ref().map_or_else(|| String::new(), |it| it())
+ );
self.update_text(&text);
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 3e5e40750..b0b724bdf 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -2,6 +2,7 @@
use std::{
collections::{HashMap, HashSet},
+ path::PathBuf,
time::Instant,
};
@@ -9,7 +10,6 @@ use crate::{
cli::load_cargo::ProcMacroServerChoice,
line_index::{LineEndings, LineIndex, PositionEncoding},
};
-use hir::Name;
use ide::{
LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId,
TokenStaticData,
@@ -66,7 +66,6 @@ impl flags::Scip {
.as_os_str()
.to_str()
.ok_or(anyhow::anyhow!("Unable to normalize project_root path"))?
- .to_string()
),
text_document_encoding: scip_types::TextEncoding::UTF8.into(),
special_fields: Default::default(),
@@ -167,7 +166,8 @@ impl flags::Scip {
special_fields: Default::default(),
};
- scip::write_message_to_file("index.scip", index)
+ let out_path = self.output.unwrap_or_else(|| PathBuf::from(r"index.scip"));
+ scip::write_message_to_file(out_path, index)
.map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?;
eprintln!("Generating SCIP finished {:?}", now.elapsed());
@@ -210,13 +210,12 @@ fn new_descriptor_str(
}
}
-fn new_descriptor(name: Name, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor {
- let mut name = name.to_string();
- if name.contains("'") {
- name = format!("`{name}`");
+fn new_descriptor(name: &str, suffix: scip_types::descriptor::Suffix) -> scip_types::Descriptor {
+ if name.contains('\'') {
+ new_descriptor_str(&format!("`{name}`"), suffix)
+ } else {
+ new_descriptor_str(&name, suffix)
}
-
- new_descriptor_str(name.as_str(), suffix)
}
/// Loosely based on `def_to_moniker`
@@ -236,7 +235,7 @@ fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
.iter()
.map(|desc| {
new_descriptor(
- desc.name.clone(),
+ &desc.name,
match desc.desc {
MonikerDescriptorKind::Namespace => Namespace,
MonikerDescriptorKind::Type => Type,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index c35cce103..6355c620f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -7,13 +7,14 @@
//! configure the server itself, feature flags are passed into analysis, and
//! tweak things like automatic insertion of `()` in completions.
-use std::{fmt, iter, path::PathBuf};
+use std::{fmt, iter, ops::Not, path::PathBuf};
+use cfg::{CfgAtom, CfgDiff};
use flycheck::FlycheckConfig;
use ide::{
AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig,
- JoinLinesConfig, Snippet, SnippetScope,
+ JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope,
};
use ide_db::{
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
@@ -23,11 +24,10 @@ use itertools::Itertools;
use lsp_types::{ClientCapabilities, MarkupKind};
use project_model::{
CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource,
- UnsetTestCrates,
};
use rustc_hash::{FxHashMap, FxHashSet};
use serde::{de::DeserializeOwned, Deserialize};
-use vfs::AbsPathBuf;
+use vfs::{AbsPath, AbsPathBuf};
use crate::{
caps::completion_item_edit_resolve,
@@ -101,6 +101,8 @@ config_data! {
/// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
/// avoid checking unnecessary things.
cargo_buildScripts_useRustcWrapper: bool = "true",
+ /// List of cfg options to enable with the given values.
+ cargo_cfgs: FxHashMap<String, String> = "{}",
/// Extra arguments that are passed to every cargo invocation.
cargo_extraArgs: Vec<String> = "[]",
/// Extra environment variables that will be set when running cargo, rustc
@@ -128,7 +130,7 @@ config_data! {
// FIXME(@poliorcetics): move to multiple targets here too, but this will need more work
// than `checkOnSave_target`
cargo_target: Option<String> = "null",
- /// Unsets `#[cfg(test)]` for the specified crates.
+ /// Unsets the implicit `#[cfg(test)]` for the specified crates.
cargo_unsetTest: Vec<String> = "[\"core\"]",
/// Run the check command for diagnostics on save.
@@ -281,6 +283,8 @@ config_data! {
/// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
highlightRelated_breakPoints_enable: bool = "true",
+ /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure.
+ highlightRelated_closureCaptures_enable: bool = "true",
/// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
highlightRelated_exitPoints_enable: bool = "true",
/// Enables highlighting of related references while the cursor is on any identifier.
@@ -311,8 +315,18 @@ config_data! {
/// Whether to show keyword hover popups. Only applies when
/// `#rust-analyzer.hover.documentation.enable#` is set.
hover_documentation_keywords_enable: bool = "true",
- /// Use markdown syntax for links in hover.
+ /// Use markdown syntax for links on hover.
hover_links_enable: bool = "true",
+ /// How to render the align information in a memory layout hover.
+ hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = "\"hexadecimal\"",
+ /// Whether to show memory layout data on hover.
+ hover_memoryLayout_enable: bool = "true",
+ /// How to render the niche information in a memory layout hover.
+ hover_memoryLayout_niches: Option<bool> = "false",
+ /// How to render the offset information in a memory layout hover.
+ hover_memoryLayout_offset: Option<MemoryLayoutHoverRenderKindDef> = "\"hexadecimal\"",
+ /// How to render the size information in a memory layout hover.
+ hover_memoryLayout_size: Option<MemoryLayoutHoverRenderKindDef> = "\"both\"",
/// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.
imports_granularity_enforce: bool = "false",
@@ -336,8 +350,12 @@ config_data! {
/// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
/// to always show them).
inlayHints_closingBraceHints_minLines: usize = "25",
+ /// Whether to show inlay hints for closure captures.
+ inlayHints_closureCaptureHints_enable: bool = "false",
/// Whether to show inlay type hints for return types of closures.
inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = "\"never\"",
+ /// Closure notation in type and chaining inlay hints.
+ inlayHints_closureStyle: ClosureStyle = "\"impl_fn\"",
/// Whether to show enum variant discriminant hints.
inlayHints_discriminantHints_enable: DiscriminantHintsDef = "\"never\"",
/// Whether to show inlay hints for type adjustments.
@@ -418,6 +436,8 @@ config_data! {
/// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
lru_capacity: Option<usize> = "null",
+ /// Sets the LRU capacity of the specified queries.
+ lru_query_capacities: FxHashMap<Box<str>, usize> = "{}",
/// Whether to show `can't find Cargo.toml` error message.
notifications_cargoTomlNotFound: bool = "true",
@@ -433,8 +453,7 @@ config_data! {
///
/// This config takes a map of crate names with the exported proc-macro names to ignore as values.
procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>> = "{}",
- /// Internal config, path to proc-macro server executable (typically,
- /// this is rust-analyzer itself, but we override this in tests).
+ /// Internal config, path to proc-macro server executable.
procMacro_server: Option<PathBuf> = "null",
/// Exclude imports from find-all-references.
@@ -474,6 +493,8 @@ config_data! {
/// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra
/// doc links.
semanticHighlighting_doc_comment_inject_enable: bool = "true",
+ /// Whether the server is allowed to emit non-standard tokens and modifiers.
+ semanticHighlighting_nonStandardTokens: bool = "true",
/// Use semantic tokens for operators.
///
/// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when
@@ -484,7 +505,7 @@ config_data! {
/// When enabled, rust-analyzer will emit special token types for operator tokens instead
/// of the generic `operator` token type.
semanticHighlighting_operator_specialization_enable: bool = "false",
- /// Use semantic tokens for punctuations.
+ /// Use semantic tokens for punctuation.
///
/// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when
/// they are tagged with modifiers or have a special role.
@@ -492,7 +513,7 @@ config_data! {
/// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro
/// calls.
semanticHighlighting_punctuation_separate_macro_bang: bool = "false",
- /// Use specialized semantic tokens for punctuations.
+ /// Use specialized semantic tokens for punctuation.
///
/// When enabled, rust-analyzer will emit special token types for punctuation tokens instead
/// of the generic `punctuation` token type.
@@ -531,8 +552,9 @@ impl Default for ConfigData {
#[derive(Debug, Clone)]
pub struct Config {
- pub discovered_projects: Option<Vec<ProjectManifest>>,
- pub workspace_roots: Vec<AbsPathBuf>,
+ discovered_projects: Vec<ProjectManifest>,
+ /// The workspace roots as registered by the LSP client
+ workspace_roots: Vec<AbsPathBuf>,
caps: lsp_types::ClientCapabilities,
root_path: AbsPathBuf,
data: ConfigData,
@@ -570,6 +592,7 @@ pub struct LensConfig {
// runnables
pub run: bool,
pub debug: bool,
+ pub interpret: bool,
// implementations
pub implementations: bool,
@@ -707,11 +730,11 @@ pub struct ClientCommandsConfig {
}
#[derive(Debug)]
-pub struct ConfigUpdateError {
+pub struct ConfigError {
errors: Vec<(String, serde_json::Error)>,
}
-impl fmt::Display for ConfigUpdateError {
+impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let errors = self.errors.iter().format_with("\n", |(key, e), f| {
f(key)?;
@@ -720,8 +743,7 @@ impl fmt::Display for ConfigUpdateError {
});
write!(
f,
- "rust-analyzer found {} invalid config value{}:\n{}",
- self.errors.len(),
+ "invalid config value{}:\n{}",
if self.errors.len() == 1 { "" } else { "s" },
errors
)
@@ -738,7 +760,7 @@ impl Config {
caps,
data: ConfigData::default(),
detached_files: Vec::new(),
- discovered_projects: None,
+ discovered_projects: Vec::new(),
root_path,
snippets: Default::default(),
workspace_roots,
@@ -751,10 +773,20 @@ impl Config {
if discovered.is_empty() {
tracing::error!("failed to find any projects in {:?}", &self.workspace_roots);
}
- self.discovered_projects = Some(discovered);
+ self.discovered_projects = discovered;
}
- pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigUpdateError> {
+ pub fn remove_workspace(&mut self, path: &AbsPath) {
+ if let Some(position) = self.workspace_roots.iter().position(|it| it == path) {
+ self.workspace_roots.remove(position);
+ }
+ }
+
+ pub fn add_workspaces(&mut self, paths: impl Iterator<Item = AbsPathBuf>) {
+ self.workspace_roots.extend(paths);
+ }
+
+ pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigError> {
tracing::info!("updating config from JSON: {:#}", json);
if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
return Ok(());
@@ -801,7 +833,7 @@ impl Config {
if errors.is_empty() {
Ok(())
} else {
- Err(ConfigUpdateError { errors })
+ Err(ConfigError { errors })
}
}
@@ -856,25 +888,19 @@ impl Config {
pub fn linked_projects(&self) -> Vec<LinkedProject> {
match self.data.linkedProjects.as_slice() {
[] => {
- match self.discovered_projects.as_ref() {
- Some(discovered_projects) => {
- let exclude_dirs: Vec<_> = self
- .data
- .files_excludeDirs
- .iter()
- .map(|p| self.root_path.join(p))
- .collect();
- discovered_projects
- .iter()
- .filter(|(ProjectManifest::ProjectJson(path) | ProjectManifest::CargoToml(path))| {
+ let exclude_dirs: Vec<_> =
+ self.data.files_excludeDirs.iter().map(|p| self.root_path.join(p)).collect();
+ self.discovered_projects
+ .iter()
+ .filter(
+ |(ProjectManifest::ProjectJson(path)
+ | ProjectManifest::CargoToml(path))| {
!exclude_dirs.iter().any(|p| path.starts_with(p))
- })
- .cloned()
- .map(LinkedProject::from)
- .collect()
- }
- None => Vec::new(),
- }
+ },
+ )
+ .cloned()
+ .map(LinkedProject::from)
+ .collect()
}
linked_projects => linked_projects
.iter()
@@ -1013,6 +1039,11 @@ impl Config {
.is_some()
}
+ pub fn semantics_tokens_augments_syntax_tokens(&self) -> bool {
+ try_!(self.caps.text_document.as_ref()?.semantic_tokens.as_ref()?.augments_syntax_tokens?)
+ .unwrap_or(false)
+ }
+
pub fn position_encoding(&self) -> PositionEncoding {
negotiated_encoding(&self.caps)
}
@@ -1025,6 +1056,10 @@ impl Config {
self.experimental("codeActionGroup")
}
+ pub fn local_docs(&self) -> bool {
+ self.experimental("localDocs")
+ }
+
pub fn open_server_logs(&self) -> bool {
self.experimental("openServerLogs")
}
@@ -1085,27 +1120,27 @@ impl Config {
extra_env
}
- pub fn lru_capacity(&self) -> Option<usize> {
+ pub fn lru_parse_query_capacity(&self) -> Option<usize> {
self.data.lru_capacity
}
- pub fn proc_macro_srv(&self) -> Option<(AbsPathBuf, /* is path explicitly set */ bool)> {
- if !self.data.procMacro_enable {
- return None;
- }
- Some(match &self.data.procMacro_server {
- Some(it) => (
- AbsPathBuf::try_from(it.clone()).unwrap_or_else(|path| self.root_path.join(path)),
- true,
- ),
- None => (AbsPathBuf::assert(std::env::current_exe().ok()?), false),
- })
+ pub fn lru_query_capacities(&self) -> Option<&FxHashMap<Box<str>, usize>> {
+ self.data.lru_query_capacities.is_empty().not().then(|| &self.data.lru_query_capacities)
+ }
+
+ pub fn proc_macro_srv(&self) -> Option<AbsPathBuf> {
+ let path = self.data.procMacro_server.clone()?;
+ Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(&path)))
}
pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
&self.data.procMacro_ignored
}
+ pub fn expand_proc_macros(&self) -> bool {
+ self.data.procMacro_enable
+ }
+
pub fn expand_proc_attr_macros(&self) -> bool {
self.data.procMacro_enable && self.data.procMacro_attributes_enable
}
@@ -1164,7 +1199,34 @@ impl Config {
sysroot,
sysroot_src,
rustc_source,
- unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()),
+ cfg_overrides: project_model::CfgOverrides {
+ global: CfgDiff::new(
+ self.data
+ .cargo_cfgs
+ .iter()
+ .map(|(key, val)| {
+ if val.is_empty() {
+ CfgAtom::Flag(key.into())
+ } else {
+ CfgAtom::KeyValue { key: key.into(), value: val.into() }
+ }
+ })
+ .collect(),
+ vec![],
+ )
+ .unwrap(),
+ selective: self
+ .data
+ .cargo_unsetTest
+ .iter()
+ .map(|it| {
+ (
+ it.clone(),
+ CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap(),
+ )
+ })
+ .collect(),
+ },
wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper,
invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy {
InvocationStrategy::Once => project_model::InvocationStrategy::Once,
@@ -1291,6 +1353,13 @@ impl Config {
hide_closure_initialization_hints: self
.data
.inlayHints_typeHints_hideClosureInitialization,
+ closure_style: match self.data.inlayHints_closureStyle {
+ ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn,
+ ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation,
+ ClosureStyle::WithId => hir::ClosureStyle::ClosureWithId,
+ ClosureStyle::Hide => hir::ClosureStyle::Hide,
+ },
+ closure_capture_hints: self.data.inlayHints_closureCaptureHints_enable,
adjustment_hints: match self.data.inlayHints_expressionAdjustmentHints_enable {
AdjustmentHintsDef::Always => ide::AdjustmentHints::Always,
AdjustmentHintsDef::Never => match self.data.inlayHints_reborrowHints_enable {
@@ -1409,6 +1478,9 @@ impl Config {
LensConfig {
run: self.data.lens_enable && self.data.lens_run_enable,
debug: self.data.lens_enable && self.data.lens_debug_enable,
+ interpret: self.data.lens_enable
+ && self.data.lens_run_enable
+ && self.data.interpret_tests,
implementations: self.data.lens_enable && self.data.lens_implementations_enable,
method_refs: self.data.lens_enable && self.data.lens_references_method_enable,
refs_adt: self.data.lens_enable && self.data.lens_references_adt_enable,
@@ -1430,6 +1502,10 @@ impl Config {
}
}
+ pub fn highlighting_non_standard_tokens(&self) -> bool {
+ self.data.semanticHighlighting_nonStandardTokens
+ }
+
pub fn highlighting_config(&self) -> HighlightConfig {
HighlightConfig {
strings: self.data.semanticHighlighting_strings_enable,
@@ -1446,8 +1522,19 @@ impl Config {
}
pub fn hover(&self) -> HoverConfig {
+ let mem_kind = |kind| match kind {
+ MemoryLayoutHoverRenderKindDef::Both => MemoryLayoutHoverRenderKind::Both,
+ MemoryLayoutHoverRenderKindDef::Decimal => MemoryLayoutHoverRenderKind::Decimal,
+ MemoryLayoutHoverRenderKindDef::Hexadecimal => MemoryLayoutHoverRenderKind::Hexadecimal,
+ };
HoverConfig {
links_in_hover: self.data.hover_links_enable,
+ memory_layout: self.data.hover_memoryLayout_enable.then_some(MemoryLayoutHoverConfig {
+ size: self.data.hover_memoryLayout_size.map(mem_kind),
+ offset: self.data.hover_memoryLayout_offset.map(mem_kind),
+ alignment: self.data.hover_memoryLayout_alignment.map(mem_kind),
+ niches: self.data.hover_memoryLayout_niches.unwrap_or_default(),
+ }),
documentation: self.data.hover_documentation_enable,
format: {
let is_markdown = try_or_def!(self
@@ -1467,7 +1554,6 @@ impl Config {
}
},
keywords: self.data.hover_documentation_keywords_enable,
- interpret_tests: self.data.interpret_tests,
}
}
@@ -1537,6 +1623,7 @@ impl Config {
break_points: self.data.highlightRelated_breakPoints_enable,
exit_points: self.data.highlightRelated_exitPoints_enable,
yield_points: self.data.highlightRelated_yieldPoints_enable,
+ closure_captures: self.data.highlightRelated_closureCaptures_enable,
}
}
@@ -1657,6 +1744,9 @@ mod de_unit_v {
named_unit_variant!(reborrow);
named_unit_variant!(fieldless);
named_unit_variant!(with_block);
+ named_unit_variant!(decimal);
+ named_unit_variant!(hexadecimal);
+ named_unit_variant!(both);
}
#[derive(Deserialize, Debug, Clone, Copy)]
@@ -1798,6 +1888,15 @@ enum ClosureReturnTypeHintsDef {
}
#[derive(Deserialize, Debug, Clone)]
+#[serde(rename_all = "snake_case")]
+enum ClosureStyle {
+ ImplFn,
+ RustAnalyzer,
+ WithId,
+ Hide,
+}
+
+#[derive(Deserialize, Debug, Clone)]
#[serde(untagged)]
enum ReborrowHintsDef {
#[serde(deserialize_with = "true_or_always")]
@@ -1878,6 +1977,18 @@ enum WorkspaceSymbolSearchKindDef {
AllSymbols,
}
+#[derive(Deserialize, Debug, Copy, Clone)]
+#[serde(rename_all = "snake_case")]
+#[serde(untagged)]
+pub enum MemoryLayoutHoverRenderKindDef {
+ #[serde(deserialize_with = "de_unit_v::decimal")]
+ Decimal,
+ #[serde(deserialize_with = "de_unit_v::hexadecimal")]
+ Hexadecimal,
+ #[serde(deserialize_with = "de_unit_v::both")]
+ Both,
+}
+
macro_rules! _config_data {
(struct $name:ident {
$(
@@ -1940,7 +2051,7 @@ fn get_field<T: DeserializeOwned>(
alias: Option<&'static str>,
default: &str,
) -> T {
- // XXX: check alias first, to work-around the VS Code where it pre-fills the
+ // XXX: check alias first, to work around the VS Code where it pre-fills the
// defaults instead of sending an empty object.
alias
.into_iter()
@@ -1960,7 +2071,9 @@ fn get_field<T: DeserializeOwned>(
None
}
})
- .unwrap_or_else(|| serde_json::from_str(default).unwrap())
+ .unwrap_or_else(|| {
+ serde_json::from_str(default).unwrap_or_else(|e| panic!("{e} on: `{default}`"))
+ })
}
fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json::Value {
@@ -2020,6 +2133,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
"FxHashMap<String, String>" => set! {
"type": "object",
},
+ "FxHashMap<Box<str>, usize>" => set! {
+ "type": "object",
+ },
"Option<usize>" => set! {
"type": ["null", "integer"],
"minimum": 0,
@@ -2169,8 +2285,8 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
"enumDescriptions": [
"Always show adjustment hints as prefix (`*expr`).",
"Always show adjustment hints as postfix (`expr.*`).",
- "Show prefix or postfix depending on which uses less parenthesis, prefering prefix.",
- "Show prefix or postfix depending on which uses less parenthesis, prefering postfix.",
+ "Show prefix or postfix depending on which uses less parenthesis, preferring prefix.",
+ "Show prefix or postfix depending on which uses less parenthesis, preferring postfix.",
]
},
"CargoFeaturesDef" => set! {
@@ -2275,6 +2391,32 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
},
],
},
+ "ClosureStyle" => set! {
+ "type": "string",
+ "enum": ["impl_fn", "rust_analyzer", "with_id", "hide"],
+ "enumDescriptions": [
+ "`impl_fn`: `impl FnMut(i32, u64) -> i8`",
+ "`rust_analyzer`: `|i32, u64| -> i8`",
+ "`with_id`: `{closure#14352}`, where that id is the unique number of the closure in r-a internals",
+ "`hide`: Shows `...` for every closure type",
+ ],
+ },
+ "Option<MemoryLayoutHoverRenderKindDef>" => set! {
+ "anyOf": [
+ {
+ "type": "null"
+ },
+ {
+ "type": "string",
+ "enum": ["both", "decimal", "hexadecimal", ],
+ "enumDescriptions": [
+ "Render as 12 (0xC)",
+ "Render as 12",
+ "Render as 0xC"
+ ],
+ },
+ ],
+ },
_ => panic!("missing entry for {ty}: {default}"),
}
@@ -2384,4 +2526,43 @@ mod tests {
fn remove_ws(text: &str) -> String {
text.replace(char::is_whitespace, "")
}
+
+ #[test]
+ fn proc_macro_srv_null() {
+ let mut config =
+ Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]);
+ config
+ .update(serde_json::json!({
+ "procMacro_server": null,
+ }))
+ .unwrap();
+ assert_eq!(config.proc_macro_srv(), None);
+ }
+
+ #[test]
+ fn proc_macro_srv_abs() {
+ let mut config =
+ Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]);
+ config
+ .update(serde_json::json!({
+ "procMacro": {"server": project_root().display().to_string()}
+ }))
+ .unwrap();
+ assert_eq!(config.proc_macro_srv(), Some(AbsPathBuf::try_from(project_root()).unwrap()));
+ }
+
+ #[test]
+ fn proc_macro_srv_rel() {
+ let mut config =
+ Config::new(AbsPathBuf::try_from(project_root()).unwrap(), Default::default(), vec![]);
+ config
+ .update(serde_json::json!({
+ "procMacro": {"server": "./server"}
+ }))
+ .unwrap();
+ assert_eq!(
+ config.proc_macro_srv(),
+ Some(AbsPathBuf::try_from(project_root().join("./server")).unwrap())
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
index 83b03fe47..33422fd05 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
@@ -1,15 +1,16 @@
//! Book keeping for keeping diagnostics easily in sync with the client.
pub(crate) mod to_proto;
-use std::{mem, sync::Arc};
+use std::mem;
use ide::FileId;
use ide_db::FxHashMap;
-use stdx::hash::{NoHashHashMap, NoHashHashSet};
+use nohash_hasher::{IntMap, IntSet};
+use triomphe::Arc;
use crate::lsp_ext;
-pub(crate) type CheckFixes = Arc<NoHashHashMap<usize, NoHashHashMap<FileId, Vec<Fix>>>>;
+pub(crate) type CheckFixes = Arc<IntMap<usize, IntMap<FileId, Vec<Fix>>>>;
#[derive(Debug, Default, Clone)]
pub struct DiagnosticsMapConfig {
@@ -20,12 +21,12 @@ pub struct DiagnosticsMapConfig {
#[derive(Debug, Default, Clone)]
pub(crate) struct DiagnosticCollection {
- // FIXME: should be NoHashHashMap<FileId, Vec<ra_id::Diagnostic>>
- pub(crate) native: NoHashHashMap<FileId, Vec<lsp_types::Diagnostic>>,
+ // FIXME: should be IntMap<FileId, Vec<ra_id::Diagnostic>>
+ pub(crate) native: IntMap<FileId, Vec<lsp_types::Diagnostic>>,
// FIXME: should be Vec<flycheck::Diagnostic>
- pub(crate) check: NoHashHashMap<usize, NoHashHashMap<FileId, Vec<lsp_types::Diagnostic>>>,
+ pub(crate) check: IntMap<usize, IntMap<FileId, Vec<lsp_types::Diagnostic>>>,
pub(crate) check_fixes: CheckFixes,
- changes: NoHashHashSet<FileId>,
+ changes: IntSet<FileId>,
}
#[derive(Debug, Clone)]
@@ -105,7 +106,7 @@ impl DiagnosticCollection {
native.chain(check)
}
- pub(crate) fn take_changes(&mut self) -> Option<NoHashHashSet<FileId>> {
+ pub(crate) fn take_changes(&mut self) -> Option<IntSet<FileId>> {
if self.changes.is_empty() {
return None;
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 415fa4e02..e1d1130ff 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -3,7 +3,6 @@
use std::collections::HashMap;
use flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan};
-use ide_db::line_index::WideEncoding;
use itertools::Itertools;
use stdx::format_to;
use vfs::{AbsPath, AbsPathBuf};
@@ -80,37 +79,33 @@ fn position(
position_encoding: &PositionEncoding,
span: &DiagnosticSpan,
line_offset: usize,
- column_offset: usize,
+ column_offset_utf32: usize,
) -> lsp_types::Position {
let line_index = line_offset - span.line_start;
- let mut true_column_offset = column_offset;
- if let Some(line) = span.text.get(line_index) {
- if line.text.chars().count() == line.text.len() {
- // all one byte utf-8 char
- return lsp_types::Position {
- line: (line_offset as u32).saturating_sub(1),
- character: (column_offset as u32).saturating_sub(1),
- };
- }
- let mut char_offset = 0;
- let len_func = match position_encoding {
- PositionEncoding::Utf8 => char::len_utf8,
- PositionEncoding::Wide(WideEncoding::Utf16) => char::len_utf16,
- PositionEncoding::Wide(WideEncoding::Utf32) => |_| 1,
- };
- for c in line.text.chars() {
- char_offset += 1;
- if char_offset > column_offset {
- break;
+ let column_offset_encoded = match span.text.get(line_index) {
+ // Fast path.
+ Some(line) if line.text.is_ascii() => column_offset_utf32,
+ Some(line) => {
+ let line_prefix_len = line
+ .text
+ .char_indices()
+ .take(column_offset_utf32)
+ .last()
+ .map(|(pos, c)| pos + c.len_utf8())
+ .unwrap_or(0);
+ let line_prefix = &line.text[..line_prefix_len];
+ match position_encoding {
+ PositionEncoding::Utf8 => line_prefix.len(),
+ PositionEncoding::Wide(enc) => enc.measure(line_prefix),
}
- true_column_offset += len_func(c) - 1;
}
- }
+ None => column_offset_utf32,
+ };
lsp_types::Position {
line: (line_offset as u32).saturating_sub(1),
- character: (true_column_offset as u32).saturating_sub(1),
+ character: (column_offset_encoded as u32).saturating_sub(1),
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
index 313bb2ec8..4e57c6eb6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
@@ -4,6 +4,7 @@ use std::{fmt, panic, thread};
use ide::Cancelled;
use lsp_server::ExtractError;
use serde::{de::DeserializeOwned, Serialize};
+use stdx::thread::ThreadIntent;
use crate::{
global_state::{GlobalState, GlobalStateSnapshot},
@@ -87,7 +88,8 @@ impl<'a> RequestDispatcher<'a> {
self
}
- /// Dispatches the request onto thread pool
+ /// Dispatches a non-latency-sensitive request onto the thread pool
+ /// without retrying it if it panics.
pub(crate) fn on_no_retry<R>(
&mut self,
f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
@@ -102,7 +104,7 @@ impl<'a> RequestDispatcher<'a> {
None => return self,
};
- self.global_state.task_pool.handle.spawn({
+ self.global_state.task_pool.handle.spawn(ThreadIntent::Worker, {
let world = self.global_state.snapshot();
move || {
let result = panic::catch_unwind(move || {
@@ -123,7 +125,7 @@ impl<'a> RequestDispatcher<'a> {
self
}
- /// Dispatches the request onto thread pool
+ /// Dispatches a non-latency-sensitive request onto the thread pool.
pub(crate) fn on<R>(
&mut self,
f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
@@ -133,26 +135,35 @@ impl<'a> RequestDispatcher<'a> {
R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
R::Result: Serialize,
{
- let (req, params, panic_context) = match self.parse::<R>() {
- Some(it) => it,
- None => return self,
- };
+ self.on_with_thread_intent::<true, R>(ThreadIntent::Worker, f)
+ }
- self.global_state.task_pool.handle.spawn({
- let world = self.global_state.snapshot();
- move || {
- let result = panic::catch_unwind(move || {
- let _pctx = stdx::panic_context::enter(panic_context);
- f(world, params)
- });
- match thread_result_to_response::<R>(req.id.clone(), result) {
- Ok(response) => Task::Response(response),
- Err(_) => Task::Retry(req),
- }
- }
- });
+ /// Dispatches a latency-sensitive request onto the thread pool.
+ pub(crate) fn on_latency_sensitive<R>(
+ &mut self,
+ f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
+ ) -> &mut Self
+ where
+ R: lsp_types::request::Request + 'static,
+ R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
+ R::Result: Serialize,
+ {
+ self.on_with_thread_intent::<true, R>(ThreadIntent::LatencySensitive, f)
+ }
- self
+ /// Formatting requests should never block on waiting a for task thread to open up, editors will wait
+ /// on the response and a late formatting update might mess with the document and user.
+ /// We can't run this on the main thread though as we invoke rustfmt which may take arbitrary time to complete!
+ pub(crate) fn on_fmt_thread<R>(
+ &mut self,
+ f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
+ ) -> &mut Self
+ where
+ R: lsp_types::request::Request + 'static,
+ R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
+ R::Result: Serialize,
+ {
+ self.on_with_thread_intent::<false, R>(ThreadIntent::LatencySensitive, f)
}
pub(crate) fn finish(&mut self) {
@@ -167,6 +178,41 @@ impl<'a> RequestDispatcher<'a> {
}
}
+ fn on_with_thread_intent<const MAIN_POOL: bool, R>(
+ &mut self,
+ intent: ThreadIntent,
+ f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
+ ) -> &mut Self
+ where
+ R: lsp_types::request::Request + 'static,
+ R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
+ R::Result: Serialize,
+ {
+ let (req, params, panic_context) = match self.parse::<R>() {
+ Some(it) => it,
+ None => return self,
+ };
+
+ let world = self.global_state.snapshot();
+ if MAIN_POOL {
+ &mut self.global_state.task_pool.handle
+ } else {
+ &mut self.global_state.fmt_pool.handle
+ }
+ .spawn(intent, move || {
+ let result = panic::catch_unwind(move || {
+ let _pctx = stdx::panic_context::enter(panic_context);
+ f(world, params)
+ });
+ match thread_result_to_response::<R>(req.id.clone(), result) {
+ Ok(response) => Task::Response(response),
+ Err(_) => Task::Retry(req),
+ }
+ });
+
+ self
+ }
+
fn parse<R>(&mut self) -> Option<(lsp_server::Request, R::Params, String)>
where
R: lsp_types::request::Request,
@@ -261,7 +307,7 @@ pub(crate) struct NotificationDispatcher<'a> {
}
impl<'a> NotificationDispatcher<'a> {
- pub(crate) fn on<N>(
+ pub(crate) fn on_sync_mut<N>(
&mut self,
f: fn(&mut GlobalState, N::Params) -> Result<()>,
) -> Result<&mut Self>
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
index 50af38cd6..cd74a5500 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
@@ -31,7 +31,10 @@ pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> R
PositionEncoding::Utf8 => LineCol { line: position.line, col: position.character },
PositionEncoding::Wide(enc) => {
let line_col = WideLineCol { line: position.line, col: position.character };
- line_index.index.to_utf8(enc, line_col)
+ line_index
+ .index
+ .to_utf8(enc, line_col)
+ .ok_or_else(|| format_err!("Invalid wide col offset"))?
}
};
let text_size =
@@ -98,13 +101,18 @@ pub(crate) fn assist_kind(kind: lsp_types::CodeActionKind) -> Option<AssistKind>
pub(crate) fn annotation(
snap: &GlobalStateSnapshot,
code_lens: lsp_types::CodeLens,
-) -> Result<Annotation> {
+) -> Result<Option<Annotation>> {
let data =
code_lens.data.ok_or_else(|| invalid_params_error("code lens without data".to_string()))?;
let resolve = from_json::<lsp_ext::CodeLensResolveData>("CodeLensResolveData", &data)?;
- match resolve {
- lsp_ext::CodeLensResolveData::Impls(params) => {
+ match resolve.kind {
+ lsp_ext::CodeLensResolveDataKind::Impls(params) => {
+ if snap.url_file_version(&params.text_document_position_params.text_document.uri)
+ != Some(resolve.version)
+ {
+ return Ok(None);
+ }
let pos @ FilePosition { file_id, .. } =
file_position(snap, params.text_document_position_params)?;
let line_index = snap.file_line_index(file_id)?;
@@ -114,7 +122,10 @@ pub(crate) fn annotation(
kind: AnnotationKind::HasImpls { pos, data: None },
})
}
- lsp_ext::CodeLensResolveData::References(params) => {
+ lsp_ext::CodeLensResolveDataKind::References(params) => {
+ if snap.url_file_version(&params.text_document.uri) != Some(resolve.version) {
+ return Ok(None);
+ }
let pos @ FilePosition { file_id, .. } = file_position(snap, params)?;
let line_index = snap.file_line_index(file_id)?;
@@ -124,4 +135,5 @@ pub(crate) fn annotation(
})
}
}
+ .map(Some)
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index aca6c9235..d5b0e3a57 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -3,22 +3,23 @@
//!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
-use std::{sync::Arc, time::Instant};
+use std::time::Instant;
use crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle;
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
-use ide_db::base_db::{CrateId, FileLoader, SourceDatabase};
+use ide_db::base_db::{CrateId, FileLoader, ProcMacroPaths, SourceDatabase};
use lsp_types::{SemanticTokens, Url};
+use nohash_hasher::IntMap;
use parking_lot::{Mutex, RwLock};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
-use rustc_hash::FxHashMap;
-use stdx::hash::NoHashHashMap;
+use rustc_hash::{FxHashMap, FxHashSet};
+use triomphe::Arc;
use vfs::AnchoredPathBuf;
use crate::{
- config::Config,
+ config::{Config, ConfigError},
diagnostics::{CheckFixes, DiagnosticCollection},
from_proto,
line_index::{LineEndings, LineIndex},
@@ -51,24 +52,35 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
pub(crate) struct GlobalState {
sender: Sender<lsp_server::Message>,
req_queue: ReqQueue,
+
pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
- pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
+ pub(crate) fmt_pool: Handle<TaskPool<Task>, Receiver<Task>>,
+
pub(crate) config: Arc<Config>,
+ pub(crate) config_errors: Option<ConfigError>,
pub(crate) analysis_host: AnalysisHost,
pub(crate) diagnostics: DiagnosticCollection,
pub(crate) mem_docs: MemDocs,
+ pub(crate) source_root_config: SourceRootConfig,
pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
+
+ // status
pub(crate) shutdown_requested: bool,
- pub(crate) proc_macro_changed: bool,
pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
- pub(crate) source_root_config: SourceRootConfig,
- pub(crate) proc_macro_clients: Vec<Result<ProcMacroServer, String>>,
+ // proc macros
+ pub(crate) proc_macro_changed: bool,
+ pub(crate) proc_macro_clients: Arc<[anyhow::Result<ProcMacroServer>]>,
+
+ // Flycheck
pub(crate) flycheck: Arc<[FlycheckHandle]>,
pub(crate) flycheck_sender: Sender<flycheck::Message>,
pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
+ pub(crate) last_flycheck_error: Option<String>,
- pub(crate) vfs: Arc<RwLock<(vfs::Vfs, NoHashHashMap<FileId, LineEndings>)>>,
+ // VFS
+ pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
+ pub(crate) vfs: Arc<RwLock<(vfs::Vfs, IntMap<FileId, LineEndings>)>>,
pub(crate) vfs_config_version: u32,
pub(crate) vfs_progress_config_version: u32,
pub(crate) vfs_progress_n_total: usize,
@@ -92,7 +104,7 @@ pub(crate) struct GlobalState {
/// first phase is much faster, and is much less likely to fail.
///
/// This creates a complication -- by the time the second phase completes,
- /// the results of the fist phase could be invalid. That is, while we run
+ /// the results of the first phase could be invalid. That is, while we run
/// `cargo check`, the user edits `Cargo.toml`, we notice this, and the new
/// `cargo metadata` completes before `cargo check`.
///
@@ -100,11 +112,15 @@ pub(crate) struct GlobalState {
/// the user just adds comments or whitespace to Cargo.toml, we do not want
/// to invalidate any salsa caches.
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
- pub(crate) fetch_workspaces_queue: OpQueue<Option<Vec<anyhow::Result<ProjectWorkspace>>>>,
- pub(crate) fetch_build_data_queue:
- OpQueue<(Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
+ pub(crate) crate_graph_file_dependencies: FxHashSet<vfs::VfsPath>,
- pub(crate) prime_caches_queue: OpQueue<()>,
+ // op queues
+ pub(crate) fetch_workspaces_queue:
+ OpQueue<bool, Option<(Vec<anyhow::Result<ProjectWorkspace>>, bool)>>,
+ pub(crate) fetch_build_data_queue:
+ OpQueue<(), (Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
+ pub(crate) fetch_proc_macros_queue: OpQueue<Vec<ProcMacroPaths>, bool>,
+ pub(crate) prime_caches_queue: OpQueue,
}
/// An immutable snapshot of the world's state at a point in time.
@@ -114,8 +130,9 @@ pub(crate) struct GlobalStateSnapshot {
pub(crate) check_fixes: CheckFixes,
mem_docs: MemDocs,
pub(crate) semantic_tokens_cache: Arc<Mutex<FxHashMap<Url, SemanticTokens>>>,
- vfs: Arc<RwLock<(vfs::Vfs, NoHashHashMap<FileId, LineEndings>)>>,
+ vfs: Arc<RwLock<(vfs::Vfs, IntMap<FileId, LineEndings>)>>,
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
+ // used to signal semantic highlighting to fall back to syntax based highlighting until proc-macros have been loaded
pub(crate) proc_macros_loaded: bool,
pub(crate) flycheck: Arc<[FlycheckHandle]>,
}
@@ -137,13 +154,22 @@ impl GlobalState {
let handle = TaskPool::new_with_threads(sender, config.main_loop_num_threads());
Handle { handle, receiver }
};
+ let fmt_pool = {
+ let (sender, receiver) = unbounded();
+ let handle = TaskPool::new_with_threads(sender, 1);
+ Handle { handle, receiver }
+ };
- let analysis_host = AnalysisHost::new(config.lru_capacity());
+ let mut analysis_host = AnalysisHost::new(config.lru_parse_query_capacity());
+ if let Some(capacities) = config.lru_query_capacities() {
+ analysis_host.update_lru_capacities(capacities);
+ }
let (flycheck_sender, flycheck_receiver) = unbounded();
let mut this = GlobalState {
sender,
req_queue: ReqQueue::default(),
task_pool,
+ fmt_pool,
loader,
config: Arc::new(config.clone()),
analysis_host,
@@ -151,26 +177,33 @@ impl GlobalState {
mem_docs: MemDocs::default(),
semantic_tokens_cache: Arc::new(Default::default()),
shutdown_requested: false,
- proc_macro_changed: false,
last_reported_status: None,
source_root_config: SourceRootConfig::default(),
- proc_macro_clients: vec![],
+ config_errors: Default::default(),
+
+ proc_macro_changed: false,
+ // FIXME: use `Arc::from_iter` when it becomes available
+ proc_macro_clients: Arc::from(Vec::new()),
- flycheck: Arc::new([]),
+ // FIXME: use `Arc::from_iter` when it becomes available
+ flycheck: Arc::from(Vec::new()),
flycheck_sender,
flycheck_receiver,
+ last_flycheck_error: None,
- vfs: Arc::new(RwLock::new((vfs::Vfs::default(), NoHashHashMap::default()))),
+ vfs: Arc::new(RwLock::new((vfs::Vfs::default(), IntMap::default()))),
vfs_config_version: 0,
vfs_progress_config_version: 0,
vfs_progress_n_total: 0,
vfs_progress_n_done: 0,
workspaces: Arc::new(Vec::new()),
+ crate_graph_file_dependencies: FxHashSet::default(),
fetch_workspaces_queue: OpQueue::default(),
- prime_caches_queue: OpQueue::default(),
-
fetch_build_data_queue: OpQueue::default(),
+ fetch_proc_macros_queue: OpQueue::default(),
+
+ prime_caches_queue: OpQueue::default(),
};
// Apply any required database inputs from the config.
this.update_configuration(config);
@@ -179,13 +212,12 @@ impl GlobalState {
pub(crate) fn process_changes(&mut self) -> bool {
let _p = profile::span("GlobalState::process_changes");
- let mut workspace_structure_change = None;
let mut file_changes = FxHashMap::default();
- let (change, changed_files) = {
+ let (change, changed_files, workspace_structure_change) = {
let mut change = Change::new();
let (vfs, line_endings_map) = &mut *self.vfs.write();
- let mut changed_files = vfs.take_changes();
+ let changed_files = vfs.take_changes();
if changed_files.is_empty() {
return false;
}
@@ -193,7 +225,7 @@ impl GlobalState {
// We need to fix up the changed events a bit. If we have a create or modify for a file
// id that is followed by a delete we actually skip observing the file text from the
// earlier event, to avoid problems later on.
- for changed_file in &changed_files {
+ for changed_file in changed_files {
use vfs::ChangeKind::*;
file_changes
@@ -229,25 +261,28 @@ impl GlobalState {
));
}
- changed_files.extend(
- file_changes
- .into_iter()
- .filter(|(_, (change_kind, just_created))| {
- !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true))
- })
- .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind }),
- );
+ let changed_files: Vec<_> = file_changes
+ .into_iter()
+ .filter(|(_, (change_kind, just_created))| {
+ !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true))
+ })
+ .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind })
+ .collect();
+ let mut workspace_structure_change = None;
// A file was added or deleted
let mut has_structure_changes = false;
for file in &changed_files {
- if let Some(path) = vfs.file_path(file.file_id).as_path() {
+ let vfs_path = &vfs.file_path(file.file_id);
+ if let Some(path) = vfs_path.as_path() {
let path = path.to_path_buf();
if reload::should_refresh_for_change(&path, file.change_kind) {
- workspace_structure_change = Some(path);
+ workspace_structure_change = Some((path.clone(), false));
}
if file.is_created_or_deleted() {
has_structure_changes = true;
+ workspace_structure_change =
+ Some((path, self.crate_graph_file_dependencies.contains(vfs_path)));
}
}
@@ -261,7 +296,7 @@ impl GlobalState {
String::from_utf8(bytes).ok().and_then(|text| {
let (text, line_endings) = LineEndings::normalize(text);
line_endings_map.insert(file.file_id, line_endings);
- Some(Arc::new(text))
+ Some(Arc::from(text))
})
} else {
None
@@ -272,7 +307,7 @@ impl GlobalState {
let roots = self.source_root_config.partition(vfs);
change.set_roots(roots);
}
- (change, changed_files)
+ (change, changed_files, workspace_structure_change)
};
self.analysis_host.apply_change(change);
@@ -280,11 +315,13 @@ impl GlobalState {
{
let raw_database = self.analysis_host.raw_database();
// FIXME: ideally we should only trigger a workspace fetch for non-library changes
- // but somethings going wrong with the source root business when we add a new local
+ // but something's going wrong with the source root business when we add a new local
// crate see https://github.com/rust-lang/rust-analyzer/issues/13029
- if let Some(path) = workspace_structure_change {
- self.fetch_workspaces_queue
- .request_op(format!("workspace vfs file change: {}", path.display()));
+ if let Some((path, force_crate_graph_reload)) = workspace_structure_change {
+ self.fetch_workspaces_queue.request_op(
+ format!("workspace vfs file change: {}", path.display()),
+ force_crate_graph_reload,
+ );
}
self.proc_macro_changed =
changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {
@@ -307,7 +344,8 @@ impl GlobalState {
check_fixes: Arc::clone(&self.diagnostics.check_fixes),
mem_docs: self.mem_docs.clone(),
semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
- proc_macros_loaded: !self.fetch_build_data_queue.last_op_result().0.is_empty(),
+ proc_macros_loaded: !self.config.expand_proc_macros()
+ || *self.fetch_proc_macros_queue.last_op_result(),
flycheck: self.flycheck.clone(),
}
}
@@ -331,7 +369,7 @@ impl GlobalState {
}
pub(crate) fn send_notification<N: lsp_types::notification::Notification>(
- &mut self,
+ &self,
params: N::Params,
) {
let not = lsp_server::Notification::new(N::METHOD.to_string(), params);
@@ -372,7 +410,7 @@ impl GlobalState {
self.req_queue.incoming.is_completed(&request.id)
}
- fn send(&mut self, message: lsp_server::Message) {
+ fn send(&self, message: lsp_server::Message) {
self.sender.send(message).unwrap()
}
}
@@ -431,6 +469,10 @@ impl GlobalStateSnapshot {
ProjectWorkspace::DetachedFiles { .. } => None,
})
}
+
+ pub(crate) fn vfs_memory_usage(&self) -> usize {
+ self.vfs.read().0.memory_usage()
+ }
}
pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
new file mode 100644
index 000000000..ae1dc2315
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -0,0 +1,334 @@
+//! This module is responsible for implementing handlers for Language Server
+//! Protocol. This module specifically handles notifications.
+
+use std::ops::Deref;
+
+use itertools::Itertools;
+use lsp_types::{
+ CancelParams, DidChangeConfigurationParams, DidChangeTextDocumentParams,
+ DidChangeWatchedFilesParams, DidChangeWorkspaceFoldersParams, DidCloseTextDocumentParams,
+ DidOpenTextDocumentParams, DidSaveTextDocumentParams, WorkDoneProgressCancelParams,
+};
+use triomphe::Arc;
+use vfs::{AbsPathBuf, ChangeKind, VfsPath};
+
+use crate::{
+ config::Config, from_proto, global_state::GlobalState, lsp_ext::RunFlycheckParams,
+ lsp_utils::apply_document_changes, mem_docs::DocumentData, reload, Result,
+};
+
+pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> Result<()> {
+ let id: lsp_server::RequestId = match params.id {
+ lsp_types::NumberOrString::Number(id) => id.into(),
+ lsp_types::NumberOrString::String(id) => id.into(),
+ };
+ state.cancel(id);
+ Ok(())
+}
+
+pub(crate) fn handle_work_done_progress_cancel(
+ state: &mut GlobalState,
+ params: WorkDoneProgressCancelParams,
+) -> Result<()> {
+ if let lsp_types::NumberOrString::String(s) = &params.token {
+ if let Some(id) = s.strip_prefix("rust-analyzer/flycheck/") {
+ if let Ok(id) = u32::from_str_radix(id, 10) {
+ if let Some(flycheck) = state.flycheck.get(id as usize) {
+ flycheck.cancel();
+ }
+ }
+ }
+ }
+
+ // Just ignore this. It is OK to continue sending progress
+ // notifications for this token, as the client can't know when
+ // we accepted notification.
+ Ok(())
+}
+
+pub(crate) fn handle_did_open_text_document(
+ state: &mut GlobalState,
+ params: DidOpenTextDocumentParams,
+) -> Result<()> {
+ let _p = profile::span("handle_did_open_text_document");
+
+ if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
+ let already_exists = state
+ .mem_docs
+ .insert(path.clone(), DocumentData::new(params.text_document.version))
+ .is_err();
+ if already_exists {
+ tracing::error!("duplicate DidOpenTextDocument: {}", path);
+ }
+ state.vfs.write().0.set_file_contents(path, Some(params.text_document.text.into_bytes()));
+ }
+ Ok(())
+}
+
+pub(crate) fn handle_did_change_text_document(
+ state: &mut GlobalState,
+ params: DidChangeTextDocumentParams,
+) -> Result<()> {
+ let _p = profile::span("handle_did_change_text_document");
+
+ if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
+ match state.mem_docs.get_mut(&path) {
+ Some(doc) => {
+ // The version passed in DidChangeTextDocument is the version after all edits are applied
+ // so we should apply it before the vfs is notified.
+ doc.version = params.text_document.version;
+ }
+ None => {
+ tracing::error!("unexpected DidChangeTextDocument: {}", path);
+ return Ok(());
+ }
+ };
+
+ let vfs = &mut state.vfs.write().0;
+ let file_id = vfs.file_id(&path).unwrap();
+ let text = apply_document_changes(
+ state.config.position_encoding(),
+ || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(),
+ params.content_changes,
+ );
+
+ vfs.set_file_contents(path, Some(text.into_bytes()));
+ }
+ Ok(())
+}
+
+pub(crate) fn handle_did_close_text_document(
+ state: &mut GlobalState,
+ params: DidCloseTextDocumentParams,
+) -> Result<()> {
+ let _p = profile::span("handle_did_close_text_document");
+
+ if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
+ if state.mem_docs.remove(&path).is_err() {
+ tracing::error!("orphan DidCloseTextDocument: {}", path);
+ }
+
+ state.semantic_tokens_cache.lock().remove(&params.text_document.uri);
+
+ if let Some(path) = path.as_path() {
+ state.loader.handle.invalidate(path.to_path_buf());
+ }
+ }
+ Ok(())
+}
+
+pub(crate) fn handle_did_save_text_document(
+ state: &mut GlobalState,
+ params: DidSaveTextDocumentParams,
+) -> Result<()> {
+ if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
+ // Re-fetch workspaces if a workspace related file has changed
+ if let Some(abs_path) = vfs_path.as_path() {
+ if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) {
+ state
+ .fetch_workspaces_queue
+ .request_op(format!("DidSaveTextDocument {}", abs_path.display()), false);
+ }
+ }
+
+ if !state.config.check_on_save() || run_flycheck(state, vfs_path) {
+ return Ok(());
+ }
+ } else if state.config.check_on_save() {
+ // No specific flycheck was triggered, so let's trigger all of them.
+ for flycheck in state.flycheck.iter() {
+ flycheck.restart();
+ }
+ }
+ Ok(())
+}
+
+pub(crate) fn handle_did_change_configuration(
+ state: &mut GlobalState,
+ _params: DidChangeConfigurationParams,
+) -> Result<()> {
+ // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
+ // this notification's parameters should be ignored and the actual config queried separately.
+ state.send_request::<lsp_types::request::WorkspaceConfiguration>(
+ lsp_types::ConfigurationParams {
+ items: vec![lsp_types::ConfigurationItem {
+ scope_uri: None,
+ section: Some("rust-analyzer".to_string()),
+ }],
+ },
+ |this, resp| {
+ tracing::debug!("config update response: '{:?}", resp);
+ let lsp_server::Response { error, result, .. } = resp;
+
+ match (error, result) {
+ (Some(err), _) => {
+ tracing::error!("failed to fetch the server settings: {:?}", err)
+ }
+ (None, Some(mut configs)) => {
+ if let Some(json) = configs.get_mut(0) {
+ // Note that json can be null according to the spec if the client can't
+ // provide a configuration. This is handled in Config::update below.
+ let mut config = Config::clone(&*this.config);
+ this.config_errors = config.update(json.take()).err();
+ this.update_configuration(config);
+ }
+ }
+ (None, None) => {
+ tracing::error!("received empty server settings response from the client")
+ }
+ }
+ },
+ );
+
+ Ok(())
+}
+
+pub(crate) fn handle_did_change_workspace_folders(
+ state: &mut GlobalState,
+ params: DidChangeWorkspaceFoldersParams,
+) -> Result<()> {
+ let config = Arc::make_mut(&mut state.config);
+
+ for workspace in params.event.removed {
+ let Ok(path) = workspace.uri.to_file_path() else { continue };
+ let Ok(path) = AbsPathBuf::try_from(path) else { continue };
+ config.remove_workspace(&path);
+ }
+
+ let added = params
+ .event
+ .added
+ .into_iter()
+ .filter_map(|it| it.uri.to_file_path().ok())
+ .filter_map(|it| AbsPathBuf::try_from(it).ok());
+ config.add_workspaces(added);
+
+ if !config.has_linked_projects() && config.detached_files().is_empty() {
+ config.rediscover_workspaces();
+ state.fetch_workspaces_queue.request_op("client workspaces changed".to_string(), false)
+ }
+
+ Ok(())
+}
+
+pub(crate) fn handle_did_change_watched_files(
+ state: &mut GlobalState,
+ params: DidChangeWatchedFilesParams,
+) -> Result<()> {
+ for change in params.changes {
+ if let Ok(path) = from_proto::abs_path(&change.uri) {
+ state.loader.handle.invalidate(path);
+ }
+ }
+ Ok(())
+}
+
+fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
+ let _p = profile::span("run_flycheck");
+
+ let file_id = state.vfs.read().0.file_id(&vfs_path);
+ if let Some(file_id) = file_id {
+ let world = state.snapshot();
+ let mut updated = false;
+ let task = move || -> std::result::Result<(), ide::Cancelled> {
+ // Trigger flychecks for all workspaces that depend on the saved file
+ // Crates containing or depending on the saved file
+ let crate_ids: Vec<_> = world
+ .analysis
+ .crates_for(file_id)?
+ .into_iter()
+ .flat_map(|id| world.analysis.transitive_rev_deps(id))
+ .flatten()
+ .sorted()
+ .unique()
+ .collect();
+
+ let crate_root_paths: Vec<_> = crate_ids
+ .iter()
+ .filter_map(|&crate_id| {
+ world
+ .analysis
+ .crate_root(crate_id)
+ .map(|file_id| {
+ world.file_id_to_file_path(file_id).as_path().map(ToOwned::to_owned)
+ })
+ .transpose()
+ })
+ .collect::<ide::Cancellable<_>>()?;
+ let crate_root_paths: Vec<_> = crate_root_paths.iter().map(Deref::deref).collect();
+
+ // Find all workspaces that have at least one target containing the saved file
+ let workspace_ids = world.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
+ project_model::ProjectWorkspace::Cargo { cargo, .. } => {
+ cargo.packages().any(|pkg| {
+ cargo[pkg]
+ .targets
+ .iter()
+ .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path()))
+ })
+ }
+ project_model::ProjectWorkspace::Json { project, .. } => {
+ project.crates().any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c))
+ }
+ project_model::ProjectWorkspace::DetachedFiles { .. } => false,
+ });
+
+ // Find and trigger corresponding flychecks
+ for flycheck in world.flycheck.iter() {
+ for (id, _) in workspace_ids.clone() {
+ if id == flycheck.id() {
+ updated = true;
+ flycheck.restart();
+ continue;
+ }
+ }
+ }
+ // No specific flycheck was triggered, so let's trigger all of them.
+ if !updated {
+ for flycheck in world.flycheck.iter() {
+ flycheck.restart();
+ }
+ }
+ Ok(())
+ };
+ state.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, move |_| {
+ if let Err(e) = std::panic::catch_unwind(task) {
+ tracing::error!("flycheck task panicked: {e:?}")
+ }
+ });
+ true
+ } else {
+ false
+ }
+}
+
+pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> {
+ let _p = profile::span("handle_stop_flycheck");
+ state.flycheck.iter().for_each(|flycheck| flycheck.cancel());
+ Ok(())
+}
+
+pub(crate) fn handle_clear_flycheck(state: &mut GlobalState, _: ()) -> Result<()> {
+ let _p = profile::span("handle_clear_flycheck");
+ state.diagnostics.clear_check_all();
+ Ok(())
+}
+
+pub(crate) fn handle_run_flycheck(
+ state: &mut GlobalState,
+ params: RunFlycheckParams,
+) -> Result<()> {
+ let _p = profile::span("handle_run_flycheck");
+ if let Some(text_document) = params.text_document {
+ if let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) {
+ if run_flycheck(state, vfs_path) {
+ return Ok(());
+ }
+ }
+ }
+ // No specific flycheck was triggered, so let's trigger all of them.
+ for flycheck in state.flycheck.iter() {
+ flycheck.restart();
+ }
+ Ok(())
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index 2fca2ab85..a6a72552d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -1,26 +1,25 @@
//! This module is responsible for implementing handlers for Language Server
-//! Protocol. The majority of requests are fulfilled by calling into the
-//! `ide` crate.
+//! Protocol. This module specifically handles requests.
use std::{
+ fs,
io::Write as _,
process::{self, Stdio},
};
use anyhow::Context;
use ide::{
- AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FileId, FilePosition,
- FileRange, HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable,
- RunnableKind, SingleResolve, SourceChange, TextEdit,
+ AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange,
+ HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind,
+ SingleResolve, SourceChange, TextEdit,
};
use ide_db::SymbolKind;
use lsp_server::ErrorCode;
use lsp_types::{
CallHierarchyIncomingCall, CallHierarchyIncomingCallsParams, CallHierarchyItem,
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
- CodeLens, CompletionItem, Diagnostic, DiagnosticTag, DocumentFormattingParams, FoldingRange,
- FoldingRangeParams, HoverContents, InlayHint, InlayHintParams, Location, LocationLink,
- NumberOrString, Position, PrepareRenameResponse, Range, RenameParams,
+ CodeLens, CompletionItem, FoldingRange, FoldingRangeParams, HoverContents, InlayHint,
+ InlayHintParams, Location, LocationLink, Position, PrepareRenameResponse, Range, RenameParams,
SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
@@ -29,7 +28,8 @@ use project_model::{ManifestPath, ProjectWorkspace, TargetKind};
use serde_json::json;
use stdx::{format_to, never};
use syntax::{algo, ast, AstNode, TextRange, TextSize};
-use vfs::{AbsPath, AbsPathBuf};
+use triomphe::Arc;
+use vfs::{AbsPath, AbsPathBuf, VfsPath};
use crate::{
cargo_target_spec::CargoTargetSpec,
@@ -38,23 +38,29 @@ use crate::{
from_proto,
global_state::{GlobalState, GlobalStateSnapshot},
line_index::LineEndings,
- lsp_ext::{self, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams},
+ lsp_ext::{
+ self, CrateInfoResult, ExternalDocsPair, ExternalDocsResponse, FetchDependencyListParams,
+ FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams,
+ },
lsp_utils::{all_edits_are_disjoint, invalid_params_error},
to_proto, LspError, Result,
};
pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> {
- state.proc_macro_clients.clear();
+ // FIXME: use `Arc::from_iter` when it becomes available
+ state.proc_macro_clients = Arc::from(Vec::new());
state.proc_macro_changed = false;
- state.fetch_workspaces_queue.request_op("reload workspace request".to_string());
- state.fetch_build_data_queue.request_op("reload workspace request".to_string());
+ state.fetch_workspaces_queue.request_op("reload workspace request".to_string(), false);
Ok(())
}
-pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> {
- let _p = profile::span("handle_stop_flycheck");
- state.flycheck.iter().for_each(|flycheck| flycheck.cancel());
+pub(crate) fn handle_proc_macros_rebuild(state: &mut GlobalState, _: ()) -> Result<()> {
+ // FIXME: use `Arc::from_iter` when it becomes available
+ state.proc_macro_clients = Arc::from(Vec::new());
+ state.proc_macro_changed = false;
+
+ state.fetch_build_data_queue.request_op("rebuild proc macros request".to_string(), ());
Ok(())
}
@@ -95,6 +101,7 @@ pub(crate) fn handle_analyzer_status(
.collect::<Vec<&AbsPath>>()
);
}
+ format_to!(buf, "\nVfs memory usage: {}\n", profile::Bytes::new(snap.vfs_memory_usage() as _));
buf.push_str("\nAnalysis:\n");
buf.push_str(
&snap
@@ -107,13 +114,14 @@ pub(crate) fn handle_analyzer_status(
pub(crate) fn handle_memory_usage(state: &mut GlobalState, _: ()) -> Result<String> {
let _p = profile::span("handle_memory_usage");
- let mut mem = state.analysis_host.per_query_memory_usage();
- mem.push(("Remaining".into(), profile::memory_usage().allocated));
+ let mem = state.analysis_host.per_query_memory_usage();
let mut out = String::new();
- for (name, bytes) in mem {
- format_to!(out, "{:>8} {}\n", bytes, name);
+ for (name, bytes, entries) in mem {
+ format_to!(out, "{:>8} {:>6} {}\n", bytes, entries, name);
}
+ format_to!(out, "{:>8} Remaining\n", profile::memory_usage().allocated);
+
Ok(out)
}
@@ -154,6 +162,16 @@ pub(crate) fn handle_view_mir(
Ok(res)
}
+pub(crate) fn handle_interpret_function(
+ snap: GlobalStateSnapshot,
+ params: lsp_types::TextDocumentPositionParams,
+) -> Result<String> {
+ let _p = profile::span("handle_interpret_function");
+ let position = from_proto::file_position(&snap, params)?;
+ let res = snap.analysis.interpret_function(position)?;
+ Ok(res)
+}
+
pub(crate) fn handle_view_file_text(
snap: GlobalStateSnapshot,
params: lsp_types::TextDocumentIdentifier,
@@ -502,7 +520,10 @@ pub(crate) fn handle_workspace_symbol(
#[allow(deprecated)]
let info = SymbolInformation {
- name: nav.name.to_string(),
+ name: match &nav.alias {
+ Some(alias) => format!("{} (alias for {})", alias, nav.name),
+ None => format!("{}", nav.name),
+ },
kind: nav
.kind
.map(to_proto::symbol_kind)
@@ -747,20 +768,25 @@ pub(crate) fn handle_runnables(
let config = snap.config.runnables();
match cargo_spec {
Some(spec) => {
+ let all_targets = !snap.analysis.is_crate_no_std(spec.crate_id)?;
for cmd in ["check", "test"] {
+ let mut cargo_args =
+ vec![cmd.to_owned(), "--package".to_owned(), spec.package.clone()];
+ if all_targets {
+ cargo_args.push("--all-targets".to_owned());
+ }
res.push(lsp_ext::Runnable {
- label: format!("cargo {cmd} -p {} --all-targets", spec.package),
+ label: format!(
+ "cargo {cmd} -p {}{all_targets}",
+ spec.package,
+ all_targets = if all_targets { " --all-targets" } else { "" }
+ ),
location: None,
kind: lsp_ext::RunnableKind::Cargo,
args: lsp_ext::CargoRunnable {
workspace_root: Some(spec.workspace_root.clone().into()),
override_cargo: config.override_cargo.clone(),
- cargo_args: vec![
- cmd.to_string(),
- "--package".to_string(),
- spec.package.clone(),
- "--all-targets".to_string(),
- ],
+ cargo_args,
cargo_extra_args: config.cargo_extra_args.clone(),
executable_args: Vec::new(),
expect_test: None,
@@ -769,14 +795,7 @@ pub(crate) fn handle_runnables(
}
}
None => {
- if !snap.config.linked_projects().is_empty()
- || !snap
- .config
- .discovered_projects
- .as_ref()
- .map(|projects| projects.is_empty())
- .unwrap_or(true)
- {
+ if !snap.config.linked_projects().is_empty() {
res.push(lsp_ext::Runnable {
label: "cargo check --workspace".to_string(),
location: None,
@@ -1057,7 +1076,7 @@ pub(crate) fn handle_references(
pub(crate) fn handle_formatting(
snap: GlobalStateSnapshot,
- params: DocumentFormattingParams,
+ params: lsp_types::DocumentFormattingParams,
) -> Result<Option<Vec<lsp_types::TextEdit>>> {
let _p = profile::span("handle_formatting");
@@ -1262,7 +1281,7 @@ pub(crate) fn handle_code_lens_resolve(
snap: GlobalStateSnapshot,
code_lens: CodeLens,
) -> Result<CodeLens> {
- let annotation = from_proto::annotation(&snap, code_lens.clone())?;
+ let Some(annotation) = from_proto::annotation(&snap, code_lens.clone())? else { return Ok(code_lens) };
let annotation = snap.analysis.resolve_annotation(annotation)?;
let mut acc = Vec::new();
@@ -1321,38 +1340,6 @@ pub(crate) fn handle_ssr(
to_proto::workspace_edit(&snap, source_change).map_err(Into::into)
}
-pub(crate) fn publish_diagnostics(
- snap: &GlobalStateSnapshot,
- file_id: FileId,
-) -> Result<Vec<Diagnostic>> {
- let _p = profile::span("publish_diagnostics");
- let line_index = snap.file_line_index(file_id)?;
-
- let diagnostics: Vec<Diagnostic> = snap
- .analysis
- .diagnostics(&snap.config.diagnostics(), AssistResolveStrategy::None, file_id)?
- .into_iter()
- .map(|d| Diagnostic {
- range: to_proto::range(&line_index, d.range),
- severity: Some(to_proto::diagnostic_severity(d.severity)),
- code: Some(NumberOrString::String(d.code.as_str().to_string())),
- code_description: Some(lsp_types::CodeDescription {
- href: lsp_types::Url::parse(&format!(
- "https://rust-analyzer.github.io/manual.html#{}",
- d.code.as_str()
- ))
- .unwrap(),
- }),
- source: Some("rust-analyzer".to_string()),
- message: d.message,
- related_information: None,
- tags: if d.unused { Some(vec![DiagnosticTag::UNNECESSARY]) } else { None },
- data: None,
- })
- .collect();
- Ok(diagnostics)
-}
-
pub(crate) fn handle_inlay_hints(
snap: GlobalStateSnapshot,
params: InlayHintParams,
@@ -1370,9 +1357,7 @@ pub(crate) fn handle_inlay_hints(
snap.analysis
.inlay_hints(&inlay_hints_config, file_id, Some(range))?
.into_iter()
- .map(|it| {
- to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it)
- })
+ .map(|it| to_proto::inlay_hint(&snap, &line_index, it))
.collect::<Cancellable<Vec<_>>>()?,
))
}
@@ -1493,7 +1478,13 @@ pub(crate) fn handle_semantic_tokens_full(
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
let highlights = snap.analysis.highlight(highlight_config, file_id)?;
- let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
+ let semantic_tokens = to_proto::semantic_tokens(
+ &text,
+ &line_index,
+ highlights,
+ snap.config.semantics_tokens_augments_syntax_tokens(),
+ snap.config.highlighting_non_standard_tokens(),
+ );
// Unconditionally cache the tokens
snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens.clone());
@@ -1517,7 +1508,13 @@ pub(crate) fn handle_semantic_tokens_full_delta(
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
let highlights = snap.analysis.highlight(highlight_config, file_id)?;
- let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
+ let semantic_tokens = to_proto::semantic_tokens(
+ &text,
+ &line_index,
+ highlights,
+ snap.config.semantics_tokens_augments_syntax_tokens(),
+ snap.config.highlighting_non_standard_tokens(),
+ );
let mut cache = snap.semantic_tokens_cache.lock();
let cached_tokens = cache.entry(params.text_document.uri).or_default();
@@ -1551,20 +1548,53 @@ pub(crate) fn handle_semantic_tokens_range(
snap.workspaces.is_empty() || !snap.proc_macros_loaded;
let highlights = snap.analysis.highlight_range(highlight_config, frange)?;
- let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
+ let semantic_tokens = to_proto::semantic_tokens(
+ &text,
+ &line_index,
+ highlights,
+ snap.config.semantics_tokens_augments_syntax_tokens(),
+ snap.config.highlighting_non_standard_tokens(),
+ );
Ok(Some(semantic_tokens.into()))
}
pub(crate) fn handle_open_docs(
snap: GlobalStateSnapshot,
params: lsp_types::TextDocumentPositionParams,
-) -> Result<Option<lsp_types::Url>> {
+) -> Result<ExternalDocsResponse> {
let _p = profile::span("handle_open_docs");
let position = from_proto::file_position(&snap, params)?;
- let remote = snap.analysis.external_docs(position)?;
+ let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match ws {
+ ProjectWorkspace::Cargo { cargo, sysroot, .. } => Some((cargo, sysroot.as_ref().ok())),
+ ProjectWorkspace::Json { .. } => None,
+ ProjectWorkspace::DetachedFiles { .. } => None,
+ });
+
+ let (cargo, sysroot) = match ws_and_sysroot {
+ Some((ws, sysroot)) => (Some(ws), sysroot),
+ _ => (None, None),
+ };
+
+ let sysroot = sysroot.map(|p| p.root().as_os_str());
+ let target_dir = cargo.map(|cargo| cargo.target_directory()).map(|p| p.as_os_str());
+
+ let Ok(remote_urls) = snap.analysis.external_docs(position, target_dir, sysroot) else {
+ return if snap.config.local_docs() {
+ Ok(ExternalDocsResponse::WithLocal(Default::default()))
+ } else {
+ Ok(ExternalDocsResponse::Simple(None))
+ }
+ };
- Ok(remote.and_then(|remote| Url::parse(&remote).ok()))
+ let web = remote_urls.web_url.and_then(|it| Url::parse(&it).ok());
+ let local = remote_urls.local_url.and_then(|it| Url::parse(&it).ok());
+
+ if snap.config.local_docs() {
+ Ok(ExternalDocsResponse::WithLocal(ExternalDocsPair { web, local }))
+ } else {
+ Ok(ExternalDocsResponse::Simple(web))
+ }
}
pub(crate) fn handle_open_cargo_toml(
@@ -1908,3 +1938,52 @@ fn run_rustfmt(
Ok(Some(to_proto::text_edit_vec(&line_index, diff(&file, &new_text))))
}
}
+
+pub(crate) fn fetch_dependency_list(
+ state: GlobalStateSnapshot,
+ _params: FetchDependencyListParams,
+) -> Result<FetchDependencyListResult> {
+ let crates = state.analysis.fetch_crates()?;
+ let crate_infos = crates
+ .into_iter()
+ .filter_map(|it| {
+ let root_file_path = state.file_id_to_file_path(it.root_file_id);
+ crate_path(root_file_path).and_then(to_url).map(|path| CrateInfoResult {
+ name: it.name,
+ version: it.version,
+ path,
+ })
+ })
+ .collect();
+ Ok(FetchDependencyListResult { crates: crate_infos })
+}
+
+/// Searches for the directory of a Rust crate given this crate's root file path.
+///
+/// # Arguments
+///
+/// * `root_file_path`: The path to the root file of the crate.
+///
+/// # Returns
+///
+/// An `Option` value representing the path to the directory of the crate with the given
+/// name, if such a crate is found. If no crate with the given name is found, this function
+/// returns `None`.
+fn crate_path(root_file_path: VfsPath) -> Option<VfsPath> {
+ let mut current_dir = root_file_path.parent();
+ while let Some(path) = current_dir {
+ let cargo_toml_path = path.join("../Cargo.toml")?;
+ if fs::metadata(cargo_toml_path.as_path()?).is_ok() {
+ let crate_path = cargo_toml_path.parent()?;
+ return Some(crate_path);
+ }
+ current_dir = path.parent();
+ }
+ None
+}
+
+fn to_url(path: VfsPath) -> Option<Url> {
+ let path = path.as_path()?;
+ let str_path = path.as_os_str().to_str()?;
+ Url::from_file_path(str_path).ok()
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index e8912b907..bd9f471a4 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -10,8 +10,6 @@
//! in release mode in VS Code. There's however "rust-analyzer: Copy Run Command Line"
//! which you can use to paste the command in terminal and add `--release` manually.
-use std::sync::Arc;
-
use ide::{CallableSnippets, Change, CompletionConfig, FilePosition, TextSize};
use ide_db::{
imports::insert_use::{ImportGranularity, InsertUseConfig},
@@ -19,6 +17,7 @@ use ide_db::{
};
use project_model::CargoConfig;
use test_utils::project_root;
+use triomphe::Arc;
use vfs::{AbsPathBuf, VfsPath};
use crate::cli::load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
@@ -65,7 +64,7 @@ fn integrated_highlighting_benchmark() {
let mut text = host.analysis().file_text(file_id).unwrap().to_string();
text.push_str("\npub fn _dummy() {}\n");
let mut change = Change::new();
- change.change_file(file_id, Some(Arc::new(text)));
+ change.change_file(file_id, Some(Arc::from(text)));
host.apply_change(change);
}
@@ -121,7 +120,7 @@ fn integrated_completion_benchmark() {
patch(&mut text, "db.struct_data(self.id)", "sel;\ndb.struct_data(self.id)")
+ "sel".len();
let mut change = Change::new();
- change.change_file(file_id, Some(Arc::new(text)));
+ change.change_file(file_id, Some(Arc::from(text)));
host.apply_change(change);
completion_offset
};
@@ -160,7 +159,7 @@ fn integrated_completion_benchmark() {
patch(&mut text, "sel;\ndb.struct_data(self.id)", "self.;\ndb.struct_data(self.id)")
+ "self.".len();
let mut change = Change::new();
- change.change_file(file_id, Some(Arc::new(text)));
+ change.change_file(file_id, Some(Arc::from(text)));
host.apply_change(change);
completion_offset
};
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
index 32dc3750f..65de4366e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
@@ -25,7 +25,6 @@ mod diff;
mod dispatch;
mod from_proto;
mod global_state;
-mod handlers;
mod line_index;
mod lsp_utils;
mod main_loop;
@@ -38,6 +37,11 @@ mod task_pool;
mod to_proto;
mod version;
+mod handlers {
+ pub(crate) mod notification;
+ pub(crate) mod request;
+}
+
pub mod config;
pub mod lsp_ext;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs
index 791cd931d..15450303f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/line_index.rs
@@ -5,9 +5,8 @@
//! This module does line ending conversion and detection (so that we can
//! convert back to `\r\n` on the way out).
-use std::sync::Arc;
-
use ide_db::line_index::WideEncoding;
+use triomphe::Arc;
#[derive(Clone, Copy)]
pub enum PositionEncoding {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
index c7b513db9..4d67c8b30 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
@@ -4,11 +4,11 @@ use std::{collections::HashMap, path::PathBuf};
use ide_db::line_index::WideEncoding;
use lsp_types::request::Request;
-use lsp_types::PositionEncodingKind;
use lsp_types::{
notification::Notification, CodeActionKind, DocumentOnTypeFormattingParams,
PartialResultParams, Position, Range, TextDocumentIdentifier, WorkDoneProgressParams,
};
+use lsp_types::{PositionEncodingKind, Url};
use serde::{Deserialize, Serialize};
use crate::line_index::PositionEncoding;
@@ -27,6 +27,31 @@ pub struct AnalyzerStatusParams {
pub text_document: Option<TextDocumentIdentifier>,
}
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct CrateInfoResult {
+ pub name: Option<String>,
+ pub version: Option<String>,
+ pub path: Url,
+}
+pub enum FetchDependencyList {}
+
+impl Request for FetchDependencyList {
+ type Params = FetchDependencyListParams;
+ type Result = FetchDependencyListResult;
+ const METHOD: &'static str = "rust-analyzer/fetchDependencyList";
+}
+
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct FetchDependencyListParams {}
+
+#[derive(Deserialize, Serialize, Debug)]
+#[serde(rename_all = "camelCase")]
+pub struct FetchDependencyListResult {
+ pub crates: Vec<CrateInfoResult>,
+}
+
pub enum MemoryUsage {}
impl Request for MemoryUsage {
@@ -51,6 +76,14 @@ impl Request for ReloadWorkspace {
const METHOD: &'static str = "rust-analyzer/reloadWorkspace";
}
+pub enum RebuildProcMacros {}
+
+impl Request for RebuildProcMacros {
+ type Params = ();
+ type Result = ();
+ const METHOD: &'static str = "rust-analyzer/rebuildProcMacros";
+}
+
pub enum SyntaxTree {}
impl Request for SyntaxTree {
@@ -82,6 +115,14 @@ impl Request for ViewMir {
const METHOD: &'static str = "rust-analyzer/viewMir";
}
+pub enum InterpretFunction {}
+
+impl Request for InterpretFunction {
+ type Params = lsp_types::TextDocumentPositionParams;
+ type Result = String;
+ const METHOD: &'static str = "rust-analyzer/interpretFunction";
+}
+
pub enum ViewFileText {}
impl Request for ViewFileText {
@@ -343,6 +384,7 @@ impl Request for CodeActionRequest {
}
pub enum CodeActionResolveRequest {}
+
impl Request for CodeActionResolveRequest {
type Params = CodeAction;
type Result = CodeAction;
@@ -418,7 +460,7 @@ pub enum HoverRequest {}
impl Request for HoverRequest {
type Params = HoverParams;
type Result = Option<Hover>;
- const METHOD: &'static str = "textDocument/hover";
+ const METHOD: &'static str = lsp_types::request::HoverRequest::METHOD;
}
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)]
@@ -466,10 +508,24 @@ pub enum ExternalDocs {}
impl Request for ExternalDocs {
type Params = lsp_types::TextDocumentPositionParams;
- type Result = Option<lsp_types::Url>;
+ type Result = ExternalDocsResponse;
const METHOD: &'static str = "experimental/externalDocs";
}
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+#[serde(untagged)]
+pub enum ExternalDocsResponse {
+ Simple(Option<lsp_types::Url>),
+ WithLocal(ExternalDocsPair),
+}
+
+#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct ExternalDocsPair {
+ pub web: Option<lsp_types::Url>,
+ pub local: Option<lsp_types::Url>,
+}
+
pub enum OpenCargoToml {}
impl Request for OpenCargoToml {
@@ -487,7 +543,14 @@ pub struct OpenCargoTomlParams {
/// Information about CodeLens, that is to be resolved.
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
-pub(crate) enum CodeLensResolveData {
+pub struct CodeLensResolveData {
+ pub version: i32,
+ pub kind: CodeLensResolveDataKind,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub enum CodeLensResolveDataKind {
Impls(lsp_types::request::GotoImplementationParams),
References(lsp_types::TextDocumentPositionParams),
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
index 12e5caf2c..74e79e8e6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
@@ -1,8 +1,9 @@
//! Utilities for LSP-related boilerplate code.
-use std::{mem, ops::Range, sync::Arc};
+use std::{mem, ops::Range};
use lsp_server::Notification;
use lsp_types::request::Request;
+use triomphe::Arc;
use crate::{
from_proto,
@@ -80,39 +81,14 @@ impl GlobalState {
match additional_info {
Some(additional_info) => {
tracing::error!("{}:\n{}", &message, &additional_info);
- match self.config.open_server_logs() && tracing::enabled!(tracing::Level::ERROR) {
- true => self.send_request::<lsp_types::request::ShowMessageRequest>(
- lsp_types::ShowMessageRequestParams {
- typ: lsp_types::MessageType::ERROR,
- message,
- actions: Some(vec![lsp_types::MessageActionItem {
- title: "Open server logs".to_owned(),
- properties: Default::default(),
- }]),
- },
- |this, resp| {
- let lsp_server::Response { error: None, result: Some(result), .. } = resp
- else { return };
- if let Ok(Some(_item)) = crate::from_json::<
- <lsp_types::request::ShowMessageRequest as lsp_types::request::Request>::Result,
- >(
- lsp_types::request::ShowMessageRequest::METHOD, &result
- ) {
- this.send_notification::<lsp_ext::OpenServerLogs>(());
- }
- },
- ),
- false => self.send_notification::<lsp_types::notification::ShowMessage>(
- lsp_types::ShowMessageParams {
- typ: lsp_types::MessageType::ERROR,
- message,
- },
- ),
- }
+ self.show_message(
+ lsp_types::MessageType::ERROR,
+ message,
+ tracing::enabled!(tracing::Level::ERROR),
+ );
}
None => {
tracing::error!("{}", &message);
-
self.send_notification::<lsp_types::notification::ShowMessage>(
lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, message },
);
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 67a54cde6..02dd94e5f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -2,8 +2,6 @@
//! requests/replies and notifications back to the client.
use std::{
fmt,
- ops::Deref,
- sync::Arc,
time::{Duration, Instant},
};
@@ -11,20 +9,20 @@ use always_assert::always;
use crossbeam_channel::{select, Receiver};
use flycheck::FlycheckHandle;
use ide_db::base_db::{SourceDatabaseExt, VfsPath};
-use itertools::Itertools;
use lsp_server::{Connection, Notification, Request};
use lsp_types::notification::Notification as _;
-use vfs::{AbsPathBuf, ChangeKind, FileId};
+use stdx::thread::ThreadIntent;
+use triomphe::Arc;
+use vfs::FileId;
use crate::{
config::Config,
dispatch::{NotificationDispatcher, RequestDispatcher},
from_proto,
global_state::{file_id_to_url, url_to_file_id, GlobalState},
- handlers, lsp_ext,
- lsp_utils::{apply_document_changes, notification_is, Progress},
- mem_docs::DocumentData,
- reload::{self, BuildDataProgress, ProjectWorkspaceProgress},
+ lsp_ext,
+ lsp_utils::{notification_is, Progress},
+ reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress},
Result,
};
@@ -36,7 +34,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
// temporary bumped. This optimization backfires in our case: each time the
// `main_loop` schedules a task to run on a threadpool, the worker threads
// gets a higher priority, and (on a machine with fewer cores) displaces the
- // main loop! We work-around this by marking the main loop as a
+ // main loop! We work around this by marking the main loop as a
// higher-priority thread.
//
// https://docs.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities
@@ -68,6 +66,7 @@ pub(crate) enum Task {
PrimeCaches(PrimeCachesProgress),
FetchWorkspace(ProjectWorkspaceProgress),
FetchBuildData(BuildDataProgress),
+ LoadProcMacros(ProcMacroProgress),
}
#[derive(Debug)]
@@ -79,7 +78,7 @@ pub(crate) enum PrimeCachesProgress {
impl fmt::Debug for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter<'_>| {
+ let debug_non_verbose = |not: &Notification, f: &mut fmt::Formatter<'_>| {
f.debug_struct("Notification").field("method", &not.method).finish()
};
@@ -88,7 +87,7 @@ impl fmt::Debug for Event {
if notification_is::<lsp_types::notification::DidOpenTextDocument>(not)
|| notification_is::<lsp_types::notification::DidChangeTextDocument>(not)
{
- return debug_verbose_not(not, f);
+ return debug_non_verbose(not, f);
}
}
Event::Task(Task::Response(resp)) => {
@@ -114,57 +113,65 @@ impl GlobalState {
self.update_status_or_notify();
if self.config.did_save_text_document_dynamic_registration() {
- let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
- include_text: Some(false),
- text_document_registration_options: lsp_types::TextDocumentRegistrationOptions {
- document_selector: Some(vec![
- lsp_types::DocumentFilter {
- language: None,
- scheme: None,
- pattern: Some("**/*.rs".into()),
- },
- lsp_types::DocumentFilter {
- language: None,
- scheme: None,
- pattern: Some("**/Cargo.toml".into()),
- },
- lsp_types::DocumentFilter {
- language: None,
- scheme: None,
- pattern: Some("**/Cargo.lock".into()),
- },
- ]),
- },
- };
-
- let registration = lsp_types::Registration {
- id: "textDocument/didSave".to_string(),
- method: "textDocument/didSave".to_string(),
- register_options: Some(serde_json::to_value(save_registration_options).unwrap()),
- };
- self.send_request::<lsp_types::request::RegisterCapability>(
- lsp_types::RegistrationParams { registrations: vec![registration] },
- |_, _| (),
- );
+ self.register_did_save_capability();
}
- self.fetch_workspaces_queue.request_op("startup".to_string());
- if let Some(cause) = self.fetch_workspaces_queue.should_start_op() {
- self.fetch_workspaces(cause);
+ self.fetch_workspaces_queue.request_op("startup".to_string(), false);
+ if let Some((cause, force_crate_graph_reload)) =
+ self.fetch_workspaces_queue.should_start_op()
+ {
+ self.fetch_workspaces(cause, force_crate_graph_reload);
}
while let Some(event) = self.next_event(&inbox) {
- if let Event::Lsp(lsp_server::Message::Notification(not)) = &event {
- if not.method == lsp_types::notification::Exit::METHOD {
- return Ok(());
- }
+ if matches!(
+ &event,
+ Event::Lsp(lsp_server::Message::Notification(Notification { method, .. }))
+ if method == lsp_types::notification::Exit::METHOD
+ ) {
+ return Ok(());
}
- self.handle_event(event)?
+ self.handle_event(event)?;
}
Err("client exited without proper shutdown sequence".into())
}
+ fn register_did_save_capability(&mut self) {
+ let save_registration_options = lsp_types::TextDocumentSaveRegistrationOptions {
+ include_text: Some(false),
+ text_document_registration_options: lsp_types::TextDocumentRegistrationOptions {
+ document_selector: Some(vec![
+ lsp_types::DocumentFilter {
+ language: None,
+ scheme: None,
+ pattern: Some("**/*.rs".into()),
+ },
+ lsp_types::DocumentFilter {
+ language: None,
+ scheme: None,
+ pattern: Some("**/Cargo.toml".into()),
+ },
+ lsp_types::DocumentFilter {
+ language: None,
+ scheme: None,
+ pattern: Some("**/Cargo.lock".into()),
+ },
+ ]),
+ },
+ };
+
+ let registration = lsp_types::Registration {
+ id: "textDocument/didSave".to_string(),
+ method: "textDocument/didSave".to_string(),
+ register_options: Some(serde_json::to_value(save_registration_options).unwrap()),
+ };
+ self.send_request::<lsp_types::request::RegisterCapability>(
+ lsp_types::RegistrationParams { registrations: vec![registration] },
+ |_, _| (),
+ );
+ }
+
fn next_event(&self, inbox: &Receiver<lsp_server::Message>) -> Option<Event> {
select! {
recv(inbox) -> msg =>
@@ -173,6 +180,9 @@ impl GlobalState {
recv(self.task_pool.receiver) -> task =>
Some(Event::Task(task.unwrap())),
+ recv(self.fmt_pool.receiver) -> task =>
+ Some(Event::Task(task.unwrap())),
+
recv(self.loader.receiver) -> task =>
Some(Event::Vfs(task.unwrap())),
@@ -186,19 +196,20 @@ impl GlobalState {
// NOTE: don't count blocking select! call as a loop-turn time
let _p = profile::span("GlobalState::handle_event");
- tracing::debug!("{:?} handle_event({:?})", loop_start, event);
- let task_queue_len = self.task_pool.handle.len();
- if task_queue_len > 0 {
- tracing::info!("task queue len: {}", task_queue_len);
+ let event_dbg_msg = format!("{event:?}");
+ tracing::debug!("{:?} handle_event({})", loop_start, event_dbg_msg);
+ if tracing::enabled!(tracing::Level::INFO) {
+ let task_queue_len = self.task_pool.handle.len();
+ if task_queue_len > 0 {
+ tracing::info!("task queue len: {}", task_queue_len);
+ }
}
let was_quiescent = self.is_quiescent();
match event {
Event::Lsp(msg) => match msg {
lsp_server::Message::Request(req) => self.on_new_request(loop_start, req),
- lsp_server::Message::Notification(not) => {
- self.on_notification(not)?;
- }
+ lsp_server::Message::Notification(not) => self.on_notification(not)?,
lsp_server::Message::Response(resp) => self.complete_request(resp),
},
Event::Task(task) => {
@@ -247,7 +258,7 @@ impl GlobalState {
self.prime_caches_queue.op_completed(());
if cancelled {
self.prime_caches_queue
- .request_op("restart after cancellation".to_string());
+ .request_op("restart after cancellation".to_string(), ());
}
}
};
@@ -272,6 +283,7 @@ impl GlobalState {
}
}
}
+ let event_handling_duration = loop_start.elapsed();
let state_changed = self.process_changes();
let memdocs_added_or_removed = self.mem_docs.take_changes();
@@ -279,7 +291,8 @@ impl GlobalState {
if self.is_quiescent() {
let became_quiescent = !(was_quiescent
|| self.fetch_workspaces_queue.op_requested()
- || self.fetch_build_data_queue.op_requested());
+ || self.fetch_build_data_queue.op_requested()
+ || self.fetch_proc_macros_queue.op_requested());
if became_quiescent {
if self.config.check_on_save() {
@@ -287,11 +300,12 @@ impl GlobalState {
self.flycheck.iter().for_each(FlycheckHandle::restart);
}
if self.config.prefill_caches() {
- self.prime_caches_queue.request_op("became quiescent".to_string());
+ self.prime_caches_queue.request_op("became quiescent".to_string(), ());
}
}
- if !was_quiescent || state_changed {
+ let client_refresh = !was_quiescent || state_changed;
+ if client_refresh {
// Refresh semantic tokens if the client supports it.
if self.config.semantic_tokens_refresh() {
self.semantic_tokens_cache.lock().clear();
@@ -309,9 +323,9 @@ impl GlobalState {
}
}
- if (!was_quiescent || state_changed || memdocs_added_or_removed)
- && self.config.publish_diagnostics()
- {
+ let update_diagnostics = (!was_quiescent || state_changed || memdocs_added_or_removed)
+ && self.config.publish_diagnostics();
+ if update_diagnostics {
self.update_diagnostics()
}
}
@@ -357,48 +371,56 @@ impl GlobalState {
}
if self.config.cargo_autoreload() {
- if let Some(cause) = self.fetch_workspaces_queue.should_start_op() {
- self.fetch_workspaces(cause);
+ if let Some((cause, force_crate_graph_reload)) =
+ self.fetch_workspaces_queue.should_start_op()
+ {
+ self.fetch_workspaces(cause, force_crate_graph_reload);
}
}
if !self.fetch_workspaces_queue.op_in_progress() {
- if let Some(cause) = self.fetch_build_data_queue.should_start_op() {
+ if let Some((cause, ())) = self.fetch_build_data_queue.should_start_op() {
self.fetch_build_data(cause);
+ } else if let Some((cause, paths)) = self.fetch_proc_macros_queue.should_start_op() {
+ self.fetch_proc_macros(cause, paths);
}
}
- if let Some(cause) = self.prime_caches_queue.should_start_op() {
- tracing::debug!(%cause, "will prime caches");
- let num_worker_threads = self.config.prime_caches_num_threads();
-
- self.task_pool.handle.spawn_with_sender({
- let analysis = self.snapshot().analysis;
- move |sender| {
- sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap();
- let res = analysis.parallel_prime_caches(num_worker_threads, |progress| {
- let report = PrimeCachesProgress::Report(progress);
- sender.send(Task::PrimeCaches(report)).unwrap();
- });
- sender
- .send(Task::PrimeCaches(PrimeCachesProgress::End {
- cancelled: res.is_err(),
- }))
- .unwrap();
- }
- });
+ if let Some((cause, ())) = self.prime_caches_queue.should_start_op() {
+ self.prime_caches(cause);
}
self.update_status_or_notify();
let loop_duration = loop_start.elapsed();
if loop_duration > Duration::from_millis(100) && was_quiescent {
- tracing::warn!("overly long loop turn: {:?}", loop_duration);
- self.poke_rust_analyzer_developer(format!("overly long loop turn: {loop_duration:?}"));
+ tracing::warn!("overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}");
+ self.poke_rust_analyzer_developer(format!(
+ "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}"
+ ));
}
Ok(())
}
+ fn prime_caches(&mut self, cause: String) {
+ tracing::debug!(%cause, "will prime caches");
+ let num_worker_threads = self.config.prime_caches_num_threads();
+
+ self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
+ let analysis = self.snapshot().analysis;
+ move |sender| {
+ sender.send(Task::PrimeCaches(PrimeCachesProgress::Begin)).unwrap();
+ let res = analysis.parallel_prime_caches(num_worker_threads, |progress| {
+ let report = PrimeCachesProgress::Report(progress);
+ sender.send(Task::PrimeCaches(report)).unwrap();
+ });
+ sender
+ .send(Task::PrimeCaches(PrimeCachesProgress::End { cancelled: res.is_err() }))
+ .unwrap();
+ }
+ });
+ }
+
fn update_status_or_notify(&mut self) {
let status = self.current_status();
if self.last_reported_status.as_ref() != Some(&status) {
@@ -406,7 +428,11 @@ impl GlobalState {
if self.config.server_status_notification() {
self.send_notification::<lsp_ext::ServerStatusNotification>(status);
- } else if let (health, Some(message)) = (status.health, &status.message) {
+ } else if let (
+ health @ (lsp_ext::Health::Warning | lsp_ext::Health::Error),
+ Some(message),
+ ) = (status.health, &status.message)
+ {
let open_log_button = tracing::enabled!(tracing::Level::ERROR)
&& (self.fetch_build_data_error().is_err()
|| self.fetch_workspace_error().is_err());
@@ -451,8 +477,9 @@ impl GlobalState {
let (state, msg) = match progress {
ProjectWorkspaceProgress::Begin => (Progress::Begin, None),
ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
- ProjectWorkspaceProgress::End(workspaces) => {
- self.fetch_workspaces_queue.op_completed(Some(workspaces));
+ ProjectWorkspaceProgress::End(workspaces, force_reload_crate_graph) => {
+ self.fetch_workspaces_queue
+ .op_completed(Some((workspaces, force_reload_crate_graph)));
if let Err(e) = self.fetch_workspace_error() {
tracing::error!("FetchWorkspaceError:\n{e}");
}
@@ -462,7 +489,8 @@ impl GlobalState {
let workspaces_updated = !Arc::ptr_eq(&old, &self.workspaces);
if self.config.run_build_scripts() && workspaces_updated {
- self.fetch_build_data_queue.request_op(format!("workspace updated"));
+ self.fetch_build_data_queue
+ .request_op(format!("workspace updated"), ());
}
(Progress::End, None)
@@ -488,6 +516,22 @@ impl GlobalState {
};
if let Some(state) = state {
+ self.report_progress("Building", state, msg, None, None);
+ }
+ }
+ Task::LoadProcMacros(progress) => {
+ let (state, msg) = match progress {
+ ProcMacroProgress::Begin => (Some(Progress::Begin), None),
+ ProcMacroProgress::Report(msg) => (Some(Progress::Report), Some(msg)),
+ ProcMacroProgress::End(proc_macro_load_result) => {
+ self.fetch_proc_macros_queue.op_completed(true);
+ self.set_proc_macros(proc_macro_load_result);
+
+ (Some(Progress::End), None)
+ }
+ };
+
+ if let Some(state) = state {
self.report_progress("Loading", state, msg, None, None);
}
}
@@ -512,7 +556,6 @@ impl GlobalState {
self.vfs_progress_n_total = n_total;
self.vfs_progress_n_done = n_done;
- // if n_total != 0 {
let state = if n_done == 0 {
Progress::Begin
} else if n_done < n_total {
@@ -528,7 +571,6 @@ impl GlobalState {
Some(Progress::fraction(n_done, n_total)),
None,
);
- // }
}
}
}
@@ -568,21 +610,18 @@ impl GlobalState {
(Progress::Begin, None)
}
flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)),
- flycheck::Progress::DidCancel => (Progress::End, None),
+ flycheck::Progress::DidCancel => {
+ self.last_flycheck_error = None;
+ (Progress::End, None)
+ }
flycheck::Progress::DidFailToRestart(err) => {
- self.show_and_log_error(
- "cargo check failed to start".to_string(),
- Some(err),
- );
+ self.last_flycheck_error =
+ Some(format!("cargo check failed to start: {err}"));
return;
}
flycheck::Progress::DidFinish(result) => {
- if let Err(err) = result {
- self.show_and_log_error(
- "cargo check failed".to_string(),
- Some(err.to_string()),
- );
- }
+ self.last_flycheck_error =
+ result.err().map(|err| format!("cargo check failed to start: {err}"));
(Progress::End, None)
}
};
@@ -631,18 +670,52 @@ impl GlobalState {
_ => (),
}
+ use crate::handlers::request as handlers;
+
dispatcher
+ // Request handlers that must run on the main thread
+ // because they mutate GlobalState:
.on_sync_mut::<lsp_ext::ReloadWorkspace>(handlers::handle_workspace_reload)
+ .on_sync_mut::<lsp_ext::RebuildProcMacros>(handlers::handle_proc_macros_rebuild)
.on_sync_mut::<lsp_ext::MemoryUsage>(handlers::handle_memory_usage)
.on_sync_mut::<lsp_ext::ShuffleCrateGraph>(handlers::handle_shuffle_crate_graph)
+ // Request handlers which are related to the user typing
+ // are run on the main thread to reduce latency:
.on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines)
.on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter)
.on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)
.on_sync::<lsp_ext::MatchingBrace>(handlers::handle_matching_brace)
+ .on_sync::<lsp_ext::OnTypeFormatting>(handlers::handle_on_type_formatting)
+ // Formatting should be done immediately as the editor might wait on it, but we can't
+ // put it on the main thread as we do not want the main thread to block on rustfmt.
+ // So we have an extra thread just for formatting requests to make sure it gets handled
+ // as fast as possible.
+ .on_fmt_thread::<lsp_types::request::Formatting>(handlers::handle_formatting)
+ .on_fmt_thread::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting)
+ // We can’t run latency-sensitive request handlers which do semantic
+ // analysis on the main thread because that would block other
+ // requests. Instead, we run these request handlers on higher priority
+ // threads in the threadpool.
+ .on_latency_sensitive::<lsp_types::request::Completion>(handlers::handle_completion)
+ .on_latency_sensitive::<lsp_types::request::ResolveCompletionItem>(
+ handlers::handle_completion_resolve,
+ )
+ .on_latency_sensitive::<lsp_types::request::SemanticTokensFullRequest>(
+ handlers::handle_semantic_tokens_full,
+ )
+ .on_latency_sensitive::<lsp_types::request::SemanticTokensFullDeltaRequest>(
+ handlers::handle_semantic_tokens_full_delta,
+ )
+ .on_latency_sensitive::<lsp_types::request::SemanticTokensRangeRequest>(
+ handlers::handle_semantic_tokens_range,
+ )
+ // All other request handlers
+ .on::<lsp_ext::FetchDependencyList>(handlers::fetch_dependency_list)
.on::<lsp_ext::AnalyzerStatus>(handlers::handle_analyzer_status)
.on::<lsp_ext::SyntaxTree>(handlers::handle_syntax_tree)
.on::<lsp_ext::ViewHir>(handlers::handle_view_hir)
.on::<lsp_ext::ViewMir>(handlers::handle_view_mir)
+ .on::<lsp_ext::InterpretFunction>(handlers::handle_interpret_function)
.on::<lsp_ext::ViewFileText>(handlers::handle_view_file_text)
.on::<lsp_ext::ViewCrateGraph>(handlers::handle_view_crate_graph)
.on::<lsp_ext::ViewItemTree>(handlers::handle_view_item_tree)
@@ -657,7 +730,6 @@ impl GlobalState {
.on::<lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml)
.on::<lsp_ext::MoveItem>(handlers::handle_move_item)
.on::<lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol)
- .on::<lsp_ext::OnTypeFormatting>(handlers::handle_on_type_formatting)
.on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)
.on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)
.on::<lsp_types::request::GotoDeclaration>(handlers::handle_goto_declaration)
@@ -665,8 +737,6 @@ impl GlobalState {
.on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
.on_no_retry::<lsp_types::request::InlayHintRequest>(handlers::handle_inlay_hints)
.on::<lsp_types::request::InlayHintResolveRequest>(handlers::handle_inlay_hints_resolve)
- .on::<lsp_types::request::Completion>(handlers::handle_completion)
- .on::<lsp_types::request::ResolveCompletionItem>(handlers::handle_completion_resolve)
.on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)
.on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)
.on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)
@@ -674,8 +744,6 @@ impl GlobalState {
.on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)
.on::<lsp_types::request::Rename>(handlers::handle_rename)
.on::<lsp_types::request::References>(handlers::handle_references)
- .on::<lsp_types::request::Formatting>(handlers::handle_formatting)
- .on::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting)
.on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight)
.on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)
.on::<lsp_types::request::CallHierarchyIncomingCalls>(
@@ -684,15 +752,6 @@ impl GlobalState {
.on::<lsp_types::request::CallHierarchyOutgoingCalls>(
handlers::handle_call_hierarchy_outgoing,
)
- .on::<lsp_types::request::SemanticTokensFullRequest>(
- handlers::handle_semantic_tokens_full,
- )
- .on::<lsp_types::request::SemanticTokensFullDeltaRequest>(
- handlers::handle_semantic_tokens_full_delta,
- )
- .on::<lsp_types::request::SemanticTokensRangeRequest>(
- handlers::handle_semantic_tokens_range,
- )
.on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files)
.on::<lsp_ext::Ssr>(handlers::handle_ssr)
.finish();
@@ -700,282 +759,32 @@ impl GlobalState {
/// Handles an incoming notification.
fn on_notification(&mut self, not: Notification) -> Result<()> {
- // FIXME: Move these implementations out into a module similar to on_request
- fn run_flycheck(this: &mut GlobalState, vfs_path: VfsPath) -> bool {
- let file_id = this.vfs.read().0.file_id(&vfs_path);
- if let Some(file_id) = file_id {
- let world = this.snapshot();
- let mut updated = false;
- let task = move || -> std::result::Result<(), ide::Cancelled> {
- // Trigger flychecks for all workspaces that depend on the saved file
- // Crates containing or depending on the saved file
- let crate_ids: Vec<_> = world
- .analysis
- .crates_for(file_id)?
- .into_iter()
- .flat_map(|id| world.analysis.transitive_rev_deps(id))
- .flatten()
- .sorted()
- .unique()
- .collect();
-
- let crate_root_paths: Vec<_> = crate_ids
- .iter()
- .filter_map(|&crate_id| {
- world
- .analysis
- .crate_root(crate_id)
- .map(|file_id| {
- world
- .file_id_to_file_path(file_id)
- .as_path()
- .map(ToOwned::to_owned)
- })
- .transpose()
- })
- .collect::<ide::Cancellable<_>>()?;
- let crate_root_paths: Vec<_> =
- crate_root_paths.iter().map(Deref::deref).collect();
-
- // Find all workspaces that have at least one target containing the saved file
- let workspace_ids =
- world.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
- project_model::ProjectWorkspace::Cargo { cargo, .. } => {
- cargo.packages().any(|pkg| {
- cargo[pkg].targets.iter().any(|&it| {
- crate_root_paths.contains(&cargo[it].root.as_path())
- })
- })
- }
- project_model::ProjectWorkspace::Json { project, .. } => project
- .crates()
- .any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c)),
- project_model::ProjectWorkspace::DetachedFiles { .. } => false,
- });
-
- // Find and trigger corresponding flychecks
- for flycheck in world.flycheck.iter() {
- for (id, _) in workspace_ids.clone() {
- if id == flycheck.id() {
- updated = true;
- flycheck.restart();
- continue;
- }
- }
- }
- // No specific flycheck was triggered, so let's trigger all of them.
- if !updated {
- for flycheck in world.flycheck.iter() {
- flycheck.restart();
- }
- }
- Ok(())
- };
- this.task_pool.handle.spawn_with_sender(move |_| {
- if let Err(e) = std::panic::catch_unwind(task) {
- tracing::error!("flycheck task panicked: {e:?}")
- }
- });
- true
- } else {
- false
- }
- }
+ use crate::handlers::notification as handlers;
+ use lsp_types::notification as notifs;
NotificationDispatcher { not: Some(not), global_state: self }
- .on::<lsp_types::notification::Cancel>(|this, params| {
- let id: lsp_server::RequestId = match params.id {
- lsp_types::NumberOrString::Number(id) => id.into(),
- lsp_types::NumberOrString::String(id) => id.into(),
- };
- this.cancel(id);
- Ok(())
- })?
- .on::<lsp_types::notification::WorkDoneProgressCancel>(|this, params| {
- if let lsp_types::NumberOrString::String(s) = &params.token {
- if let Some(id) = s.strip_prefix("rust-analyzer/flycheck/") {
- if let Ok(id) = u32::from_str_radix(id, 10) {
- if let Some(flycheck) = this.flycheck.get(id as usize) {
- flycheck.cancel();
- }
- }
- }
- }
- // Just ignore this. It is OK to continue sending progress
- // notifications for this token, as the client can't know when
- // we accepted notification.
- Ok(())
- })?
- .on::<lsp_types::notification::DidOpenTextDocument>(|this, params| {
- if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
- let already_exists = this
- .mem_docs
- .insert(path.clone(), DocumentData::new(params.text_document.version))
- .is_err();
- if already_exists {
- tracing::error!("duplicate DidOpenTextDocument: {}", path);
- }
- this.vfs
- .write()
- .0
- .set_file_contents(path, Some(params.text_document.text.into_bytes()));
- }
- Ok(())
- })?
- .on::<lsp_ext::CancelFlycheck>(handlers::handle_cancel_flycheck)?
- .on::<lsp_types::notification::DidChangeTextDocument>(|this, params| {
- if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
- match this.mem_docs.get_mut(&path) {
- Some(doc) => {
- // The version passed in DidChangeTextDocument is the version after all edits are applied
- // so we should apply it before the vfs is notified.
- doc.version = params.text_document.version;
- }
- None => {
- tracing::error!("unexpected DidChangeTextDocument: {}", path);
- return Ok(());
- }
- };
-
- let vfs = &mut this.vfs.write().0;
- let file_id = vfs.file_id(&path).unwrap();
- let text = apply_document_changes(
- this.config.position_encoding(),
- || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(),
- params.content_changes,
- );
-
- vfs.set_file_contents(path, Some(text.into_bytes()));
- }
- Ok(())
- })?
- .on::<lsp_types::notification::DidCloseTextDocument>(|this, params| {
- if let Ok(path) = from_proto::vfs_path(&params.text_document.uri) {
- if this.mem_docs.remove(&path).is_err() {
- tracing::error!("orphan DidCloseTextDocument: {}", path);
- }
-
- this.semantic_tokens_cache.lock().remove(&params.text_document.uri);
-
- if let Some(path) = path.as_path() {
- this.loader.handle.invalidate(path.to_path_buf());
- }
- }
- Ok(())
- })?
- .on::<lsp_ext::ClearFlycheck>(|this, ()| {
- this.diagnostics.clear_check_all();
- Ok(())
- })?
- .on::<lsp_ext::RunFlycheck>(|this, params| {
- if let Some(text_document) = params.text_document {
- if let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) {
- if run_flycheck(this, vfs_path) {
- return Ok(());
- }
- }
- }
- // No specific flycheck was triggered, so let's trigger all of them.
- for flycheck in this.flycheck.iter() {
- flycheck.restart();
- }
- Ok(())
- })?
- .on::<lsp_types::notification::DidSaveTextDocument>(|this, params| {
- if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
- // Re-fetch workspaces if a workspace related file has changed
- if let Some(abs_path) = vfs_path.as_path() {
- if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) {
- this.fetch_workspaces_queue
- .request_op(format!("DidSaveTextDocument {}", abs_path.display()));
- }
- }
-
- if !this.config.check_on_save() || run_flycheck(this, vfs_path) {
- return Ok(());
- }
- } else if this.config.check_on_save() {
- // No specific flycheck was triggered, so let's trigger all of them.
- for flycheck in this.flycheck.iter() {
- flycheck.restart();
- }
- }
- Ok(())
- })?
- .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
- // As stated in https://github.com/microsoft/language-server-protocol/issues/676,
- // this notification's parameters should be ignored and the actual config queried separately.
- this.send_request::<lsp_types::request::WorkspaceConfiguration>(
- lsp_types::ConfigurationParams {
- items: vec![lsp_types::ConfigurationItem {
- scope_uri: None,
- section: Some("rust-analyzer".to_string()),
- }],
- },
- |this, resp| {
- tracing::debug!("config update response: '{:?}", resp);
- let lsp_server::Response { error, result, .. } = resp;
-
- match (error, result) {
- (Some(err), _) => {
- tracing::error!("failed to fetch the server settings: {:?}", err)
- }
- (None, Some(mut configs)) => {
- if let Some(json) = configs.get_mut(0) {
- // Note that json can be null according to the spec if the client can't
- // provide a configuration. This is handled in Config::update below.
- let mut config = Config::clone(&*this.config);
- if let Err(error) = config.update(json.take()) {
- this.show_message(
- lsp_types::MessageType::WARNING,
- error.to_string(),
- false,
- );
- }
- this.update_configuration(config);
- }
- }
- (None, None) => tracing::error!(
- "received empty server settings response from the client"
- ),
- }
- },
- );
-
- Ok(())
- })?
- .on::<lsp_types::notification::DidChangeWorkspaceFolders>(|this, params| {
- let config = Arc::make_mut(&mut this.config);
-
- for workspace in params.event.removed {
- let Ok(path) = workspace.uri.to_file_path() else { continue };
- let Ok(path) = AbsPathBuf::try_from(path) else { continue };
- let Some(position) = config.workspace_roots.iter().position(|it| it == &path) else { continue };
- config.workspace_roots.remove(position);
- }
-
- let added = params
- .event
- .added
- .into_iter()
- .filter_map(|it| it.uri.to_file_path().ok())
- .filter_map(|it| AbsPathBuf::try_from(it).ok());
- config.workspace_roots.extend(added);
- if !config.has_linked_projects() && config.detached_files().is_empty() {
- config.rediscover_workspaces();
- this.fetch_workspaces_queue.request_op("client workspaces changed".to_string())
- }
-
- Ok(())
- })?
- .on::<lsp_types::notification::DidChangeWatchedFiles>(|this, params| {
- for change in params.changes {
- if let Ok(path) = from_proto::abs_path(&change.uri) {
- this.loader.handle.invalidate(path);
- }
- }
- Ok(())
- })?
+ .on_sync_mut::<notifs::Cancel>(handlers::handle_cancel)?
+ .on_sync_mut::<notifs::WorkDoneProgressCancel>(
+ handlers::handle_work_done_progress_cancel,
+ )?
+ .on_sync_mut::<notifs::DidOpenTextDocument>(handlers::handle_did_open_text_document)?
+ .on_sync_mut::<notifs::DidChangeTextDocument>(
+ handlers::handle_did_change_text_document,
+ )?
+ .on_sync_mut::<notifs::DidCloseTextDocument>(handlers::handle_did_close_text_document)?
+ .on_sync_mut::<notifs::DidSaveTextDocument>(handlers::handle_did_save_text_document)?
+ .on_sync_mut::<notifs::DidChangeConfiguration>(
+ handlers::handle_did_change_configuration,
+ )?
+ .on_sync_mut::<notifs::DidChangeWorkspaceFolders>(
+ handlers::handle_did_change_workspace_folders,
+ )?
+ .on_sync_mut::<notifs::DidChangeWatchedFiles>(
+ handlers::handle_did_change_watched_files,
+ )?
+ .on_sync_mut::<lsp_ext::CancelFlycheck>(handlers::handle_cancel_flycheck)?
+ .on_sync_mut::<lsp_ext::ClearFlycheck>(handlers::handle_clear_flycheck)?
+ .on_sync_mut::<lsp_ext::RunFlycheck>(handlers::handle_run_flycheck)?
.finish();
Ok(())
}
@@ -1000,16 +809,60 @@ impl GlobalState {
tracing::trace!("updating notifications for {:?}", subscriptions);
let snapshot = self.snapshot();
- self.task_pool.handle.spawn(move || {
+
+ // Diagnostics are triggered by the user typing
+ // so we run them on a latency sensitive thread.
+ self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, move || {
+ let _p = profile::span("publish_diagnostics");
+ let _ctx = stdx::panic_context::enter("publish_diagnostics".to_owned());
let diagnostics = subscriptions
.into_iter()
.filter_map(|file_id| {
- handlers::publish_diagnostics(&snapshot, file_id)
- .ok()
- .map(|diags| (file_id, diags))
+ let line_index = snapshot.file_line_index(file_id).ok()?;
+ Some((
+ file_id,
+ line_index,
+ snapshot
+ .analysis
+ .diagnostics(
+ &snapshot.config.diagnostics(),
+ ide::AssistResolveStrategy::None,
+ file_id,
+ )
+ .ok()?,
+ ))
})
- .collect::<Vec<_>>();
- Task::Diagnostics(diagnostics)
- })
+ .map(|(file_id, line_index, it)| {
+ (
+ file_id,
+ it.into_iter()
+ .map(move |d| lsp_types::Diagnostic {
+ range: crate::to_proto::range(&line_index, d.range),
+ severity: Some(crate::to_proto::diagnostic_severity(d.severity)),
+ code: Some(lsp_types::NumberOrString::String(
+ d.code.as_str().to_string(),
+ )),
+ code_description: Some(lsp_types::CodeDescription {
+ href: lsp_types::Url::parse(&format!(
+ "https://rust-analyzer.github.io/manual.html#{}",
+ d.code.as_str()
+ ))
+ .unwrap(),
+ }),
+ source: Some("rust-analyzer".to_string()),
+ message: d.message,
+ related_information: None,
+ tags: if d.unused {
+ Some(vec![lsp_types::DiagnosticTag::UNNECESSARY])
+ } else {
+ None
+ },
+ data: None,
+ })
+ .collect::<Vec<_>>(),
+ )
+ });
+ Task::Diagnostics(diagnostics.collect())
+ });
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs
index 912ed1e76..58426c66a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs
@@ -28,7 +28,7 @@ pub(crate) fn format_docs(src: &str) -> String {
if in_code_block {
let trimmed = line.trim_start();
- if trimmed.starts_with("##") {
+ if is_rust && trimmed.starts_with("##") {
line = &trimmed[1..];
}
}
@@ -154,4 +154,12 @@ let s = "foo
assert_eq!(format_docs(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```");
}
+
+ #[test]
+ fn test_format_docs_handles_double_hashes_non_rust() {
+ let comment = r#"```markdown
+## A second-level heading
+```"#;
+ assert_eq!(format_docs(comment), "```markdown\n## A second-level heading\n```");
+ }
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
index 97aca0161..932730fc2 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
@@ -3,23 +3,23 @@
pub(crate) type Cause = String;
-pub(crate) struct OpQueue<Output> {
- op_requested: Option<Cause>,
+pub(crate) struct OpQueue<Args = (), Output = ()> {
+ op_requested: Option<(Cause, Args)>,
op_in_progress: bool,
last_op_result: Output,
}
-impl<Output: Default> Default for OpQueue<Output> {
+impl<Args, Output: Default> Default for OpQueue<Args, Output> {
fn default() -> Self {
Self { op_requested: None, op_in_progress: false, last_op_result: Default::default() }
}
}
-impl<Output> OpQueue<Output> {
- pub(crate) fn request_op(&mut self, reason: Cause) {
- self.op_requested = Some(reason);
+impl<Args, Output> OpQueue<Args, Output> {
+ pub(crate) fn request_op(&mut self, reason: Cause, args: Args) {
+ self.op_requested = Some((reason, args));
}
- pub(crate) fn should_start_op(&mut self) -> Option<Cause> {
+ pub(crate) fn should_start_op(&mut self) -> Option<(Cause, Args)> {
if self.op_in_progress {
return None;
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 1a6e1af2e..310c6b076 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -12,22 +12,25 @@
//! correct. Instead, we try to provide a best-effort service. Even if the
//! project is currently loading and we don't have a full project model, we
//! still want to respond to various requests.
-use std::{collections::hash_map::Entry, mem, sync::Arc};
+use std::{collections::hash_map::Entry, iter, mem, sync};
use flycheck::{FlycheckConfig, FlycheckHandle};
use hir::db::DefDatabase;
use ide::Change;
use ide_db::{
base_db::{
- CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind,
- ProcMacroLoadResult, SourceRoot, VfsPath,
+ salsa::Durability, CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
+ ProcMacroKind, ProcMacroLoadResult, ProcMacroPaths, ProcMacros, SourceRoot, VfsPath,
},
FxHashMap,
};
use itertools::Itertools;
use proc_macro_api::{MacroDylib, ProcMacroServer};
use project_model::{PackageRoot, ProjectWorkspace, WorkspaceBuildScripts};
+use rustc_hash::FxHashSet;
+use stdx::{format_to, thread::ThreadIntent};
use syntax::SmolStr;
+use triomphe::Arc;
use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};
use crate::{
@@ -44,7 +47,7 @@ use ::tt::token_id as tt;
pub(crate) enum ProjectWorkspaceProgress {
Begin,
Report(String),
- End(Vec<anyhow::Result<ProjectWorkspace>>),
+ End(Vec<anyhow::Result<ProjectWorkspace>>, bool),
}
#[derive(Debug)]
@@ -54,11 +57,19 @@ pub(crate) enum BuildDataProgress {
End((Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)),
}
+#[derive(Debug)]
+pub(crate) enum ProcMacroProgress {
+ Begin,
+ Report(String),
+ End(ProcMacros),
+}
+
impl GlobalState {
pub(crate) fn is_quiescent(&self) -> bool {
!(self.last_reported_status.is_none()
|| self.fetch_workspaces_queue.op_in_progress()
|| self.fetch_build_data_queue.op_in_progress()
+ || self.fetch_proc_macros_queue.op_in_progress()
|| self.vfs_progress_config_version < self.vfs_config_version
|| self.vfs_progress_n_done < self.vfs_progress_n_total)
}
@@ -66,21 +77,27 @@ impl GlobalState {
pub(crate) fn update_configuration(&mut self, config: Config) {
let _p = profile::span("GlobalState::update_configuration");
let old_config = mem::replace(&mut self.config, Arc::new(config));
- if self.config.lru_capacity() != old_config.lru_capacity() {
- self.analysis_host.update_lru_capacity(self.config.lru_capacity());
+ if self.config.lru_parse_query_capacity() != old_config.lru_parse_query_capacity() {
+ self.analysis_host.update_lru_capacity(self.config.lru_parse_query_capacity());
+ }
+ if self.config.lru_query_capacities() != old_config.lru_query_capacities() {
+ self.analysis_host.update_lru_capacities(
+ &self.config.lru_query_capacities().cloned().unwrap_or_default(),
+ );
}
if self.config.linked_projects() != old_config.linked_projects() {
- self.fetch_workspaces_queue.request_op("linked projects changed".to_string())
+ self.fetch_workspaces_queue.request_op("linked projects changed".to_string(), false)
} else if self.config.flycheck() != old_config.flycheck() {
self.reload_flycheck();
}
- if self.analysis_host.raw_database().enable_proc_attr_macros()
+ if self.analysis_host.raw_database().expand_proc_attr_macros()
!= self.config.expand_proc_attr_macros()
{
- self.analysis_host
- .raw_database_mut()
- .set_enable_proc_attr_macros(self.config.expand_proc_attr_macros());
+ self.analysis_host.raw_database_mut().set_expand_proc_attr_macros_with_durability(
+ self.config.expand_proc_attr_macros(),
+ Durability::HIGH,
+ );
}
}
@@ -94,12 +111,16 @@ impl GlobalState {
if self.proc_macro_changed {
status.health = lsp_ext::Health::Warning;
- message.push_str("Reload required due to source changes of a procedural macro.\n\n");
+ message.push_str("Proc-macros have changed and need to be rebuilt.\n\n");
}
if let Err(_) = self.fetch_build_data_error() {
status.health = lsp_ext::Health::Warning;
message.push_str("Failed to run build scripts of some packages.\n\n");
}
+ if self.proc_macro_clients.iter().any(|it| it.is_err()) {
+ status.health = lsp_ext::Health::Warning;
+ message.push_str("Failed to spawn one or more proc-macro servers.\n\n");
+ }
if !self.config.cargo_autoreload()
&& self.is_quiescent()
&& self.fetch_workspaces_queue.op_requested()
@@ -112,17 +133,37 @@ impl GlobalState {
&& self.config.notifications().cargo_toml_not_found
{
status.health = lsp_ext::Health::Warning;
- message.push_str("Failed to discover workspace.\n\n");
+ message.push_str("Failed to discover workspace.\n");
+ message.push_str("Consider adding the `Cargo.toml` of the workspace to the [`linkedProjects`](https://rust-analyzer.github.io/manual.html#rust-analyzer.linkedProjects) setting.\n\n");
+ }
+ if let Some(err) = &self.config_errors {
+ status.health = lsp_ext::Health::Warning;
+ format_to!(message, "{err}\n");
+ }
+ if let Some(err) = &self.last_flycheck_error {
+ status.health = lsp_ext::Health::Warning;
+ message.push_str(err);
+ message.push('\n');
}
for ws in self.workspaces.iter() {
let (ProjectWorkspace::Cargo { sysroot, .. }
| ProjectWorkspace::Json { sysroot, .. }
| ProjectWorkspace::DetachedFiles { sysroot, .. }) = ws;
- if let Err(Some(e)) = sysroot {
- status.health = lsp_ext::Health::Warning;
- message.push_str(e);
- message.push_str("\n\n");
+ match sysroot {
+ Err(None) => (),
+ Err(Some(e)) => {
+ status.health = lsp_ext::Health::Warning;
+ message.push_str(e);
+ message.push_str("\n\n");
+ }
+ Ok(s) => {
+ if let Some(e) = s.loading_warning() {
+ status.health = lsp_ext::Health::Warning;
+ message.push_str(&e);
+ message.push_str("\n\n");
+ }
+ }
}
if let ProjectWorkspace::Cargo { rustc: Err(Some(e)), .. } = ws {
status.health = lsp_ext::Health::Warning;
@@ -142,10 +183,10 @@ impl GlobalState {
status
}
- pub(crate) fn fetch_workspaces(&mut self, cause: Cause) {
+ pub(crate) fn fetch_workspaces(&mut self, cause: Cause, force_crate_graph_reload: bool) {
tracing::info!(%cause, "will fetch workspaces");
- self.task_pool.handle.spawn_with_sender({
+ self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
let linked_projects = self.config.linked_projects();
let detached_files = self.config.detached_files().to_vec();
let cargo_config = self.config.cargo();
@@ -177,11 +218,30 @@ impl GlobalState {
it.clone(),
cargo_config.target.as_deref(),
&cargo_config.extra_env,
+ None,
))
}
})
.collect::<Vec<_>>();
+ let mut i = 0;
+ while i < workspaces.len() {
+ if let Ok(w) = &workspaces[i] {
+ let dupes: Vec<_> = workspaces
+ .iter()
+ .enumerate()
+ .skip(i + 1)
+ .filter_map(|(i, it)| {
+ it.as_ref().ok().filter(|ws| ws.eq_ignore_build_data(w)).map(|_| i)
+ })
+ .collect();
+ dupes.into_iter().rev().for_each(|d| {
+ _ = workspaces.remove(d);
+ });
+ }
+ i += 1;
+ }
+
if !detached_files.is_empty() {
workspaces.push(project_model::ProjectWorkspace::load_detached_files(
detached_files,
@@ -191,7 +251,10 @@ impl GlobalState {
tracing::info!("did fetch workspaces {:?}", workspaces);
sender
- .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(workspaces)))
+ .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(
+ workspaces,
+ force_crate_graph_reload,
+ )))
.unwrap();
}
});
@@ -201,7 +264,7 @@ impl GlobalState {
tracing::info!(%cause, "will fetch build data");
let workspaces = Arc::clone(&self.workspaces);
let config = self.config.cargo();
- self.task_pool.handle.spawn_with_sender(move |sender| {
+ self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
let progress = {
@@ -216,19 +279,80 @@ impl GlobalState {
});
}
+ pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec<ProcMacroPaths>) {
+ tracing::info!(%cause, "will load proc macros");
+ let dummy_replacements = self.config.dummy_replacements().clone();
+ let proc_macro_clients = self.proc_macro_clients.clone();
+
+ self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
+ sender.send(Task::LoadProcMacros(ProcMacroProgress::Begin)).unwrap();
+
+ let dummy_replacements = &dummy_replacements;
+ let progress = {
+ let sender = sender.clone();
+ &move |msg| {
+ sender.send(Task::LoadProcMacros(ProcMacroProgress::Report(msg))).unwrap()
+ }
+ };
+
+ let mut res = FxHashMap::default();
+ let chain = proc_macro_clients
+ .iter()
+ .map(|res| res.as_ref().map_err(|e| e.to_string()))
+ .chain(iter::repeat_with(|| Err("Proc macros servers are not running".into())));
+ for (client, paths) in chain.zip(paths) {
+ res.extend(paths.into_iter().map(move |(crate_id, res)| {
+ (
+ crate_id,
+ res.map_or_else(
+ |_| Err("proc macro crate is missing dylib".to_owned()),
+ |(crate_name, path)| {
+ progress(path.display().to_string());
+ client.as_ref().map_err(Clone::clone).and_then(|client| {
+ load_proc_macro(
+ client,
+ &path,
+ crate_name
+ .as_deref()
+ .and_then(|crate_name| {
+ dummy_replacements.get(crate_name).map(|v| &**v)
+ })
+ .unwrap_or_default(),
+ )
+ })
+ },
+ ),
+ )
+ }));
+ }
+
+ sender.send(Task::LoadProcMacros(ProcMacroProgress::End(res))).unwrap();
+ });
+ }
+
+ pub(crate) fn set_proc_macros(&mut self, proc_macros: ProcMacros) {
+ let mut change = Change::new();
+ change.set_proc_macros(proc_macros);
+ self.analysis_host.apply_change(change);
+ }
+
pub(crate) fn switch_workspaces(&mut self, cause: Cause) {
let _p = profile::span("GlobalState::switch_workspaces");
tracing::info!(%cause, "will switch workspaces");
+ let Some((workspaces, force_reload_crate_graph)) = self.fetch_workspaces_queue.last_op_result() else { return; };
+
if let Err(_) = self.fetch_workspace_error() {
if !self.workspaces.is_empty() {
+ if *force_reload_crate_graph {
+ self.recreate_crate_graph(cause);
+ }
// It only makes sense to switch to a partially broken workspace
// if we don't have any workspace at all yet.
return;
}
}
- let Some(workspaces) = self.fetch_workspaces_queue.last_op_result() else { return; };
let workspaces =
workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::<Vec<_>>();
@@ -257,6 +381,9 @@ impl GlobalState {
self.workspaces = Arc::new(workspaces);
} else {
tracing::info!("build scripts do not match the version of the active workspace");
+ if *force_reload_crate_graph {
+ self.recreate_crate_graph(cause);
+ }
// Current build scripts do not match the version of the active
// workspace, so there's nothing for us to update.
return;
@@ -303,43 +430,39 @@ impl GlobalState {
);
}
- let mut change = Change::new();
-
let files_config = self.config.files();
let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude);
- if self.proc_macro_clients.is_empty() {
- if let Some((path, path_manually_set)) = self.config.proc_macro_srv() {
+ if self.proc_macro_clients.is_empty() || !same_workspaces {
+ if self.config.expand_proc_macros() {
tracing::info!("Spawning proc-macro servers");
- self.proc_macro_clients = self
- .workspaces
- .iter()
- .map(|ws| {
- let (path, args): (_, &[_]) = if path_manually_set {
- tracing::debug!(
- "Pro-macro server path explicitly set: {}",
- path.display()
- );
- (path.clone(), &[])
- } else {
- match ws.find_sysroot_proc_macro_srv() {
- Some(server_path) => (server_path, &[]),
- None => (path.clone(), &["proc-macro"]),
- }
- };
-
- tracing::info!(?args, "Using proc-macro server at {}", path.display(),);
- ProcMacroServer::spawn(path.clone(), args).map_err(|err| {
- let error = format!(
- "Failed to run proc-macro server from path {}, error: {:?}",
- path.display(),
- err
- );
- tracing::error!(error);
- error
+
+ // FIXME: use `Arc::from_iter` when it becomes available
+ self.proc_macro_clients = Arc::from(
+ self.workspaces
+ .iter()
+ .map(|ws| {
+ let path = match self.config.proc_macro_srv() {
+ Some(path) => path,
+ None => ws.find_sysroot_proc_macro_srv()?,
+ };
+
+ tracing::info!("Using proc-macro server at {}", path.display(),);
+ ProcMacroServer::spawn(path.clone()).map_err(|err| {
+ tracing::error!(
+ "Failed to run proc-macro server from path {}, error: {:?}",
+ path.display(),
+ err
+ );
+ anyhow::anyhow!(
+ "Failed to run proc-macro server from path {}, error: {:?}",
+ path.display(),
+ err
+ )
+ })
})
- })
- .collect()
+ .collect::<Vec<_>>(),
+ )
};
}
@@ -353,63 +476,65 @@ impl GlobalState {
watch,
version: self.vfs_config_version,
});
+ self.source_root_config = project_folders.source_root_config;
- // Create crate graph from all the workspaces
- let crate_graph = {
- let dummy_replacements = self.config.dummy_replacements();
+ self.recreate_crate_graph(cause);
+ tracing::info!("did switch workspaces");
+ }
+
+ fn recreate_crate_graph(&mut self, cause: String) {
+ // Create crate graph from all the workspaces
+ let (crate_graph, proc_macro_paths, crate_graph_file_dependencies) = {
let vfs = &mut self.vfs.write().0;
let loader = &mut self.loader;
- let mem_docs = &self.mem_docs;
- let mut load = move |path: &AbsPath| {
+ // crate graph construction relies on these paths, record them so when one of them gets
+ // deleted or created we trigger a reconstruction of the crate graph
+ let mut crate_graph_file_dependencies = FxHashSet::default();
+
+ let mut load = |path: &AbsPath| {
let _p = profile::span("switch_workspaces::load");
let vfs_path = vfs::VfsPath::from(path.to_path_buf());
- if !mem_docs.contains(&vfs_path) {
- let contents = loader.handle.load_sync(path);
- vfs.set_file_contents(vfs_path.clone(), contents);
- }
- let res = vfs.file_id(&vfs_path);
- if res.is_none() {
- tracing::warn!("failed to load {}", path.display())
+ crate_graph_file_dependencies.insert(vfs_path.clone());
+ match vfs.file_id(&vfs_path) {
+ Some(file_id) => Some(file_id),
+ None => {
+ if !self.mem_docs.contains(&vfs_path) {
+ let contents = loader.handle.load_sync(path);
+ vfs.set_file_contents(vfs_path.clone(), contents);
+ }
+ vfs.file_id(&vfs_path)
+ }
}
- res
};
let mut crate_graph = CrateGraph::default();
- for (idx, ws) in self.workspaces.iter().enumerate() {
- let proc_macro_client = match self.proc_macro_clients.get(idx) {
- Some(res) => res.as_ref().map_err(|e| &**e),
- None => Err("Proc macros are disabled"),
- };
- let mut load_proc_macro = move |crate_name: &str, path: &AbsPath| {
- load_proc_macro(
- proc_macro_client,
- path,
- dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(),
- )
- };
- crate_graph.extend(ws.to_crate_graph(
- &mut load_proc_macro,
- &mut load,
- &self.config.cargo().extra_env,
- ));
+ let mut proc_macros = Vec::default();
+ for ws in &**self.workspaces {
+ let (other, mut crate_proc_macros) =
+ ws.to_crate_graph(&mut load, &self.config.extra_env());
+ crate_graph.extend(other, &mut crate_proc_macros);
+ proc_macros.push(crate_proc_macros);
}
- crate_graph
+ (crate_graph, proc_macros, crate_graph_file_dependencies)
};
- change.set_crate_graph(crate_graph);
-
- self.source_root_config = project_folders.source_root_config;
+ if self.config.expand_proc_macros() {
+ self.fetch_proc_macros_queue.request_op(cause, proc_macro_paths);
+ }
+ let mut change = Change::new();
+ change.set_crate_graph(crate_graph);
self.analysis_host.apply_change(change);
+ self.crate_graph_file_dependencies = crate_graph_file_dependencies;
self.process_changes();
+
self.reload_flycheck();
- tracing::info!("did switch workspaces");
}
pub(super) fn fetch_workspace_error(&self) -> Result<(), String> {
let mut buf = String::new();
- let Some(last_op_result) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) };
+ let Some((last_op_result, _)) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) };
if last_op_result.is_empty() {
stdx::format_to!(buf, "rust-analyzer failed to discover workspace");
} else {
@@ -642,14 +767,12 @@ impl SourceRootConfig {
/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
/// with an identity dummy expander.
pub(crate) fn load_proc_macro(
- server: Result<&ProcMacroServer, &str>,
+ server: &ProcMacroServer,
path: &AbsPath,
dummy_replace: &[Box<str>],
) -> ProcMacroLoadResult {
- let server = server.map_err(ToOwned::to_owned)?;
let res: Result<Vec<_>, String> = (|| {
- let dylib = MacroDylib::new(path.to_path_buf())
- .map_err(|io| format!("Proc-macro dylib loading failed: {io}"))?;
+ let dylib = MacroDylib::new(path.to_path_buf());
let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?;
if vec.is_empty() {
return Err("proc macro library returned no proc macros".to_string());
@@ -684,14 +807,14 @@ pub(crate) fn load_proc_macro(
proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike,
proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr,
};
- let expander: Arc<dyn ProcMacroExpander> =
+ let expander: sync::Arc<dyn ProcMacroExpander> =
if dummy_replace.iter().any(|replace| &**replace == name) {
match kind {
- ProcMacroKind::Attr => Arc::new(IdentityExpander),
- _ => Arc::new(EmptyExpander),
+ ProcMacroKind::Attr => sync::Arc::new(IdentityExpander),
+ _ => sync::Arc::new(EmptyExpander),
}
} else {
- Arc::new(Expander(expander))
+ sync::Arc::new(Expander(expander))
};
ProcMacro { name, kind, expander }
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs
index c2cc3f422..d4bb20c8f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs
@@ -13,7 +13,7 @@ macro_rules! define_semantic_token_types {
$($standard:ident),*$(,)?
}
custom {
- $(($custom:ident, $string:literal)),*$(,)?
+ $(($custom:ident, $string:literal) $(=> $fallback:ident)?),*$(,)?
}
) => {
@@ -24,6 +24,15 @@ macro_rules! define_semantic_token_types {
$(SemanticTokenType::$standard,)*
$($custom),*
];
+
+ pub(crate) fn standard_fallback_type(token: SemanticTokenType) -> Option<SemanticTokenType> {
+ $(
+ if token == $custom {
+ None $(.or(Some(SemanticTokenType::$fallback)))?
+ } else
+ )*
+ { Some(token )}
+ }
};
}
@@ -51,42 +60,46 @@ define_semantic_token_types![
custom {
(ANGLE, "angle"),
- (ARITHMETIC, "arithmetic"),
- (ATTRIBUTE, "attribute"),
- (ATTRIBUTE_BRACKET, "attributeBracket"),
- (BITWISE, "bitwise"),
+ (ARITHMETIC, "arithmetic") => OPERATOR,
+ (ATTRIBUTE, "attribute") => DECORATOR,
+ (ATTRIBUTE_BRACKET, "attributeBracket") => DECORATOR,
+ (BITWISE, "bitwise") => OPERATOR,
(BOOLEAN, "boolean"),
(BRACE, "brace"),
(BRACKET, "bracket"),
- (BUILTIN_ATTRIBUTE, "builtinAttribute"),
+ (BUILTIN_ATTRIBUTE, "builtinAttribute") => DECORATOR,
(BUILTIN_TYPE, "builtinType"),
- (CHAR, "character"),
+ (CHAR, "character") => STRING,
(COLON, "colon"),
(COMMA, "comma"),
- (COMPARISON, "comparison"),
+ (COMPARISON, "comparison") => OPERATOR,
(CONST_PARAMETER, "constParameter"),
- (DERIVE, "derive"),
- (DERIVE_HELPER, "deriveHelper"),
+ (DERIVE, "derive") => DECORATOR,
+ (DERIVE_HELPER, "deriveHelper") => DECORATOR,
(DOT, "dot"),
- (ESCAPE_SEQUENCE, "escapeSequence"),
- (FORMAT_SPECIFIER, "formatSpecifier"),
- (GENERIC, "generic"),
+ (ESCAPE_SEQUENCE, "escapeSequence") => STRING,
+ (FORMAT_SPECIFIER, "formatSpecifier") => STRING,
+ (GENERIC, "generic") => TYPE_PARAMETER,
(LABEL, "label"),
(LIFETIME, "lifetime"),
- (LOGICAL, "logical"),
- (MACRO_BANG, "macroBang"),
+ (LOGICAL, "logical") => OPERATOR,
+ (MACRO_BANG, "macroBang") => MACRO,
(PARENTHESIS, "parenthesis"),
(PUNCTUATION, "punctuation"),
- (SELF_KEYWORD, "selfKeyword"),
- (SELF_TYPE_KEYWORD, "selfTypeKeyword"),
+ (SELF_KEYWORD, "selfKeyword") => KEYWORD,
+ (SELF_TYPE_KEYWORD, "selfTypeKeyword") => KEYWORD,
(SEMICOLON, "semicolon"),
(TYPE_ALIAS, "typeAlias"),
- (TOOL_MODULE, "toolModule"),
+ (TOOL_MODULE, "toolModule") => DECORATOR,
(UNION, "union"),
(UNRESOLVED_REFERENCE, "unresolvedReference"),
}
];
+macro_rules! count_tts {
+ () => {0usize};
+ ($_head:tt $($tail:tt)*) => {1usize + count_tts!($($tail)*)};
+}
macro_rules! define_semantic_token_modifiers {
(
standard {
@@ -105,6 +118,8 @@ macro_rules! define_semantic_token_modifiers {
$(SemanticTokenModifier::$standard,)*
$($custom),*
];
+
+ const LAST_STANDARD_MOD: usize = count_tts!($($standard)*);
};
}
@@ -126,6 +141,7 @@ define_semantic_token_modifiers![
(INJECTED, "injected"),
(INTRA_DOC_LINK, "intraDocLink"),
(LIBRARY, "library"),
+ (MACRO_MODIFIER, "macro"),
(MUTABLE, "mutable"),
(PUBLIC, "public"),
(REFERENCE, "reference"),
@@ -137,6 +153,13 @@ define_semantic_token_modifiers![
#[derive(Default)]
pub(crate) struct ModifierSet(pub(crate) u32);
+impl ModifierSet {
+ pub(crate) fn standard_fallback(&mut self) {
+ // Remove all non standard modifiers
+ self.0 = self.0 & !(!0u32 << LAST_STANDARD_MOD)
+ }
+}
+
impl ops::BitOrAssign<SemanticTokenModifier> for ModifierSet {
fn bitor_assign(&mut self, rhs: SemanticTokenModifier) {
let idx = SUPPORTED_MODIFIERS.iter().position(|it| it == &rhs).unwrap();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs
index 616e44998..a5a10e869 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs
@@ -1,53 +1,42 @@
-//! A thin wrapper around `ThreadPool` to make sure that we join all things
-//! properly.
+//! A thin wrapper around [`stdx::thread::Pool`] which threads a sender through spawned jobs.
+//! It is used in [`crate::global_state::GlobalState`] throughout the main loop.
+
use crossbeam_channel::Sender;
+use stdx::thread::{Pool, ThreadIntent};
pub(crate) struct TaskPool<T> {
sender: Sender<T>,
- inner: threadpool::ThreadPool,
+ pool: Pool,
}
impl<T> TaskPool<T> {
pub(crate) fn new_with_threads(sender: Sender<T>, threads: usize) -> TaskPool<T> {
- const STACK_SIZE: usize = 8 * 1024 * 1024;
-
- let inner = threadpool::Builder::new()
- .thread_name("Worker".into())
- .thread_stack_size(STACK_SIZE)
- .num_threads(threads)
- .build();
- TaskPool { sender, inner }
+ TaskPool { sender, pool: Pool::new(threads) }
}
- pub(crate) fn spawn<F>(&mut self, task: F)
+ pub(crate) fn spawn<F>(&mut self, intent: ThreadIntent, task: F)
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
- self.inner.execute({
+ self.pool.spawn(intent, {
let sender = self.sender.clone();
move || sender.send(task()).unwrap()
})
}
- pub(crate) fn spawn_with_sender<F>(&mut self, task: F)
+ pub(crate) fn spawn_with_sender<F>(&mut self, intent: ThreadIntent, task: F)
where
F: FnOnce(Sender<T>) + Send + 'static,
T: Send + 'static,
{
- self.inner.execute({
+ self.pool.spawn(intent, {
let sender = self.sender.clone();
move || task(sender)
})
}
pub(crate) fn len(&self) -> usize {
- self.inner.queued_count()
- }
-}
-
-impl<T> Drop for TaskPool<T> {
- fn drop(&mut self) {
- self.inner.join()
+ self.pool.len()
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
index 7d97b69f8..648bc995a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
@@ -24,7 +24,7 @@ use crate::{
line_index::{LineEndings, LineIndex, PositionEncoding},
lsp_ext,
lsp_utils::invalid_params_error,
- semantic_tokens,
+ semantic_tokens::{self, standard_fallback_type},
};
pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
@@ -32,7 +32,7 @@ pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::P
match line_index.encoding {
PositionEncoding::Utf8 => lsp_types::Position::new(line_col.line, line_col.col),
PositionEncoding::Wide(enc) => {
- let line_col = line_index.index.to_wide(enc, line_col);
+ let line_col = line_index.index.to_wide(enc, line_col).unwrap();
lsp_types::Position::new(line_col.line, line_col.col)
}
}
@@ -279,7 +279,7 @@ fn completion_item(
let mut lsp_item = lsp_types::CompletionItem {
label: item.label.to_string(),
- detail: item.detail.map(|it| it.to_string()),
+ detail: item.detail,
filter_text: Some(lookup),
kind: Some(completion_item_kind(item.kind)),
text_edit: Some(text_edit),
@@ -306,12 +306,10 @@ fn completion_item(
let imports: Vec<_> = item
.import_to_add
.into_iter()
- .filter_map(|import_edit| {
- let import_path = &import_edit.import_path;
- let import_name = import_path.segments().last()?;
+ .filter_map(|(import_path, import_name)| {
Some(lsp_ext::CompletionImport {
- full_import_path: import_path.to_string(),
- imported_name: import_name.to_string(),
+ full_import_path: import_path,
+ imported_name: import_name,
})
})
.collect();
@@ -412,7 +410,7 @@ pub(crate) fn signature_help(
let documentation = call_info.doc.filter(|_| config.docs).map(|doc| {
lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
- value: doc,
+ value: crate::markdown::format_docs(&doc),
})
});
@@ -434,83 +432,23 @@ pub(crate) fn signature_help(
pub(crate) fn inlay_hint(
snap: &GlobalStateSnapshot,
line_index: &LineIndex,
- render_colons: bool,
- mut inlay_hint: InlayHint,
+ inlay_hint: InlayHint,
) -> Cancellable<lsp_types::InlayHint> {
- match inlay_hint.kind {
- InlayKind::Parameter if render_colons => inlay_hint.label.append_str(":"),
- InlayKind::Type if render_colons => inlay_hint.label.prepend_str(": "),
- InlayKind::ClosureReturnType => inlay_hint.label.prepend_str(" -> "),
- InlayKind::Discriminant => inlay_hint.label.prepend_str(" = "),
- _ => {}
- }
-
let (label, tooltip) = inlay_hint_label(snap, inlay_hint.label)?;
Ok(lsp_types::InlayHint {
- position: match inlay_hint.kind {
- // before annotated thing
- InlayKind::OpeningParenthesis
- | InlayKind::Parameter
- | InlayKind::Adjustment
- | InlayKind::BindingMode => position(line_index, inlay_hint.range.start()),
- // after annotated thing
- InlayKind::ClosureReturnType
- | InlayKind::Type
- | InlayKind::Discriminant
- | InlayKind::Chaining
- | InlayKind::GenericParamList
- | InlayKind::ClosingParenthesis
- | InlayKind::AdjustmentPostfix
- | InlayKind::Lifetime
- | InlayKind::ClosingBrace => position(line_index, inlay_hint.range.end()),
+ position: match inlay_hint.position {
+ ide::InlayHintPosition::Before => position(line_index, inlay_hint.range.start()),
+ ide::InlayHintPosition::After => position(line_index, inlay_hint.range.end()),
},
- padding_left: Some(match inlay_hint.kind {
- InlayKind::Type => !render_colons,
- InlayKind::Chaining | InlayKind::ClosingBrace => true,
- InlayKind::ClosingParenthesis
- | InlayKind::Discriminant
- | InlayKind::OpeningParenthesis
- | InlayKind::BindingMode
- | InlayKind::ClosureReturnType
- | InlayKind::GenericParamList
- | InlayKind::Adjustment
- | InlayKind::AdjustmentPostfix
- | InlayKind::Lifetime
- | InlayKind::Parameter => false,
- }),
- padding_right: Some(match inlay_hint.kind {
- InlayKind::ClosingParenthesis
- | InlayKind::OpeningParenthesis
- | InlayKind::Chaining
- | InlayKind::ClosureReturnType
- | InlayKind::GenericParamList
- | InlayKind::Adjustment
- | InlayKind::AdjustmentPostfix
- | InlayKind::Type
- | InlayKind::Discriminant
- | InlayKind::ClosingBrace => false,
- InlayKind::BindingMode => {
- matches!(&label, lsp_types::InlayHintLabel::String(s) if s != "&")
- }
- InlayKind::Parameter | InlayKind::Lifetime => true,
- }),
+ padding_left: Some(inlay_hint.pad_left),
+ padding_right: Some(inlay_hint.pad_right),
kind: match inlay_hint.kind {
InlayKind::Parameter => Some(lsp_types::InlayHintKind::PARAMETER),
- InlayKind::ClosureReturnType | InlayKind::Type | InlayKind::Chaining => {
- Some(lsp_types::InlayHintKind::TYPE)
- }
- InlayKind::ClosingParenthesis
- | InlayKind::Discriminant
- | InlayKind::OpeningParenthesis
- | InlayKind::BindingMode
- | InlayKind::GenericParamList
- | InlayKind::Lifetime
- | InlayKind::Adjustment
- | InlayKind::AdjustmentPostfix
- | InlayKind::ClosingBrace => None,
+ InlayKind::Type | InlayKind::Chaining => Some(lsp_types::InlayHintKind::TYPE),
+ _ => None,
},
- text_edits: None,
+ text_edits: inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)),
data: None,
tooltip,
label,
@@ -580,6 +518,8 @@ pub(crate) fn semantic_tokens(
text: &str,
line_index: &LineIndex,
highlights: Vec<HlRange>,
+ semantics_tokens_augments_syntax_tokens: bool,
+ non_standard_tokens: bool,
) -> lsp_types::SemanticTokens {
let id = TOKEN_RESULT_COUNTER.fetch_add(1, Ordering::SeqCst).to_string();
let mut builder = semantic_tokens::SemanticTokensBuilder::new(id);
@@ -589,7 +529,35 @@ pub(crate) fn semantic_tokens(
continue;
}
- let (ty, mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
+ if semantics_tokens_augments_syntax_tokens {
+ match highlight_range.highlight.tag {
+ HlTag::BoolLiteral
+ | HlTag::ByteLiteral
+ | HlTag::CharLiteral
+ | HlTag::Comment
+ | HlTag::Keyword
+ | HlTag::NumericLiteral
+ | HlTag::Operator(_)
+ | HlTag::Punctuation(_)
+ | HlTag::StringLiteral
+ | HlTag::None
+ if highlight_range.highlight.mods.is_empty() =>
+ {
+ continue
+ }
+ _ => (),
+ }
+ }
+
+ let (mut ty, mut mods) = semantic_token_type_and_modifiers(highlight_range.highlight);
+
+ if !non_standard_tokens {
+ ty = match standard_fallback_type(ty) {
+ Some(ty) => ty,
+ None => continue,
+ };
+ mods.standard_fallback();
+ }
let token_index = semantic_tokens::type_index(ty);
let modifier_bitset = mods.0;
@@ -710,6 +678,7 @@ fn semantic_token_type_and_modifiers(
HlMod::Injected => semantic_tokens::INJECTED,
HlMod::IntraDocLink => semantic_tokens::INTRA_DOC_LINK,
HlMod::Library => semantic_tokens::LIBRARY,
+ HlMod::Macro => semantic_tokens::MACRO_MODIFIER,
HlMod::Mutable => semantic_tokens::MUTABLE,
HlMod::Public => semantic_tokens::PUBLIC,
HlMod::Reference => semantic_tokens::REFERENCE,
@@ -1215,6 +1184,14 @@ pub(crate) fn code_lens(
data: None,
})
}
+ if lens_config.interpret {
+ let command = command::interpret_single(&r);
+ acc.push(lsp_types::CodeLens {
+ range: annotation_range,
+ command: Some(command),
+ data: None,
+ })
+ }
}
AnnotationKind::HasImpls { pos: file_range, data } => {
if !client_commands_config.show_reference {
@@ -1257,7 +1234,16 @@ pub(crate) fn code_lens(
acc.push(lsp_types::CodeLens {
range: annotation_range,
command,
- data: Some(to_value(lsp_ext::CodeLensResolveData::Impls(goto_params)).unwrap()),
+ data: (|| {
+ let version = snap.url_file_version(&url)?;
+ Some(
+ to_value(lsp_ext::CodeLensResolveData {
+ version,
+ kind: lsp_ext::CodeLensResolveDataKind::Impls(goto_params),
+ })
+ .unwrap(),
+ )
+ })(),
})
}
AnnotationKind::HasReferences { pos: file_range, data } => {
@@ -1287,7 +1273,16 @@ pub(crate) fn code_lens(
acc.push(lsp_types::CodeLens {
range: annotation_range,
command,
- data: Some(to_value(lsp_ext::CodeLensResolveData::References(doc_pos)).unwrap()),
+ data: (|| {
+ let version = snap.url_file_version(&url)?;
+ Some(
+ to_value(lsp_ext::CodeLensResolveData {
+ version,
+ kind: lsp_ext::CodeLensResolveDataKind::References(doc_pos),
+ })
+ .unwrap(),
+ )
+ })(),
})
}
}
@@ -1341,6 +1336,15 @@ pub(crate) mod command {
}
}
+ pub(crate) fn interpret_single(_runnable: &lsp_ext::Runnable) -> lsp_types::Command {
+ lsp_types::Command {
+ title: "Interpret".into(),
+ command: "rust-analyzer.interpretFunction".into(),
+ // FIXME: use the `_runnable` here.
+ arguments: Some(vec![]),
+ }
+ }
+
pub(crate) fn goto_location(
snap: &GlobalStateSnapshot,
nav: &NavigationTarget,
@@ -1406,9 +1410,9 @@ pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
#[cfg(test)]
mod tests {
- use std::sync::Arc;
-
- use ide::Analysis;
+ use ide::{Analysis, FilePosition};
+ use test_utils::extract_offset;
+ use triomphe::Arc;
use super::*;
@@ -1448,6 +1452,34 @@ fn main() {
}
}
+ #[test]
+ fn calling_function_with_ignored_code_in_signature() {
+ let text = r#"
+fn foo() {
+ bar($0);
+}
+/// ```
+/// # use crate::bar;
+/// bar(5);
+/// ```
+fn bar(_: usize) {}
+"#;
+
+ let (offset, text) = extract_offset(text);
+ let (analysis, file_id) = Analysis::from_single_file(text);
+ let help = signature_help(
+ analysis.signature_help(FilePosition { file_id, offset }).unwrap().unwrap(),
+ CallInfoConfig { params_only: false, docs: true },
+ false,
+ );
+ let docs = match &help.signatures[help.active_signature.unwrap() as usize].documentation {
+ Some(lsp_types::Documentation::MarkupContent(content)) => &content.value,
+ _ => panic!("documentation contains markup"),
+ };
+ assert!(docs.contains("bar(5)"));
+ assert!(!docs.contains("use crate::bar"));
+ }
+
// `Url` is not able to parse windows paths on unix machines.
#[test]
#[cfg(target_os = "windows")]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index 587d64096..0bb29e708 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -59,7 +59,7 @@ use std::collections::Spam;
"#,
)
.with_config(serde_json::json!({
- "cargo": { "sysroot": "discover" }
+ "cargo": { "sysroot": "discover" },
}))
.server()
.wait_until_workspace_is_loaded();
@@ -508,7 +508,7 @@ fn main() {}
#[test]
fn test_missing_module_code_action_in_json_project() {
if skip_slow_tests() {
- // return;
+ return;
}
let tmp_dir = TestDir::new();
@@ -612,7 +612,7 @@ fn main() {{}}
"#
))
.with_config(serde_json::json!({
- "cargo": { "sysroot": "discover" }
+ "cargo": { "sysroot": "discover" },
}))
.server()
.wait_until_workspace_is_loaded();
@@ -685,7 +685,7 @@ version = \"0.0.0\"
#[test]
fn out_dirs_check() {
if skip_slow_tests() {
- // return;
+ return;
}
let server = Project::with_fixture(
@@ -711,10 +711,21 @@ fn main() {
println!("cargo:rerun-if-changed=build.rs");
}
//- /src/main.rs
-#[rustc_builtin_macro] macro_rules! include {}
-#[rustc_builtin_macro] macro_rules! include_str {}
-#[rustc_builtin_macro] macro_rules! concat {}
-#[rustc_builtin_macro] macro_rules! env {}
+#![allow(warnings)]
+#![feature(rustc_attrs)]
+#[rustc_builtin_macro] macro_rules! include {
+ ($file:expr $(,)?) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro] macro_rules! include_str {
+ ($file:expr $(,)?) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro] macro_rules! concat {
+ ($($e:ident),+ $(,)?) => {{ /* compiler built-in */ }};
+}
+#[rustc_builtin_macro] macro_rules! env {
+ ($name:expr $(,)?) => {{ /* compiler built-in */ }};
+ ($name:expr, $error_msg:expr $(,)?) => {{ /* compiler built-in */ }};
+}
include!(concat!(env!("OUT_DIR"), "/hello.rs"));
@@ -741,6 +752,9 @@ fn main() {
"enable": true
},
"sysroot": null,
+ "extraEnv": {
+ "RUSTC_BOOTSTRAP": "1"
+ }
}
}))
.server()
@@ -749,7 +763,7 @@ fn main() {
let res = server.send_request::<HoverRequest>(HoverParams {
text_document_position_params: TextDocumentPositionParams::new(
server.doc_id("src/main.rs"),
- Position::new(19, 10),
+ Position::new(30, 10),
),
work_done_progress_params: Default::default(),
});
@@ -758,7 +772,7 @@ fn main() {
let res = server.send_request::<HoverRequest>(HoverParams {
text_document_position_params: TextDocumentPositionParams::new(
server.doc_id("src/main.rs"),
- Position::new(20, 10),
+ Position::new(31, 10),
),
work_done_progress_params: Default::default(),
});
@@ -768,23 +782,23 @@ fn main() {
GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams::new(
server.doc_id("src/main.rs"),
- Position::new(17, 9),
+ Position::new(28, 9),
),
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
},
json!([{
"originSelectionRange": {
- "end": { "character": 10, "line": 17 },
- "start": { "character": 8, "line": 17 }
+ "end": { "character": 10, "line": 28 },
+ "start": { "character": 8, "line": 28 }
},
"targetRange": {
- "end": { "character": 9, "line": 8 },
- "start": { "character": 0, "line": 7 }
+ "end": { "character": 9, "line": 19 },
+ "start": { "character": 0, "line": 18 }
},
"targetSelectionRange": {
- "end": { "character": 8, "line": 8 },
- "start": { "character": 7, "line": 8 }
+ "end": { "character": 8, "line": 19 },
+ "start": { "character": 7, "line": 19 }
},
"targetUri": "file:///[..]src/main.rs"
}]),
@@ -794,23 +808,23 @@ fn main() {
GotoDefinitionParams {
text_document_position_params: TextDocumentPositionParams::new(
server.doc_id("src/main.rs"),
- Position::new(18, 9),
+ Position::new(29, 9),
),
work_done_progress_params: Default::default(),
partial_result_params: Default::default(),
},
json!([{
"originSelectionRange": {
- "end": { "character": 10, "line": 18 },
- "start": { "character": 8, "line": 18 }
+ "end": { "character": 10, "line": 29 },
+ "start": { "character": 8, "line": 29 }
},
"targetRange": {
- "end": { "character": 9, "line": 12 },
- "start": { "character": 0, "line":11 }
+ "end": { "character": 9, "line": 23 },
+ "start": { "character": 0, "line": 22 }
},
"targetSelectionRange": {
- "end": { "character": 8, "line": 12 },
- "start": { "character": 7, "line": 12 }
+ "end": { "character": 8, "line": 23 },
+ "start": { "character": 7, "line": 23 }
},
"targetUri": "file:///[..]src/main.rs"
}]),
@@ -818,14 +832,24 @@ fn main() {
}
#[test]
-// FIXME: Re-enable once we can run proc-macro tests on rust-lang/rust-analyzer again
-#[cfg(any())]
+#[cfg(feature = "sysroot-abi")]
fn resolve_proc_macro() {
use expect_test::expect;
if skip_slow_tests() {
return;
}
+ // skip using the sysroot config as to prevent us from loading the sysroot sources
+ let mut rustc = std::process::Command::new(toolchain::rustc());
+ rustc.args(["--print", "sysroot"]);
+ let output = rustc.output().unwrap();
+ let sysroot =
+ vfs::AbsPathBuf::try_from(std::str::from_utf8(&output.stdout).unwrap().trim()).unwrap();
+
+ let standalone_server_name =
+ format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
+ let proc_macro_server_path = sysroot.join("libexec").join(&standalone_server_name);
+
let server = Project::with_fixture(
r###"
//- /foo/Cargo.toml
@@ -837,6 +861,7 @@ edition = "2021"
bar = {path = "../bar"}
//- /foo/src/main.rs
+#![feature(rustc_attrs, decl_macro)]
use bar::Bar;
#[rustc_builtin_macro]
@@ -902,7 +927,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
},
"procMacro": {
"enable": true,
- "server": PathBuf::from(env!("CARGO_BIN_EXE_rust-analyzer")),
+ "server": proc_macro_server_path.as_path().as_ref(),
}
}))
.root("foo")
@@ -913,7 +938,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
let res = server.send_request::<HoverRequest>(HoverParams {
text_document_position_params: TextDocumentPositionParams::new(
server.doc_id("foo/src/main.rs"),
- Position::new(10, 9),
+ Position::new(11, 9),
),
work_done_progress_params: Default::default(),
});
@@ -1083,10 +1108,18 @@ version = "0.0.0"
//- /bar/src/lib.rs
pub fn bar() {}
+
+//- /baz/Cargo.toml
+[package]
+name = "baz"
+version = "0.0.0"
+
+//- /baz/src/lib.rs
"#,
)
.root("foo")
.root("bar")
+ .root("baz")
.with_config(json!({
"files": {
"excludeDirs": ["foo", "bar"]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
index 037fc89ac..b2a8041ae 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -9,11 +9,10 @@ use std::{
use crossbeam_channel::{after, select, Receiver};
use lsp_server::{Connection, Message, Notification, Request};
use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url};
-use project_model::ProjectManifest;
use rust_analyzer::{config::Config, lsp_ext, main_loop};
use serde::Serialize;
use serde_json::{json, to_string_pretty, Value};
-use test_utils::Fixture;
+use test_utils::FixtureWithProjectMeta;
use vfs::AbsPathBuf;
use crate::testdir::TestDir;
@@ -37,8 +36,12 @@ impl<'a> Project<'a> {
"sysroot": null,
// Can't use test binary as rustc wrapper.
"buildScripts": {
- "useRustcWrapper": false
+ "useRustcWrapper": false,
+ "enable": false,
},
+ },
+ "procMacro": {
+ "enable": false,
}
}),
}
@@ -80,10 +83,12 @@ impl<'a> Project<'a> {
profile::init_from(crate::PROFILE);
});
- let (mini_core, proc_macros, fixtures) = Fixture::parse(self.fixture);
- assert!(proc_macros.is_empty());
+ let FixtureWithProjectMeta { fixture, mini_core, proc_macro_names, toolchain } =
+ FixtureWithProjectMeta::parse(self.fixture);
+ assert!(proc_macro_names.is_empty());
assert!(mini_core.is_none());
- for entry in fixtures {
+ assert!(toolchain.is_none());
+ for entry in fixture {
let path = tmp_dir.path().join(&entry.path['/'.len_utf8()..]);
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(path.as_path(), entry.text.as_bytes()).unwrap();
@@ -95,10 +100,6 @@ impl<'a> Project<'a> {
if roots.is_empty() {
roots.push(tmp_dir_path.clone());
}
- let discovered_projects = roots
- .into_iter()
- .map(|it| ProjectManifest::discover_single(&it).unwrap())
- .collect::<Vec<_>>();
let mut config = Config::new(
tmp_dir_path,
@@ -138,10 +139,10 @@ impl<'a> Project<'a> {
})),
..Default::default()
},
- Vec::new(),
+ roots,
);
- config.discovered_projects = Some(discovered_projects);
config.update(self.config).expect("invalid config");
+ config.rediscover_workspaces();
Server::new(tmp_dir, config)
}
@@ -154,7 +155,7 @@ pub(crate) fn project(fixture: &str) -> Server {
pub(crate) struct Server {
req_id: Cell<i32>,
messages: RefCell<Vec<Message>>,
- _thread: jod_thread::JoinHandle<()>,
+ _thread: stdx::thread::JoinHandle,
client: Connection,
/// XXX: remove the tempdir last
dir: TestDir,
@@ -164,7 +165,7 @@ impl Server {
fn new(dir: TestDir, config: Config) -> Server {
let (connection, client) = Connection::memory();
- let _thread = jod_thread::Builder::new()
+ let _thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
.name("test server".to_string())
.spawn(move || main_loop(config, connection).unwrap())
.expect("failed to spawn a thread");
@@ -251,6 +252,9 @@ impl Server {
.clone()
.extract::<lsp_ext::ServerStatusParams>("experimental/serverStatus")
.unwrap();
+ if status.health != lsp_ext::Health::Ok {
+ panic!("server errored/warned while loading workspace: {:?}", status.message);
+ }
status.quiescent
}
_ => false,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
index 8e3097fce..f230cba2b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
@@ -257,6 +257,8 @@ fn check_dbg(path: &Path, text: &str) {
"ide-db/src/generated/lints.rs",
// test for doc test for remove_dbg
"src/tests/generated.rs",
+ // `expect!` string can contain `dbg!` (due to .dbg postfix)
+ "ide-completion/src/tests/special.rs",
];
if need_dbg.iter().any(|p| path.ends_with(p)) {
return;
diff --git a/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs b/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs
index 72d26635c..c5da6ceb4 100644
--- a/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/sourcegen/src/lib.rs
@@ -58,21 +58,19 @@ impl CommentBlock {
assert!(tag.starts_with(char::is_uppercase));
let tag = format!("{tag}:");
- // Would be nice if we had `.retain_mut` here!
- CommentBlock::extract_untagged(text)
- .into_iter()
- .filter_map(|mut block| {
- let first = block.contents.remove(0);
- first.strip_prefix(&tag).map(|id| {
- if block.is_doc {
- panic!("Use plain (non-doc) comments with tags like {tag}:\n {first}");
- }
+ let mut blocks = CommentBlock::extract_untagged(text);
+ blocks.retain_mut(|block| {
+ let first = block.contents.remove(0);
+ let Some(id) = first.strip_prefix(&tag) else { return false; };
+
+ if block.is_doc {
+ panic!("Use plain (non-doc) comments with tags like {tag}:\n {first}");
+ }
- block.id = id.trim().to_string();
- block
- })
- })
- .collect()
+ block.id = id.trim().to_string();
+ true
+ });
+ blocks
}
pub fn extract_untagged(text: &str) -> Vec<CommentBlock> {
diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
index c881f2fd3..a67f36ae9 100644
--- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
@@ -15,6 +15,8 @@ doctest = false
libc = "0.2.135"
backtrace = { version = "0.3.65", optional = true }
always-assert = { version = "0.1.2", features = ["log"] }
+jod-thread = "0.1.2"
+crossbeam-channel = "0.5.5"
# Think twice before adding anything here
[target.'cfg(windows)'.dependencies]
diff --git a/src/tools/rust-analyzer/crates/stdx/src/hash.rs b/src/tools/rust-analyzer/crates/stdx/src/hash.rs
deleted file mode 100644
index 0c21d2674..000000000
--- a/src/tools/rust-analyzer/crates/stdx/src/hash.rs
+++ /dev/null
@@ -1,80 +0,0 @@
-//! A none hashing [`Hasher`] implementation.
-use std::{
- hash::{BuildHasher, Hasher},
- marker::PhantomData,
-};
-
-pub type NoHashHashMap<K, V> = std::collections::HashMap<K, V, NoHashHasherBuilder<K>>;
-pub type NoHashHashSet<K> = std::collections::HashSet<K, NoHashHasherBuilder<K>>;
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub struct NoHashHasherBuilder<T>(PhantomData<T>);
-
-impl<T> Default for NoHashHasherBuilder<T> {
- fn default() -> Self {
- Self(Default::default())
- }
-}
-
-pub trait NoHashHashable {}
-impl NoHashHashable for usize {}
-impl NoHashHashable for u32 {}
-
-pub struct NoHashHasher(u64);
-
-impl<T: NoHashHashable> BuildHasher for NoHashHasherBuilder<T> {
- type Hasher = NoHashHasher;
- fn build_hasher(&self) -> Self::Hasher {
- NoHashHasher(0)
- }
-}
-
-impl Hasher for NoHashHasher {
- fn finish(&self) -> u64 {
- self.0
- }
-
- fn write(&mut self, _: &[u8]) {
- unimplemented!("NoHashHasher should only be used for hashing primitive integers")
- }
-
- fn write_u8(&mut self, i: u8) {
- self.0 = i as u64;
- }
-
- fn write_u16(&mut self, i: u16) {
- self.0 = i as u64;
- }
-
- fn write_u32(&mut self, i: u32) {
- self.0 = i as u64;
- }
-
- fn write_u64(&mut self, i: u64) {
- self.0 = i;
- }
-
- fn write_usize(&mut self, i: usize) {
- self.0 = i as u64;
- }
-
- fn write_i8(&mut self, i: i8) {
- self.0 = i as u64;
- }
-
- fn write_i16(&mut self, i: i16) {
- self.0 = i as u64;
- }
-
- fn write_i32(&mut self, i: i32) {
- self.0 = i as u64;
- }
-
- fn write_i64(&mut self, i: i64) {
- self.0 = i as u64;
- }
-
- fn write_isize(&mut self, i: isize) {
- self.0 = i as u64;
- }
-}
diff --git a/src/tools/rust-analyzer/crates/stdx/src/lib.rs b/src/tools/rust-analyzer/crates/stdx/src/lib.rs
index 5639aaf57..24990d6a0 100644
--- a/src/tools/rust-analyzer/crates/stdx/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/stdx/src/lib.rs
@@ -7,11 +7,11 @@ use std::process::Command;
use std::{cmp::Ordering, ops, time::Instant};
mod macros;
-pub mod hash;
pub mod process;
pub mod panic_context;
pub mod non_empty_vec;
pub mod rand;
+pub mod thread;
pub use always_assert::{always, never};
diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread.rs b/src/tools/rust-analyzer/crates/stdx/src/thread.rs
new file mode 100644
index 000000000..e577eb431
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/stdx/src/thread.rs
@@ -0,0 +1,102 @@
+//! A utility module for working with threads that automatically joins threads upon drop
+//! and abstracts over operating system quality of service (QoS) APIs
+//! through the concept of a “thread intent”.
+//!
+//! The intent of a thread is frozen at thread creation time,
+//! i.e. there is no API to change the intent of a thread once it has been spawned.
+//!
+//! As a system, rust-analyzer should have the property that
+//! old manual scheduling APIs are replaced entirely by QoS.
+//! To maintain this invariant, we panic when it is clear that
+//! old scheduling APIs have been used.
+//!
+//! Moreover, we also want to ensure that every thread has an intent set explicitly
+//! to force a decision about its importance to the system.
+//! Thus, [`ThreadIntent`] has no default value
+//! and every entry point to creating a thread requires a [`ThreadIntent`] upfront.
+
+use std::fmt;
+
+mod intent;
+mod pool;
+
+pub use intent::ThreadIntent;
+pub use pool::Pool;
+
+pub fn spawn<F, T>(intent: ThreadIntent, f: F) -> JoinHandle<T>
+where
+ F: FnOnce() -> T,
+ F: Send + 'static,
+ T: Send + 'static,
+{
+ Builder::new(intent).spawn(f).expect("failed to spawn thread")
+}
+
+pub struct Builder {
+ intent: ThreadIntent,
+ inner: jod_thread::Builder,
+ allow_leak: bool,
+}
+
+impl Builder {
+ pub fn new(intent: ThreadIntent) -> Builder {
+ Builder { intent, inner: jod_thread::Builder::new(), allow_leak: false }
+ }
+
+ pub fn name(self, name: String) -> Builder {
+ Builder { inner: self.inner.name(name), ..self }
+ }
+
+ pub fn stack_size(self, size: usize) -> Builder {
+ Builder { inner: self.inner.stack_size(size), ..self }
+ }
+
+ pub fn allow_leak(self, b: bool) -> Builder {
+ Builder { allow_leak: b, ..self }
+ }
+
+ pub fn spawn<F, T>(self, f: F) -> std::io::Result<JoinHandle<T>>
+ where
+ F: FnOnce() -> T,
+ F: Send + 'static,
+ T: Send + 'static,
+ {
+ let inner_handle = self.inner.spawn(move || {
+ self.intent.apply_to_current_thread();
+ f()
+ })?;
+
+ Ok(JoinHandle { inner: Some(inner_handle), allow_leak: self.allow_leak })
+ }
+}
+
+pub struct JoinHandle<T = ()> {
+ // `inner` is an `Option` so that we can
+ // take ownership of the contained `JoinHandle`.
+ inner: Option<jod_thread::JoinHandle<T>>,
+ allow_leak: bool,
+}
+
+impl<T> JoinHandle<T> {
+ pub fn join(mut self) -> T {
+ self.inner.take().unwrap().join()
+ }
+}
+
+impl<T> Drop for JoinHandle<T> {
+ fn drop(&mut self) {
+ if !self.allow_leak {
+ return;
+ }
+
+ if let Some(join_handle) = self.inner.take() {
+ join_handle.detach();
+ }
+ }
+}
+
+impl<T> fmt::Debug for JoinHandle<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad("JoinHandle { .. }")
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/intent.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/intent.rs
new file mode 100644
index 000000000..7b65db30c
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/stdx/src/thread/intent.rs
@@ -0,0 +1,287 @@
+//! An opaque façade around platform-specific QoS APIs.
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+// Please maintain order from least to most priority for the derived `Ord` impl.
+pub enum ThreadIntent {
+ /// Any thread which does work that isn’t in the critical path of the user typing
+ /// (e.g. processing Go To Definition).
+ Worker,
+
+ /// Any thread which does work caused by the user typing
+ /// (e.g. processing syntax highlighting).
+ LatencySensitive,
+}
+
+impl ThreadIntent {
+ // These APIs must remain private;
+ // we only want consumers to set thread intent
+ // either during thread creation or using our pool impl.
+
+ pub(super) fn apply_to_current_thread(self) {
+ let class = thread_intent_to_qos_class(self);
+ set_current_thread_qos_class(class);
+ }
+
+ pub(super) fn assert_is_used_on_current_thread(self) {
+ if IS_QOS_AVAILABLE {
+ let class = thread_intent_to_qos_class(self);
+ assert_eq!(get_current_thread_qos_class(), Some(class));
+ }
+ }
+}
+
+use imp::QoSClass;
+
+const IS_QOS_AVAILABLE: bool = imp::IS_QOS_AVAILABLE;
+
+fn set_current_thread_qos_class(class: QoSClass) {
+ imp::set_current_thread_qos_class(class)
+}
+
+fn get_current_thread_qos_class() -> Option<QoSClass> {
+ imp::get_current_thread_qos_class()
+}
+
+fn thread_intent_to_qos_class(intent: ThreadIntent) -> QoSClass {
+ imp::thread_intent_to_qos_class(intent)
+}
+
+// All Apple platforms use XNU as their kernel
+// and thus have the concept of QoS.
+#[cfg(target_vendor = "apple")]
+mod imp {
+ use super::ThreadIntent;
+
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+ // Please maintain order from least to most priority for the derived `Ord` impl.
+ pub(super) enum QoSClass {
+ // Documentation adapted from https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/include/sys/qos.h#L55
+ //
+ /// TLDR: invisible maintenance tasks
+ ///
+ /// Contract:
+ ///
+ /// * **You do not care about how long it takes for work to finish.**
+ /// * **You do not care about work being deferred temporarily.**
+ /// (e.g. if the device’s battery is in a critical state)
+ ///
+ /// Examples:
+ ///
+ /// * in a video editor:
+ /// creating periodic backups of project files
+ /// * in a browser:
+ /// cleaning up cached sites which have not been accessed in a long time
+ /// * in a collaborative word processor:
+ /// creating a searchable index of all documents
+ ///
+ /// Use this QoS class for background tasks
+ /// which the user did not initiate themselves
+ /// and which are invisible to the user.
+ /// It is expected that this work will take significant time to complete:
+ /// minutes or even hours.
+ ///
+ /// This QoS class provides the most energy and thermally-efficient execution possible.
+ /// All other work is prioritized over background tasks.
+ Background,
+
+ /// TLDR: tasks that don’t block using your app
+ ///
+ /// Contract:
+ ///
+ /// * **Your app remains useful even as the task is executing.**
+ ///
+ /// Examples:
+ ///
+ /// * in a video editor:
+ /// exporting a video to disk –
+ /// the user can still work on the timeline
+ /// * in a browser:
+ /// automatically extracting a downloaded zip file –
+ /// the user can still switch tabs
+ /// * in a collaborative word processor:
+ /// downloading images embedded in a document –
+ /// the user can still make edits
+ ///
+ /// Use this QoS class for tasks which
+ /// may or may not be initiated by the user,
+ /// but whose result is visible.
+ /// It is expected that this work will take a few seconds to a few minutes.
+ /// Typically your app will include a progress bar
+ /// for tasks using this class.
+ ///
+ /// This QoS class provides a balance between
+ /// performance, responsiveness and efficiency.
+ Utility,
+
+ /// TLDR: tasks that block using your app
+ ///
+ /// Contract:
+ ///
+ /// * **You need this work to complete
+ /// before the user can keep interacting with your app.**
+ /// * **Your work will not take more than a few seconds to complete.**
+ ///
+ /// Examples:
+ ///
+ /// * in a video editor:
+ /// opening a saved project
+ /// * in a browser:
+ /// loading a list of the user’s bookmarks and top sites
+ /// when a new tab is created
+ /// * in a collaborative word processor:
+ /// running a search on the document’s content
+ ///
+ /// Use this QoS class for tasks which were initiated by the user
+ /// and block the usage of your app while they are in progress.
+ /// It is expected that this work will take a few seconds or less to complete;
+ /// not long enough to cause the user to switch to something else.
+ /// Your app will likely indicate progress on these tasks
+ /// through the display of placeholder content or modals.
+ ///
+ /// This QoS class is not energy-efficient.
+ /// Rather, it provides responsiveness
+ /// by prioritizing work above other tasks on the system
+ /// except for critical user-interactive work.
+ UserInitiated,
+
+ /// TLDR: render loops and nothing else
+ ///
+ /// Contract:
+ ///
+ /// * **You absolutely need this work to complete immediately
+ /// or your app will appear to freeze.**
+ /// * **Your work will always complete virtually instantaneously.**
+ ///
+ /// Examples:
+ ///
+ /// * the main thread in a GUI application
+ /// * the update & render loop in a game
+ /// * a secondary thread which progresses an animation
+ ///
+ /// Use this QoS class for any work which, if delayed,
+ /// will make your user interface unresponsive.
+ /// It is expected that this work will be virtually instantaneous.
+ ///
+ /// This QoS class is not energy-efficient.
+ /// Specifying this class is a request to run with
+ /// nearly all available system CPU and I/O bandwidth even under contention.
+ UserInteractive,
+ }
+
+ pub(super) const IS_QOS_AVAILABLE: bool = true;
+
+ pub(super) fn set_current_thread_qos_class(class: QoSClass) {
+ let c = match class {
+ QoSClass::UserInteractive => libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE,
+ QoSClass::UserInitiated => libc::qos_class_t::QOS_CLASS_USER_INITIATED,
+ QoSClass::Utility => libc::qos_class_t::QOS_CLASS_UTILITY,
+ QoSClass::Background => libc::qos_class_t::QOS_CLASS_BACKGROUND,
+ };
+
+ let code = unsafe { libc::pthread_set_qos_class_self_np(c, 0) };
+
+ if code == 0 {
+ return;
+ }
+
+ let errno = unsafe { *libc::__error() };
+
+ match errno {
+ libc::EPERM => {
+ // This thread has been excluded from the QoS system
+ // due to a previous call to a function such as `pthread_setschedparam`
+ // which is incompatible with QoS.
+ //
+ // Panic instead of returning an error
+ // to maintain the invariant that we only use QoS APIs.
+ panic!("tried to set QoS of thread which has opted out of QoS (os error {errno})")
+ }
+
+ libc::EINVAL => {
+ // This is returned if we pass something other than a qos_class_t
+ // to `pthread_set_qos_class_self_np`.
+ //
+ // This is impossible, so again panic.
+ unreachable!(
+ "invalid qos_class_t value was passed to pthread_set_qos_class_self_np"
+ )
+ }
+
+ _ => {
+ // `pthread_set_qos_class_self_np`’s documentation
+ // does not mention any other errors.
+ unreachable!("`pthread_set_qos_class_self_np` returned unexpected error {errno}")
+ }
+ }
+ }
+
+ pub(super) fn get_current_thread_qos_class() -> Option<QoSClass> {
+ let current_thread = unsafe { libc::pthread_self() };
+ let mut qos_class_raw = libc::qos_class_t::QOS_CLASS_UNSPECIFIED;
+ let code = unsafe {
+ libc::pthread_get_qos_class_np(current_thread, &mut qos_class_raw, std::ptr::null_mut())
+ };
+
+ if code != 0 {
+ // `pthread_get_qos_class_np`’s documentation states that
+ // an error value is placed into errno if the return code is not zero.
+ // However, it never states what errors are possible.
+ // Inspecting the source[0] shows that, as of this writing, it always returns zero.
+ //
+ // Whatever errors the function could report in future are likely to be
+ // ones which we cannot handle anyway
+ //
+ // 0: https://github.com/apple-oss-distributions/libpthread/blob/67e155c94093be9a204b69637d198eceff2c7c46/src/qos.c#L171-L177
+ let errno = unsafe { *libc::__error() };
+ unreachable!("`pthread_get_qos_class_np` failed unexpectedly (os error {errno})");
+ }
+
+ match qos_class_raw {
+ libc::qos_class_t::QOS_CLASS_USER_INTERACTIVE => Some(QoSClass::UserInteractive),
+ libc::qos_class_t::QOS_CLASS_USER_INITIATED => Some(QoSClass::UserInitiated),
+ libc::qos_class_t::QOS_CLASS_DEFAULT => None, // QoS has never been set
+ libc::qos_class_t::QOS_CLASS_UTILITY => Some(QoSClass::Utility),
+ libc::qos_class_t::QOS_CLASS_BACKGROUND => Some(QoSClass::Background),
+
+ libc::qos_class_t::QOS_CLASS_UNSPECIFIED => {
+ // Using manual scheduling APIs causes threads to “opt out” of QoS.
+ // At this point they become incompatible with QoS,
+ // and as such have the “unspecified” QoS class.
+ //
+ // Panic instead of returning an error
+ // to maintain the invariant that we only use QoS APIs.
+ panic!("tried to get QoS of thread which has opted out of QoS")
+ }
+ }
+ }
+
+ pub(super) fn thread_intent_to_qos_class(intent: ThreadIntent) -> QoSClass {
+ match intent {
+ ThreadIntent::Worker => QoSClass::Utility,
+ ThreadIntent::LatencySensitive => QoSClass::UserInitiated,
+ }
+ }
+}
+
+// FIXME: Windows has QoS APIs, we should use them!
+#[cfg(not(target_vendor = "apple"))]
+mod imp {
+ use super::ThreadIntent;
+
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+ pub(super) enum QoSClass {
+ Default,
+ }
+
+ pub(super) const IS_QOS_AVAILABLE: bool = false;
+
+ pub(super) fn set_current_thread_qos_class(_: QoSClass) {}
+
+ pub(super) fn get_current_thread_qos_class() -> Option<QoSClass> {
+ None
+ }
+
+ pub(super) fn thread_intent_to_qos_class(_: ThreadIntent) -> QoSClass {
+ QoSClass::Default
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
new file mode 100644
index 000000000..2ddd7da74
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs
@@ -0,0 +1,92 @@
+//! [`Pool`] implements a basic custom thread pool
+//! inspired by the [`threadpool` crate](http://docs.rs/threadpool).
+//! When you spawn a task you specify a thread intent
+//! so the pool can schedule it to run on a thread with that intent.
+//! rust-analyzer uses this to prioritize work based on latency requirements.
+//!
+//! The thread pool is implemented entirely using
+//! the threading utilities in [`crate::thread`].
+
+use std::sync::{
+ atomic::{AtomicUsize, Ordering},
+ Arc,
+};
+
+use crossbeam_channel::{Receiver, Sender};
+
+use super::{Builder, JoinHandle, ThreadIntent};
+
+pub struct Pool {
+ // `_handles` is never read: the field is present
+ // only for its `Drop` impl.
+
+ // The worker threads exit once the channel closes;
+ // make sure to keep `job_sender` above `handles`
+ // so that the channel is actually closed
+ // before we join the worker threads!
+ job_sender: Sender<Job>,
+ _handles: Vec<JoinHandle>,
+ extant_tasks: Arc<AtomicUsize>,
+}
+
+struct Job {
+ requested_intent: ThreadIntent,
+ f: Box<dyn FnOnce() + Send + 'static>,
+}
+
+impl Pool {
+ pub fn new(threads: usize) -> Pool {
+ const STACK_SIZE: usize = 8 * 1024 * 1024;
+ const INITIAL_INTENT: ThreadIntent = ThreadIntent::Worker;
+
+ let (job_sender, job_receiver) = crossbeam_channel::unbounded();
+ let extant_tasks = Arc::new(AtomicUsize::new(0));
+
+ let mut handles = Vec::with_capacity(threads);
+ for _ in 0..threads {
+ let handle = Builder::new(INITIAL_INTENT)
+ .stack_size(STACK_SIZE)
+ .name("Worker".into())
+ .spawn({
+ let extant_tasks = Arc::clone(&extant_tasks);
+ let job_receiver: Receiver<Job> = job_receiver.clone();
+ move || {
+ let mut current_intent = INITIAL_INTENT;
+ for job in job_receiver {
+ if job.requested_intent != current_intent {
+ job.requested_intent.apply_to_current_thread();
+ current_intent = job.requested_intent;
+ }
+ extant_tasks.fetch_add(1, Ordering::SeqCst);
+ (job.f)();
+ extant_tasks.fetch_sub(1, Ordering::SeqCst);
+ }
+ }
+ })
+ .expect("failed to spawn thread");
+
+ handles.push(handle);
+ }
+
+ Pool { _handles: handles, extant_tasks, job_sender }
+ }
+
+ pub fn spawn<F>(&self, intent: ThreadIntent, f: F)
+ where
+ F: FnOnce() + Send + 'static,
+ {
+ let f = Box::new(move || {
+ if cfg!(debug_assertions) {
+ intent.assert_is_used_on_current_thread();
+ }
+ f()
+ });
+
+ let job = Job { requested_intent: intent, f };
+ self.job_sender.send(job).unwrap();
+ }
+
+ pub fn len(&self) -> usize {
+ self.extant_tasks.load(Ordering::SeqCst)
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
index 305cf2d39..fb38d25ab 100644
--- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
@@ -16,12 +16,14 @@ doctest = false
cov-mark = "2.0.0-pre.1"
either = "1.7.0"
itertools = "0.10.5"
-rowan = "0.15.10"
-rustc_lexer = { version = "727.0.0", package = "rustc-ap-rustc_lexer" }
+rowan = "0.15.11"
rustc-hash = "1.1.0"
once_cell = "1.17.0"
indexmap = "1.9.1"
-smol_str = "0.1.23"
+smol_str.workspace = true
+triomphe.workspace = true
+
+rustc_lexer.workspace = true
parser.workspace = true
profile.workspace = true
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 548b5ba8b..b096c9974 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -51,7 +51,9 @@ TypeArg =
Type
AssocTypeArg =
- NameRef GenericArgList? (':' TypeBoundList | ('=' Type | ConstArg))
+ NameRef
+ (GenericArgList | ParamList RetType?)?
+ (':' TypeBoundList | ('=' Type | ConstArg))
LifetimeArg =
Lifetime
@@ -563,7 +565,7 @@ RefType =
'&' Lifetime? 'mut'? Type
ArrayType =
- '[' Type ';' Expr ']'
+ '[' Type ';' ConstArg ']'
SliceType =
'[' Type ']'
@@ -581,7 +583,7 @@ ImplTraitType =
'impl' TypeBoundList
DynTraitType =
- 'dyn' TypeBoundList
+ 'dyn'? TypeBoundList
TypeBoundList =
bounds:(TypeBound ('+' TypeBound)* '+'?)
@@ -613,7 +615,7 @@ Pat =
| ConstBlockPat
LiteralPat =
- Literal
+ '-'? Literal
IdentPat =
Attr* 'ref'? 'mut'? Name ('@' Pat)?
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
index a493c92e7..b3ea6ca8d 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
@@ -236,6 +236,21 @@ impl ast::GenericParamList {
}
}
+ /// Removes the existing generic param
+ pub fn remove_generic_param(&self, generic_param: ast::GenericParam) {
+ if let Some(previous) = generic_param.syntax().prev_sibling() {
+ if let Some(next_token) = previous.next_sibling_or_token() {
+ ted::remove_all(next_token..=generic_param.syntax().clone().into());
+ }
+ } else if let Some(next) = generic_param.syntax().next_sibling() {
+ if let Some(next_token) = next.prev_sibling_or_token() {
+ ted::remove_all(generic_param.syntax().clone().into()..=next_token);
+ }
+ } else {
+ ted::remove(generic_param.syntax());
+ }
+ }
+
/// Constructs a matching [`ast::GenericArgList`]
pub fn to_generic_args(&self) -> ast::GenericArgList {
let args = self.generic_params().filter_map(|param| match param {
@@ -465,6 +480,8 @@ impl ast::Impl {
}
impl ast::AssocItemList {
+ /// Attention! This function does align the first line of `item` with respect to `self`,
+ /// but it does _not_ change indentation of other lines (if any).
pub fn add_item(&self, item: ast::AssocItem) {
let (indent, position, whitespace) = match self.assoc_items().last() {
Some(last_item) => (
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
index c43d0830b..36980b146 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs
@@ -288,6 +288,7 @@ impl ast::ArrayExpr {
pub enum LiteralKind {
String(ast::String),
ByteString(ast::ByteString),
+ CString(ast::CString),
IntNumber(ast::IntNumber),
FloatNumber(ast::FloatNumber),
Char(ast::Char),
@@ -319,6 +320,9 @@ impl ast::Literal {
if let Some(t) = ast::ByteString::cast(token.clone()) {
return LiteralKind::ByteString(t);
}
+ if let Some(t) = ast::CString::cast(token.clone()) {
+ return LiteralKind::CString(t);
+ }
if let Some(t) = ast::Char::cast(token.clone()) {
return LiteralKind::Char(t);
}
@@ -366,8 +370,7 @@ impl ast::BlockExpr {
match parent.kind() {
FOR_EXPR | IF_EXPR => parent
.children()
- .filter(|it| ast::Expr::can_cast(it.kind()))
- .next()
+ .find(|it| ast::Expr::can_cast(it.kind()))
.map_or(true, |it| it == *self.syntax()),
LET_ELSE | FN | WHILE_EXPR | LOOP_EXPR | CONST_BLOCK_PAT => false,
_ => true,
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index fe3248453..e520801ea 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -121,6 +121,8 @@ impl ast::HasTypeBounds for AssocTypeArg {}
impl AssocTypeArg {
pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
pub fn generic_arg_list(&self) -> Option<GenericArgList> { support::child(&self.syntax) }
+ pub fn param_list(&self) -> Option<ParamList> { support::child(&self.syntax) }
+ pub fn ret_type(&self) -> Option<RetType> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
pub fn const_arg(&self) -> Option<ConstArg> { support::child(&self.syntax) }
@@ -1205,7 +1207,7 @@ impl ArrayType {
pub fn l_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['[']) }
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
- pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+ pub fn const_arg(&self) -> Option<ConstArg> { support::child(&self.syntax) }
pub fn r_brack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![']']) }
}
@@ -1375,6 +1377,7 @@ pub struct LiteralPat {
pub(crate) syntax: SyntaxNode,
}
impl LiteralPat {
+ pub fn minus_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![-]) }
pub fn literal(&self) -> Option<Literal> { support::child(&self.syntax) }
}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
index a3209c5ab..f5863e9ef 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs
@@ -91,6 +91,27 @@ impl AstToken for ByteString {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct CString {
+ pub(crate) syntax: SyntaxToken,
+}
+impl std::fmt::Display for CString {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.syntax, f)
+ }
+}
+impl AstToken for CString {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == C_STRING }
+ fn cast(syntax: SyntaxToken) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxToken { &self.syntax }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IntNumber {
pub(crate) syntax: SyntaxToken,
}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index 5aebe4cd9..3c2b7e56b 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -158,34 +158,148 @@ fn ty_from_text(text: &str) -> ast::Type {
ast_from_text(&format!("type _T = {text};"))
}
+pub fn ty_alias(
+ ident: &str,
+ generic_param_list: Option<ast::GenericParamList>,
+ type_param_bounds: Option<ast::TypeParam>,
+ where_clause: Option<ast::WhereClause>,
+ assignment: Option<(ast::Type, Option<ast::WhereClause>)>,
+) -> ast::TypeAlias {
+ let mut s = String::new();
+ s.push_str(&format!("type {}", ident));
+
+ if let Some(list) = generic_param_list {
+ s.push_str(&list.to_string());
+ }
+
+ if let Some(list) = type_param_bounds {
+ s.push_str(&format!(" : {}", &list));
+ }
+
+ if let Some(cl) = where_clause {
+ s.push_str(&format!(" {}", &cl.to_string()));
+ }
+
+ if let Some(exp) = assignment {
+ if let Some(cl) = exp.1 {
+ s.push_str(&format!(" = {} {}", &exp.0.to_string(), &cl.to_string()));
+ } else {
+ s.push_str(&format!(" = {}", &exp.0.to_string()));
+ }
+ }
+
+ s.push(';');
+ ast_from_text(&s)
+}
+
pub fn assoc_item_list() -> ast::AssocItemList {
ast_from_text("impl C for D {}")
}
-// FIXME: `ty_params` should be `ast::GenericArgList`
+fn merge_gen_params(
+ ps: Option<ast::GenericParamList>,
+ bs: Option<ast::GenericParamList>,
+) -> Option<ast::GenericParamList> {
+ match (ps, bs) {
+ (None, None) => None,
+ (None, Some(bs)) => Some(bs),
+ (Some(ps), None) => Some(ps),
+ (Some(ps), Some(bs)) => {
+ for b in bs.generic_params() {
+ ps.add_generic_param(b);
+ }
+ Some(ps)
+ }
+ }
+}
+
pub fn impl_(
- ty: ast::Path,
- params: Option<ast::GenericParamList>,
- ty_params: Option<ast::GenericParamList>,
+ generic_params: Option<ast::GenericParamList>,
+ generic_args: Option<ast::GenericParamList>,
+ path_type: ast::Type,
+ where_clause: Option<ast::WhereClause>,
+ body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>,
) -> ast::Impl {
- let params = match params {
- Some(params) => params.to_string(),
- None => String::new(),
+ let (gen_params, tr_gen_args) = match (generic_params, generic_args) {
+ (None, None) => (String::new(), String::new()),
+ (None, Some(args)) => (String::new(), args.to_generic_args().to_string()),
+ (Some(params), None) => (params.to_string(), params.to_generic_args().to_string()),
+ (Some(params), Some(args)) => match merge_gen_params(Some(params.clone()), Some(args)) {
+ Some(merged) => (params.to_string(), merged.to_generic_args().to_string()),
+ None => (params.to_string(), String::new()),
+ },
};
- let ty_params = match ty_params {
- Some(params) => params.to_string(),
+
+ let where_clause = match where_clause {
+ Some(pr) => pr.to_string(),
+ None => " ".to_string(),
+ };
+
+ let body = match body {
+ Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""),
None => String::new(),
};
- ast_from_text(&format!("impl{params} {ty}{ty_params} {{}}"))
+
+ ast_from_text(&format!("impl{gen_params} {path_type}{tr_gen_args}{where_clause}{{{}}}", body))
}
+// FIXME : We must make *_gen_args' type ast::GenericArgList but in order to do so we must implement in `edit_in_place.rs`
+// `add_generic_arg()` just like `add_generic_param()`
+// is implemented for `ast::GenericParamList`
pub fn impl_trait(
- trait_: ast::Path,
- ty: ast::Path,
- ty_params: Option<ast::GenericParamList>,
+ is_unsafe: bool,
+ trait_gen_params: Option<ast::GenericParamList>,
+ trait_gen_args: Option<ast::GenericParamList>,
+ type_gen_params: Option<ast::GenericParamList>,
+ type_gen_args: Option<ast::GenericParamList>,
+ is_negative: bool,
+ path_type: ast::Type,
+ ty: ast::Type,
+ trait_where_clause: Option<ast::WhereClause>,
+ ty_where_clause: Option<ast::WhereClause>,
+ body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>,
) -> ast::Impl {
- let ty_params = ty_params.map_or_else(String::new, |params| params.to_string());
- ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}"))
+ let is_unsafe = if is_unsafe { "unsafe " } else { "" };
+ let ty_gen_args = match merge_gen_params(type_gen_params.clone(), type_gen_args) {
+ Some(pars) => pars.to_generic_args().to_string(),
+ None => String::new(),
+ };
+
+ let tr_gen_args = match merge_gen_params(trait_gen_params.clone(), trait_gen_args) {
+ Some(pars) => pars.to_generic_args().to_string(),
+ None => String::new(),
+ };
+
+ let gen_params = match merge_gen_params(trait_gen_params, type_gen_params) {
+ Some(pars) => pars.to_string(),
+ None => String::new(),
+ };
+
+ let is_negative = if is_negative { "! " } else { "" };
+
+ let where_clause = match (ty_where_clause, trait_where_clause) {
+ (None, None) => " ".to_string(),
+ (None, Some(tr)) => format!("\n{}\n", tr).to_string(),
+ (Some(ty), None) => format!("\n{}\n", ty).to_string(),
+ (Some(ty), Some(tr)) => {
+ let updated = ty.clone_for_update();
+ tr.predicates().for_each(|p| {
+ ty.add_predicate(p);
+ });
+ format!("\n{}\n", updated).to_string()
+ }
+ };
+
+ let body = match body {
+ Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""),
+ None => String::new(),
+ };
+
+ ast_from_text(&format!("{is_unsafe}impl{gen_params} {is_negative}{path_type}{tr_gen_args} for {ty}{ty_gen_args}{where_clause}{{{}}}" , body))
+}
+
+pub fn impl_trait_type(bounds: ast::TypeBoundList) -> ast::ImplTraitType {
+ ast_from_text(&format!("fn f(x: impl {bounds}) {{}}"))
}
pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
@@ -355,7 +469,7 @@ pub fn hacky_block_expr(
format_to!(buf, " {t}\n")
} else if kind == SyntaxKind::WHITESPACE {
let content = t.text().trim_matches(|c| c != '\n');
- if content.len() >= 1 {
+ if !content.is_empty() {
format_to!(buf, "{}", &content[1..])
}
}
@@ -827,6 +941,8 @@ pub fn fn_(
body: ast::BlockExpr,
ret_type: Option<ast::RetType>,
is_async: bool,
+ is_const: bool,
+ is_unsafe: bool,
) -> ast::Fn {
let type_params = match type_params {
Some(type_params) => format!("{type_params}"),
@@ -846,12 +962,13 @@ pub fn fn_(
};
let async_literal = if is_async { "async " } else { "" };
+ let const_literal = if is_const { "const " } else { "" };
+ let unsafe_literal = if is_unsafe { "unsafe " } else { "" };
ast_from_text(&format!(
- "{visibility}{async_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}",
+ "{visibility}{async_literal}{const_literal}{unsafe_literal}fn {fn_name}{type_params}{params} {ret_type}{where_clause}{body}",
))
}
-
pub fn struct_(
visibility: Option<ast::Visibility>,
strukt_name: ast::Name,
@@ -901,7 +1018,7 @@ pub mod tokens {
pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| {
SourceFile::parse(
- "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p)\n;\n\n",
+ "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n",
)
});
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
index 2cd312e7f..090eb89f4 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs
@@ -145,6 +145,10 @@ impl QuoteOffsets {
}
pub trait IsString: AstToken {
+ const RAW_PREFIX: &'static str;
+ fn is_raw(&self) -> bool {
+ self.text().starts_with(Self::RAW_PREFIX)
+ }
fn quote_offsets(&self) -> Option<QuoteOffsets> {
let text = self.text();
let offsets = QuoteOffsets::new(text)?;
@@ -183,20 +187,18 @@ pub trait IsString: AstToken {
cb(text_range + offset, unescaped_char);
});
}
-}
-
-impl IsString for ast::String {}
-
-impl ast::String {
- pub fn is_raw(&self) -> bool {
- self.text().starts_with('r')
- }
- pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> {
+ fn map_range_up(&self, range: TextRange) -> Option<TextRange> {
let contents_range = self.text_range_between_quotes()?;
assert!(TextRange::up_to(contents_range.len()).contains_range(range));
Some(range + contents_range.start())
}
+}
+impl IsString for ast::String {
+ const RAW_PREFIX: &'static str = "r";
+}
+
+impl ast::String {
pub fn value(&self) -> Option<Cow<'_, str>> {
if self.is_raw() {
let text = self.text();
@@ -235,13 +237,11 @@ impl ast::String {
}
}
-impl IsString for ast::ByteString {}
+impl IsString for ast::ByteString {
+ const RAW_PREFIX: &'static str = "br";
+}
impl ast::ByteString {
- pub fn is_raw(&self) -> bool {
- self.text().starts_with("br")
- }
-
pub fn value(&self) -> Option<Cow<'_, [u8]>> {
if self.is_raw() {
let text = self.text();
@@ -280,6 +280,49 @@ impl ast::ByteString {
}
}
+impl IsString for ast::CString {
+ const RAW_PREFIX: &'static str = "cr";
+}
+
+impl ast::CString {
+ pub fn value(&self) -> Option<Cow<'_, str>> {
+ if self.is_raw() {
+ let text = self.text();
+ let text =
+ &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
+ return Some(Cow::Borrowed(text));
+ }
+
+ let text = self.text();
+ let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()];
+
+ let mut buf = String::new();
+ let mut prev_end = 0;
+ let mut has_error = false;
+ unescape_literal(text, Mode::Str, &mut |char_range, unescaped_char| match (
+ unescaped_char,
+ buf.capacity() == 0,
+ ) {
+ (Ok(c), false) => buf.push(c),
+ (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => {
+ prev_end = char_range.end
+ }
+ (Ok(c), true) => {
+ buf.reserve_exact(text.len());
+ buf.push_str(&text[..prev_end]);
+ buf.push(c);
+ }
+ (Err(_), _) => has_error = true,
+ });
+
+ match (has_error, buf.capacity() == 0) {
+ (true, _) => None,
+ (false, true) => Some(Cow::Borrowed(text)),
+ (false, false) => Some(Cow::Owned(buf)),
+ }
+ }
+}
+
impl ast::IntNumber {
pub fn radix(&self) -> Radix {
match self.text().get(..2).unwrap_or_default() {
diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
index 6f57cbad6..efbf87966 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
@@ -43,10 +43,11 @@ pub mod utils;
pub mod ted;
pub mod hacks;
-use std::{marker::PhantomData, sync::Arc};
+use std::marker::PhantomData;
use stdx::format_to;
use text_edit::Indel;
+use triomphe::Arc;
pub use crate::{
ast::{AstNode, AstToken},
diff --git a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
index 701e6232d..45e591609 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs
@@ -39,7 +39,7 @@ fn reparse_token(
let prev_token = root.covering_element(edit.delete).as_token()?.clone();
let prev_token_kind = prev_token.kind();
match prev_token_kind {
- WHITESPACE | COMMENT | IDENT | STRING => {
+ WHITESPACE | COMMENT | IDENT | STRING | BYTE_STRING | C_STRING => {
if prev_token_kind == WHITESPACE || prev_token_kind == COMMENT {
// removing a new line may extends previous token
let deleted_range = edit.delete - prev_token.text_range().start();
@@ -166,8 +166,8 @@ fn merge_errors(
}
res.extend(new_errors.into_iter().map(|new_err| {
// fighting borrow checker with a variable ;)
- let offseted_range = new_err.range() + range_before_reparse.start();
- new_err.with_range(offseted_range)
+ let offsetted_range = new_err.range() + range_before_reparse.start();
+ new_err.with_range(offsetted_range)
}));
res
}
@@ -408,7 +408,7 @@ enum Foo {
#[test]
fn reparse_str_token_with_error_fixed() {
- do_check(r#""unterinated$0$0"#, "\"", 12);
+ do_check(r#""unterminated$0$0"#, "\"", 13);
}
#[test]
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
index ccce71966..c5783b91a 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
@@ -71,7 +71,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
"super", "trait", "true", "try", "type", "unsafe", "use", "where", "while", "yield",
],
contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules", "yeet"],
- literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING"],
+ literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING", "C_STRING"],
tokens: &["ERROR", "IDENT", "WHITESPACE", "LIFETIME_IDENT", "COMMENT", "SHEBANG"],
nodes: &[
"SOURCE_FILE",
@@ -199,6 +199,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
"GENERIC_PARAM",
"LIFETIME_PARAM",
"TYPE_PARAM",
+ "RETURN_TYPE_ARG",
"CONST_PARAM",
"GENERIC_ARG_LIST",
"LIFETIME",
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
index e954b5825..c49c5fa10 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
@@ -535,6 +535,7 @@ impl Field {
"!" => "excl",
"*" => "star",
"&" => "amp",
+ "-" => "minus",
"_" => "underscore",
"." => "dot",
".." => "dotdot",
@@ -572,10 +573,11 @@ impl Field {
fn lower(grammar: &Grammar) -> AstSrc {
let mut res = AstSrc {
- tokens: "Whitespace Comment String ByteString IntNumber FloatNumber Char Byte Ident"
- .split_ascii_whitespace()
- .map(|it| it.to_string())
- .collect::<Vec<_>>(),
+ tokens:
+ "Whitespace Comment String ByteString CString IntNumber FloatNumber Char Byte Ident"
+ .split_ascii_whitespace()
+ .map(|it| it.to_string())
+ .collect::<Vec<_>>(),
..Default::default()
};
diff --git a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs b/src/tools/rust-analyzer/crates/syntax/src/token_text.rs
index 913b24d42..09c080c0c 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/token_text.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/token_text.rs
@@ -3,6 +3,7 @@
use std::{cmp::Ordering, fmt, ops};
use rowan::GreenToken;
+use smol_str::SmolStr;
pub struct TokenText<'a>(pub(crate) Repr<'a>);
@@ -47,6 +48,12 @@ impl From<TokenText<'_>> for String {
}
}
+impl From<TokenText<'_>> for SmolStr {
+ fn from(token_text: TokenText<'_>) -> Self {
+ SmolStr::new(token_text.as_str())
+ }
+}
+
impl PartialEq<&'_ str> for TokenText<'_> {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation.rs b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
index fb2381110..e0ec6a242 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/validation.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/validation.rs
@@ -5,11 +5,11 @@
mod block;
use rowan::Direction;
-use rustc_lexer::unescape::{self, unescape_byte, unescape_char, unescape_literal, Mode};
+use rustc_lexer::unescape::{self, unescape_literal, Mode};
use crate::{
algo,
- ast::{self, HasAttrs, HasVisibility},
+ ast::{self, HasAttrs, HasVisibility, IsString},
match_ast, AstNode, SyntaxError,
SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS},
SyntaxNode, SyntaxToken, TextSize, T,
@@ -44,7 +44,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
errors
}
-fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
+fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> (&'static str, bool) {
use unescape::EscapeError as EE;
#[rustfmt::skip]
@@ -103,12 +103,15 @@ fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str {
EE::UnicodeEscapeInByte => {
"Byte literals must not contain unicode escapes"
}
- EE::NonAsciiCharInByte | EE::NonAsciiCharInByteString => {
+ EE::NonAsciiCharInByte => {
"Byte literals must not contain non-ASCII characters"
}
+ EE::UnskippedWhitespaceWarning => "Whitespace after this escape is not skipped",
+ EE::MultipleSkippedLinesWarning => "Multiple lines are skipped by this escape",
+
};
- err_message
+ (err_message, err.is_fatal())
}
fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
@@ -121,9 +124,13 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
let text = token.text();
// FIXME: lift this lambda refactor to `fn` (https://github.com/rust-lang/rust-analyzer/pull/2834#discussion_r366199205)
- let mut push_err = |prefix_len, (off, err): (usize, unescape::EscapeError)| {
+ let mut push_err = |prefix_len, off, err: unescape::EscapeError| {
let off = token.text_range().start() + TextSize::try_from(off + prefix_len).unwrap();
- acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off));
+ let (message, is_err) = rustc_unescape_error_to_string(err);
+ // FIXME: Emit lexer warnings
+ if is_err {
+ acc.push(SyntaxError::new_at_offset(message, off));
+ }
};
match literal.kind() {
@@ -132,7 +139,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
if let Some(without_quotes) = unquote(text, 1, '"') {
unescape_literal(without_quotes, Mode::Str, &mut |range, char| {
if let Err(err) = char {
- push_err(1, (range.start, err));
+ push_err(1, range.start, err);
}
});
}
@@ -143,20 +150,39 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
if let Some(without_quotes) = unquote(text, 2, '"') {
unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
if let Err(err) = char {
- push_err(2, (range.start, err));
+ push_err(1, range.start, err);
+ }
+ });
+ }
+ }
+ }
+ ast::LiteralKind::CString(s) => {
+ if !s.is_raw() {
+ if let Some(without_quotes) = unquote(text, 2, '"') {
+ unescape_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
+ if let Err(err) = char {
+ push_err(1, range.start, err);
}
});
}
}
}
ast::LiteralKind::Char(_) => {
- if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) {
- push_err(1, e);
+ if let Some(without_quotes) = unquote(text, 1, '\'') {
+ unescape_literal(without_quotes, Mode::Char, &mut |range, char| {
+ if let Err(err) = char {
+ push_err(1, range.start, err);
+ }
+ });
}
}
ast::LiteralKind::Byte(_) => {
- if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) {
- push_err(2, e);
+ if let Some(without_quotes) = unquote(text, 2, '\'') {
+ unescape_literal(without_quotes, Mode::Byte, &mut |range, char| {
+ if let Err(err) = char {
+ push_err(2, range.start, err);
+ }
+ });
}
}
ast::LiteralKind::IntNumber(_)
@@ -175,14 +201,14 @@ pub(crate) fn validate_block_structure(root: &SyntaxNode) {
assert_eq!(
node.parent(),
pair.parent(),
- "\nunpaired curlys:\n{}\n{:#?}\n",
+ "\nunpaired curlies:\n{}\n{:#?}\n",
root.text(),
root,
);
assert!(
node.next_sibling_or_token().is_none()
&& pair.prev_sibling_or_token().is_none(),
- "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
+ "\nfloating curlies at {:?}\nfile:\n{}\nerror:\n{}\n",
node,
root.text(),
node,
diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs
index f977d23c4..13852aa78 100644
--- a/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs
+++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/fuzz-failures/0000.rs
@@ -39,13 +39,13 @@
ast::Root::cast(self.syntax()).unwrap()
}
pub fn syntax(&self) -> SyntaxNodeRef {
- self.root.brroowed()
+ self.root.borrowed()
}
mp_tree(root),
);
assert!(
node.next_sibling().is_none() && pair.prev_sibling().is_none(),
- "\nfloating curlys at {:?}\nfile:\n{}\nerror:\n{}\n",
+ "\nfloating curlies at {:?}\nfile:\n{}\nerror:\n{}\n",
node,
root.text(),
node.text(),
diff --git a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml
index 92b1ef23e..2b5b6f495 100644
--- a/src/tools/rust-analyzer/crates/test-utils/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/test-utils/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
[dependencies]
# Avoid adding deps here, this crate is widely used in tests it should compile fast!
dissimilar = "1.0.4"
-text-size = "1.1.0"
+text-size.workspace = true
rustc-hash = "1.1.0"
stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
index cd1235fa6..602baed37 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
@@ -86,7 +86,14 @@ pub struct MiniCore {
valid_flags: Vec<String>,
}
-impl Fixture {
+pub struct FixtureWithProjectMeta {
+ pub fixture: Vec<Fixture>,
+ pub mini_core: Option<MiniCore>,
+ pub proc_macro_names: Vec<String>,
+ pub toolchain: Option<String>,
+}
+
+impl FixtureWithProjectMeta {
/// Parses text which looks like this:
///
/// ```not_rust
@@ -96,37 +103,41 @@ impl Fixture {
/// //- other meta
/// ```
///
- /// Fixture can also start with a proc_macros and minicore declaration(in that order):
+ /// Fixture can also start with a proc_macros and minicore declaration (in that order):
///
/// ```
+ /// //- toolchain: nightly
/// //- proc_macros: identity
/// //- minicore: sized
/// ```
///
- /// That will include predefined proc macros and a subset of `libcore` into the fixture, see
- /// `minicore.rs` for what's available.
- pub fn parse(ra_fixture: &str) -> (Option<MiniCore>, Vec<String>, Vec<Fixture>) {
+ /// That will set toolchain to nightly and include predefined proc macros and a subset of
+ /// `libcore` into the fixture, see `minicore.rs` for what's available. Note that toolchain
+ /// defaults to stable.
+ pub fn parse(ra_fixture: &str) -> Self {
let fixture = trim_indent(ra_fixture);
let mut fixture = fixture.as_str();
+ let mut toolchain = None;
let mut mini_core = None;
let mut res: Vec<Fixture> = Vec::new();
- let mut test_proc_macros = vec![];
-
- if fixture.starts_with("//- proc_macros:") {
- let first_line = fixture.split_inclusive('\n').next().unwrap();
- test_proc_macros = first_line
- .strip_prefix("//- proc_macros:")
- .unwrap()
- .split(',')
- .map(|it| it.trim().to_string())
- .collect();
- fixture = &fixture[first_line.len()..];
+ let mut proc_macro_names = vec![];
+
+ if let Some(meta) = fixture.strip_prefix("//- toolchain:") {
+ let (meta, remain) = meta.split_once('\n').unwrap();
+ toolchain = Some(meta.trim().to_string());
+ fixture = remain;
+ }
+
+ if let Some(meta) = fixture.strip_prefix("//- proc_macros:") {
+ let (meta, remain) = meta.split_once('\n').unwrap();
+ proc_macro_names = meta.split(',').map(|it| it.trim().to_string()).collect();
+ fixture = remain;
}
- if fixture.starts_with("//- minicore:") {
- let first_line = fixture.split_inclusive('\n').next().unwrap();
- mini_core = Some(MiniCore::parse(first_line));
- fixture = &fixture[first_line.len()..];
+ if let Some(meta) = fixture.strip_prefix("//- minicore:") {
+ let (meta, remain) = meta.split_once('\n').unwrap();
+ mini_core = Some(MiniCore::parse(meta));
+ fixture = remain;
}
let default = if fixture.contains("//-") { None } else { Some("//- /main.rs") };
@@ -142,7 +153,7 @@ impl Fixture {
}
if line.starts_with("//-") {
- let meta = Fixture::parse_meta_line(line);
+ let meta = Self::parse_meta_line(line);
res.push(meta);
} else {
if line.starts_with("// ")
@@ -160,7 +171,7 @@ impl Fixture {
}
}
- (mini_core, test_proc_macros, res)
+ Self { fixture: res, mini_core, proc_macro_names, toolchain }
}
//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
@@ -243,10 +254,19 @@ impl Fixture {
}
impl MiniCore {
+ const RAW_SOURCE: &str = include_str!("./minicore.rs");
+
fn has_flag(&self, flag: &str) -> bool {
self.activated_flags.iter().any(|it| it == flag)
}
+ pub fn from_flags<'a>(flags: impl IntoIterator<Item = &'a str>) -> Self {
+ MiniCore {
+ activated_flags: flags.into_iter().map(|x| x.to_owned()).collect(),
+ valid_flags: Vec::new(),
+ }
+ }
+
#[track_caller]
fn assert_valid_flag(&self, flag: &str) {
if !self.valid_flags.iter().any(|it| it == flag) {
@@ -257,8 +277,7 @@ impl MiniCore {
fn parse(line: &str) -> MiniCore {
let mut res = MiniCore { activated_flags: Vec::new(), valid_flags: Vec::new() };
- let line = line.strip_prefix("//- minicore:").unwrap().trim();
- for entry in line.split(", ") {
+ for entry in line.trim().split(", ") {
if res.has_flag(entry) {
panic!("duplicate minicore flag: {entry:?}");
}
@@ -268,13 +287,21 @@ impl MiniCore {
res
}
+ pub fn available_flags() -> impl Iterator<Item = &'static str> {
+ let lines = MiniCore::RAW_SOURCE.split_inclusive('\n');
+ lines
+ .map_while(|x| x.strip_prefix("//!"))
+ .skip_while(|line| !line.contains("Available flags:"))
+ .skip(1)
+ .map(|x| x.split_once(':').unwrap().0.trim())
+ }
+
/// Strips parts of minicore.rs which are flagged by inactive flags.
///
/// This is probably over-engineered to support flags dependencies.
pub fn source_code(mut self) -> String {
let mut buf = String::new();
- let raw_mini_core = include_str!("./minicore.rs");
- let mut lines = raw_mini_core.split_inclusive('\n');
+ let mut lines = MiniCore::RAW_SOURCE.split_inclusive('\n');
let mut implications = Vec::new();
@@ -360,6 +387,10 @@ impl MiniCore {
}
}
+ if !active_regions.is_empty() {
+ panic!("unclosed regions: {:?} Add an `endregion` comment", active_regions);
+ }
+
for flag in &self.valid_flags {
if !seen_regions.iter().any(|it| it == flag) {
panic!("unused minicore flag: {flag:?}");
@@ -372,7 +403,7 @@ impl MiniCore {
#[test]
#[should_panic]
fn parse_fixture_checks_further_indented_metadata() {
- Fixture::parse(
+ FixtureWithProjectMeta::parse(
r"
//- /lib.rs
mod bar;
@@ -386,15 +417,18 @@ fn parse_fixture_checks_further_indented_metadata() {
#[test]
fn parse_fixture_gets_full_meta() {
- let (mini_core, proc_macros, parsed) = Fixture::parse(
- r#"
+ let FixtureWithProjectMeta { fixture: parsed, mini_core, proc_macro_names, toolchain } =
+ FixtureWithProjectMeta::parse(
+ r#"
+//- toolchain: nightly
//- proc_macros: identity
//- minicore: coerce_unsized
//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b,atom env:OUTDIR=path/to,OTHER=foo
mod m;
"#,
- );
- assert_eq!(proc_macros, vec!["identity".to_string()]);
+ );
+ assert_eq!(toolchain, Some("nightly".to_string()));
+ assert_eq!(proc_macro_names, vec!["identity".to_string()]);
assert_eq!(mini_core.unwrap().activated_flags, vec!["coerce_unsized".to_string()]);
assert_eq!(1, parsed.len());
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs
index a7a52e08e..fd3e68e2d 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/lib.rs
@@ -27,7 +27,7 @@ pub use rustc_hash::FxHashMap;
pub use crate::{
assert_linear::AssertLinear,
- fixture::{Fixture, MiniCore},
+ fixture::{Fixture, FixtureWithProjectMeta, MiniCore},
};
pub const CURSOR_MARKER: &str = "$0";
@@ -95,7 +95,7 @@ fn try_extract_range(text: &str) -> Option<(TextRange, String)> {
Some((TextRange::new(start, end), text))
}
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
pub enum RangeOrOffset {
Range(TextRange),
Offset(TextSize),
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index ca6de4061..266bc2391 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -11,6 +11,8 @@
//! add:
//! as_ref: sized
//! bool_impl: option, fn
+//! builtin_impls:
+//! cell: copy, drop
//! clone: sized
//! coerce_unsized: unsize
//! copy: clone
@@ -21,26 +23,34 @@
//! drop:
//! eq: sized
//! error: fmt
-//! fmt: result
+//! fmt: result, transmute, coerce_unsized
//! fn:
//! from: sized
//! future: pin
//! generator: pin
//! hash:
+//! include:
//! index: sized
//! infallible:
+//! int_impl: size_of, transmute
//! iterator: option
//! iterators: iterator, fn
+//! manually_drop: drop
//! non_zero:
-//! option:
+//! option: panic
//! ord: eq, option
+//! panic: fmt
+//! phantom_data:
//! pin:
+//! pointee:
//! range:
//! result:
//! send: sized
+//! size_of: sized
//! sized:
//! slice:
//! sync: sized
+//! transmute:
//! try: infallible
//! unsize: sized
@@ -106,6 +116,7 @@ pub mod marker {
impl<T: ?Sized> Copy for *const T {}
impl<T: ?Sized> Copy for *mut T {}
impl<T: ?Sized> Copy for &T {}
+ impl Copy for ! {}
}
// endregion:copy
@@ -113,6 +124,11 @@ pub mod marker {
#[lang = "tuple_trait"]
pub trait Tuple {}
// endregion:fn
+
+ // region:phantom_data
+ #[lang = "phantom_data"]
+ pub struct PhantomData<T: ?Sized>;
+ // endregion:phantom_data
}
// region:default
@@ -124,6 +140,27 @@ pub mod default {
#[rustc_builtin_macro(Default, attributes(default))]
pub macro Default($item:item) {}
// endregion:derive
+
+ // region:builtin_impls
+ macro_rules! impl_default {
+ ($v:literal; $($t:ty)*) => {
+ $(
+ impl const Default for $t {
+ fn default() -> Self {
+ $v
+ }
+ }
+ )*
+ }
+ }
+
+ impl_default! {
+ 0; usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128
+ }
+ impl_default! {
+ 0.0; f32 f64
+ }
+ // endregion:builtin_impls
}
// endregion:default
@@ -134,15 +171,100 @@ pub mod hash {
pub trait Hash {
fn hash<H: Hasher>(&self, state: &mut H);
}
+
+ // region:derive
+ #[rustc_builtin_macro]
+ pub macro Hash($item:item) {}
+ // endregion:derive
}
// endregion:hash
+// region:cell
+pub mod cell {
+ use crate::mem;
+
+ #[lang = "unsafe_cell"]
+ pub struct UnsafeCell<T: ?Sized> {
+ value: T,
+ }
+
+ impl<T> UnsafeCell<T> {
+ pub const fn new(value: T) -> UnsafeCell<T> {
+ UnsafeCell { value }
+ }
+
+ pub const fn get(&self) -> *mut T {
+ self as *const UnsafeCell<T> as *const T as *mut T
+ }
+ }
+
+ pub struct Cell<T: ?Sized> {
+ value: UnsafeCell<T>,
+ }
+
+ impl<T> Cell<T> {
+ pub const fn new(value: T) -> Cell<T> {
+ Cell { value: UnsafeCell::new(value) }
+ }
+
+ pub fn set(&self, val: T) {
+ let old = self.replace(val);
+ mem::drop(old);
+ }
+
+ pub fn replace(&self, val: T) -> T {
+ mem::replace(unsafe { &mut *self.value.get() }, val)
+ }
+ }
+
+ impl<T: Copy> Cell<T> {
+ pub fn get(&self) -> T {
+ unsafe { *self.value.get() }
+ }
+ }
+}
+// endregion:cell
+
// region:clone
pub mod clone {
#[lang = "clone"]
pub trait Clone: Sized {
fn clone(&self) -> Self;
}
+
+ impl<T> Clone for &T {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+
+ // region:builtin_impls
+ macro_rules! impl_clone {
+ ($($t:ty)*) => {
+ $(
+ impl const Clone for $t {
+ fn clone(&self) -> Self {
+ *self
+ }
+ }
+ )*
+ }
+ }
+
+ impl_clone! {
+ usize u8 u16 u32 u64 u128
+ isize i8 i16 i32 i64 i128
+ f32 f64
+ bool char
+ }
+
+ impl Clone for ! {
+ fn clone(&self) {
+ *self
+ }
+ }
+ // endregion:builtin_impls
+
// region:derive
#[rustc_builtin_macro]
pub macro Clone($item:item) {}
@@ -181,10 +303,82 @@ pub mod convert {
}
// endregion:as_ref
// region:infallible
- pub enum Infallibe {}
+ pub enum Infallible {}
// endregion:infallible
}
+pub mod mem {
+ // region:drop
+ // region:manually_drop
+ #[lang = "manually_drop"]
+ #[repr(transparent)]
+ pub struct ManuallyDrop<T: ?Sized> {
+ value: T,
+ }
+
+ impl<T> ManuallyDrop<T> {
+ pub const fn new(value: T) -> ManuallyDrop<T> {
+ ManuallyDrop { value }
+ }
+ }
+
+ // region:deref
+ impl<T: ?Sized> crate::ops::Deref for ManuallyDrop<T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ &self.value
+ }
+ }
+ // endregion:deref
+
+ // endregion:manually_drop
+
+ pub fn drop<T>(_x: T) {}
+ pub const fn replace<T>(dest: &mut T, src: T) -> T {
+ unsafe {
+ let result = crate::ptr::read(dest);
+ crate::ptr::write(dest, src);
+ result
+ }
+ }
+ // endregion:drop
+
+ // region:transmute
+ extern "rust-intrinsic" {
+ pub fn transmute<Src, Dst>(src: Src) -> Dst;
+ }
+ // endregion:transmute
+
+ // region:size_of
+ extern "rust-intrinsic" {
+ pub fn size_of<T>() -> usize;
+ }
+ // endregion:size_of
+}
+
+pub mod ptr {
+ // region:drop
+ #[lang = "drop_in_place"]
+ pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ unsafe { drop_in_place(to_drop) }
+ }
+ pub const unsafe fn read<T>(src: *const T) -> T {
+ *src
+ }
+ pub const unsafe fn write<T>(dst: *mut T, src: T) {
+ *dst = src;
+ }
+ // endregion:drop
+
+ // region:pointee
+ #[lang = "pointee_trait"]
+ pub trait Pointee {
+ #[lang = "metadata_type"]
+ type Metadata;
+ }
+ // endregion:pointee
+}
+
pub mod ops {
// region:coerce_unsized
mod unsize {
@@ -309,12 +503,6 @@ pub mod ops {
pub use self::index::{Index, IndexMut};
// endregion:index
- // region:drop
- pub mod mem {
- pub fn drop<T>(_x: T) {}
- }
- // endregion:drop
-
// region:range
mod range {
#[lang = "RangeFull"]
@@ -375,16 +563,82 @@ pub mod ops {
type Output;
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
+
+ mod impls {
+ use crate::marker::Tuple;
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const Fn<A> for &F
+ where
+ F: ~const Fn<A>,
+ {
+ extern "rust-call" fn call(&self, args: A) -> F::Output {
+ (**self).call(args)
+ }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const FnMut<A> for &F
+ where
+ F: ~const Fn<A>,
+ {
+ extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output {
+ (**self).call(args)
+ }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const FnOnce<A> for &F
+ where
+ F: ~const Fn<A>,
+ {
+ type Output = F::Output;
+
+ extern "rust-call" fn call_once(self, args: A) -> F::Output {
+ (*self).call(args)
+ }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const FnMut<A> for &mut F
+ where
+ F: ~const FnMut<A>,
+ {
+ extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output {
+ (*self).call_mut(args)
+ }
+ }
+
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+ impl<A: Tuple, F: ?Sized> const FnOnce<A> for &mut F
+ where
+ F: ~const FnMut<A>,
+ {
+ type Output = F::Output;
+ extern "rust-call" fn call_once(self, args: A) -> F::Output {
+ (*self).call_mut(args)
+ }
+ }
+ }
}
pub use self::function::{Fn, FnMut, FnOnce};
// endregion:fn
// region:try
mod try_ {
+ use super::super::convert::Infallible;
+
pub enum ControlFlow<B, C = ()> {
+ #[lang = "Continue"]
Continue(C),
+ #[lang = "Break"]
Break(B),
}
- pub trait FromResidual<R = Self::Residual> {
+ pub trait FromResidual<R = <Self as Try>::Residual> {
#[lang = "from_residual"]
fn from_residual(residual: R) -> Self;
}
@@ -400,14 +654,80 @@ pub mod ops {
impl<B, C> Try for ControlFlow<B, C> {
type Output = C;
- type Residual = ControlFlow<B, convert::Infallible>;
- fn from_output(output: Self::Output) -> Self {}
- fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {}
+ type Residual = ControlFlow<B, Infallible>;
+ fn from_output(output: Self::Output) -> Self {
+ ControlFlow::Continue(output)
+ }
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ match self {
+ ControlFlow::Continue(x) => ControlFlow::Continue(x),
+ ControlFlow::Break(x) => ControlFlow::Break(ControlFlow::Break(x)),
+ }
+ }
}
impl<B, C> FromResidual for ControlFlow<B, C> {
- fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {}
+ fn from_residual(residual: ControlFlow<B, Infallible>) -> Self {
+ match residual {
+ ControlFlow::Break(b) => ControlFlow::Break(b),
+ ControlFlow::Continue(_) => loop {},
+ }
+ }
}
+ // region:option
+ impl<T> Try for Option<T> {
+ type Output = T;
+ type Residual = Option<Infallible>;
+ fn from_output(output: Self::Output) -> Self {
+ Some(output)
+ }
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ match self {
+ Some(x) => ControlFlow::Continue(x),
+ None => ControlFlow::Break(None),
+ }
+ }
+ }
+
+ impl<T> FromResidual for Option<T> {
+ fn from_residual(x: Option<Infallible>) -> Self {
+ match x {
+ None => None,
+ Some(_) => loop {},
+ }
+ }
+ }
+ // endregion:option
+ // region:result
+ // region:from
+ use super::super::convert::From;
+
+ impl<T, E> Try for Result<T, E> {
+ type Output = T;
+ type Residual = Result<Infallible, E>;
+
+ fn from_output(output: Self::Output) -> Self {
+ Ok(output)
+ }
+
+ fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
+ match self {
+ Ok(v) => ControlFlow::Continue(v),
+ Err(e) => ControlFlow::Break(Err(e)),
+ }
+ }
+ }
+
+ impl<T, E, F: From<E>> FromResidual<Result<Infallible, E>> for Result<T, F> {
+ fn from_residual(residual: Result<Infallible, E>) -> Self {
+ match residual {
+ Err(e) => Err(From::from(e)),
+ Ok(_) => loop {},
+ }
+ }
+ }
+ // endregion:from
+ // endregion:result
}
pub use self::try_::{ControlFlow, FromResidual, Try};
// endregion:try
@@ -424,6 +744,19 @@ pub mod ops {
pub trait AddAssign<Rhs = Self> {
fn add_assign(&mut self, rhs: Rhs);
}
+
+ // region:builtin_impls
+ macro_rules! add_impl {
+ ($($t:ty)*) => ($(
+ impl const Add for $t {
+ type Output = $t;
+ fn add(self, other: $t) -> $t { self + other }
+ }
+ )*)
+ }
+
+ add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }
+ // endregion:builtin_impls
// endregion:add
// region:generator
@@ -499,12 +832,111 @@ pub mod fmt {
pub struct Error;
pub type Result = Result<(), Error>;
pub struct Formatter<'a>;
+ pub struct DebugTuple;
+ pub struct DebugStruct;
+ impl Formatter<'_> {
+ pub fn debug_tuple(&mut self, name: &str) -> DebugTuple {
+ DebugTuple
+ }
+
+ pub fn debug_struct(&mut self, name: &str) -> DebugStruct {
+ DebugStruct
+ }
+ }
+
+ impl DebugTuple {
+ pub fn field(&mut self, value: &dyn Debug) -> &mut Self {
+ self
+ }
+
+ pub fn finish(&mut self) -> Result {
+ Ok(())
+ }
+ }
+
+ impl DebugStruct {
+ pub fn field(&mut self, name: &str, value: &dyn Debug) -> &mut Self {
+ self
+ }
+
+ pub fn finish(&mut self) -> Result {
+ Ok(())
+ }
+ }
+
pub trait Debug {
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
}
pub trait Display {
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
}
+
+ extern "C" {
+ type Opaque;
+ }
+
+ #[lang = "format_argument"]
+ pub struct Argument<'a> {
+ value: &'a Opaque,
+ formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
+ }
+
+ impl<'a> Argument<'a> {
+ pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
+ use crate::mem::transmute;
+ unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
+ }
+ }
+
+ #[lang = "format_arguments"]
+ pub struct Arguments<'a> {
+ pieces: &'a [&'static str],
+ args: &'a [Argument<'a>],
+ }
+
+ impl<'a> Arguments<'a> {
+ pub const fn new_v1(pieces: &'a [&'static str], args: &'a [Argument<'a>]) -> Arguments<'a> {
+ Arguments { pieces, args }
+ }
+ }
+
+ // region:derive
+ #[rustc_builtin_macro]
+ pub macro Debug($item:item) {}
+ // endregion:derive
+
+ // region:builtin_impls
+ macro_rules! impl_debug {
+ ($($t:ty)*) => {
+ $(
+ impl const Debug for $t {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ Ok(())
+ }
+ }
+ )*
+ }
+ }
+
+ impl_debug! {
+ usize u8 u16 u32 u64 u128
+ isize i8 i16 i32 i64 i128
+ f32 f64
+ bool char
+ }
+
+ impl<T: Debug> Debug for [T] {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ Ok(())
+ }
+ }
+
+ impl<T: Debug + ?Sized> Debug for &T {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ (&**self).fmt(f)
+ }
+ }
+ // endregion:builtin_impls
}
// endregion:fmt
@@ -537,12 +969,30 @@ pub mod option {
}
}
+ pub const fn as_ref(&self) -> Option<&T> {
+ match self {
+ Some(x) => Some(x),
+ None => None,
+ }
+ }
+
pub fn and<U>(self, optb: Option<U>) -> Option<U> {
loop {}
}
pub fn unwrap_or(self, default: T) -> T {
- loop {}
+ match self {
+ Some(val) => val,
+ None => default,
+ }
}
+ // region:result
+ pub const fn ok_or<E>(self, err: E) -> Result<T, E> {
+ match self {
+ Some(v) => Ok(v),
+ None => Err(err),
+ }
+ }
+ // endregion:result
// region:fn
pub fn and_then<U, F>(self, f: F) -> Option<U>
where
@@ -713,8 +1163,6 @@ pub mod iter {
mod traits {
mod iterator {
- use super::super::Take;
-
pub trait Iterator {
type Item;
#[lang = "next"]
@@ -764,12 +1212,19 @@ pub mod iter {
self
}
}
- pub struct IntoIter<T, const N: usize>([T; N]);
+ struct IndexRange {
+ start: usize,
+ end: usize,
+ }
+ pub struct IntoIter<T, const N: usize> {
+ data: [T; N],
+ range: IndexRange,
+ }
impl<T, const N: usize> IntoIterator for [T; N] {
type Item = T;
type IntoIter = IntoIter<T, N>;
fn into_iter(self) -> I {
- IntoIter(self)
+ IntoIter { data: self, range: IndexRange { start: 0, end: loop {} } }
}
}
impl<T, const N: usize> Iterator for IntoIter<T, N> {
@@ -785,16 +1240,64 @@ pub mod iter {
}
// endregion:iterator
-// region:derive
+// region:panic
+mod panic {
+ pub macro panic_2021 {
+ ($($t:tt)+) => (
+ $crate::panicking::panic_fmt($crate::const_format_args!($($t)+))
+ ),
+ }
+}
+
+mod panicking {
+ #[lang = "panic_fmt"]
+ pub const fn panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! {
+ loop {}
+ }
+}
+// endregion:panic
+
mod macros {
+ // region:panic
+ #[macro_export]
+ #[rustc_builtin_macro(std_panic)]
+ macro_rules! panic {
+ ($($arg:tt)*) => {
+ /* compiler built-in */
+ };
+ }
+
+ pub(crate) use panic;
+ // endregion:panic
+
+ // region:fmt
+ #[macro_export]
+ #[rustc_builtin_macro]
+ macro_rules! const_format_args {
+ ($fmt:expr) => {{ /* compiler built-in */ }};
+ ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
+ }
+
+ pub(crate) use const_format_args;
+ // endregion:fmt
+
+ // region:derive
pub(crate) mod builtin {
#[rustc_builtin_macro]
pub macro derive($item:item) {
/* compiler built-in */
}
}
+ // endregion:derive
+
+ // region:include
+ #[rustc_builtin_macro]
+ #[macro_export]
+ macro_rules! include {
+ ($file:expr $(,)?) => {{ /* compiler built-in */ }};
+ }
+ // endregion:include
}
-// endregion:derive
// region:non_zero
pub mod num {
@@ -818,6 +1321,25 @@ impl bool {
}
// endregion:bool_impl
+// region:int_impl
+macro_rules! impl_int {
+ ($($t:ty)*) => {
+ $(
+ impl $t {
+ pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
+ unsafe { mem::transmute(bytes) }
+ }
+ }
+ )*
+ }
+}
+
+impl_int! {
+ usize u8 u16 u32 u64 u128
+ isize i8 i16 i32 i64 i128
+}
+// endregion:int_impl
+
// region:error
pub mod error {
#[rustc_has_incoherent_inherent_impls]
@@ -848,6 +1370,7 @@ pub mod prelude {
ops::Drop, // :drop
ops::{Fn, FnMut, FnOnce}, // :fn
option::Option::{self, None, Some}, // :option
+ panic, // :panic
result::Result::{self, Err, Ok}, // :result
};
}
diff --git a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml
index 337cd2347..76d0ca5cc 100644
--- a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml
@@ -13,4 +13,4 @@ doctest = false
[dependencies]
itertools = "0.10.5"
-text-size = "1.1.0"
+text-size.workspace = true
diff --git a/src/tools/rust-analyzer/crates/text-edit/src/lib.rs b/src/tools/rust-analyzer/crates/text-edit/src/lib.rs
index 9bb4271b6..4705d1818 100644
--- a/src/tools/rust-analyzer/crates/text-edit/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/text-edit/src/lib.rs
@@ -176,6 +176,7 @@ impl TextEditBuilder {
pub fn finish(self) -> TextEdit {
let mut indels = self.indels;
assert_disjoint_or_equal(&mut indels);
+ indels = coalesce_indels(indels);
TextEdit { indels }
}
pub fn invalidates_offset(&self, offset: TextSize) -> bool {
@@ -205,6 +206,21 @@ where
indels.clone().zip(indels.skip(1)).all(|(l, r)| l.delete.end() <= r.delete.start() || l == r)
}
+fn coalesce_indels(indels: Vec<Indel>) -> Vec<Indel> {
+ indels
+ .into_iter()
+ .coalesce(|mut a, b| {
+ if a.delete.end() == b.delete.start() {
+ a.insert.push_str(&b.insert);
+ a.delete = TextRange::new(a.delete.start(), b.delete.end());
+ Ok(a)
+ } else {
+ Err((a, b))
+ }
+ })
+ .collect_vec()
+}
+
#[cfg(test)]
mod tests {
use super::{TextEdit, TextEditBuilder, TextRange};
@@ -261,4 +277,40 @@ mod tests {
let edit2 = TextEdit::delete(range(9, 13));
assert!(edit1.union(edit2).is_err());
}
+
+ #[test]
+ fn test_coalesce_disjoint() {
+ let mut builder = TextEditBuilder::default();
+ builder.replace(range(1, 3), "aa".into());
+ builder.replace(range(5, 7), "bb".into());
+ let edit = builder.finish();
+
+ assert_eq!(edit.indels.len(), 2);
+ }
+
+ #[test]
+ fn test_coalesce_adjacent() {
+ let mut builder = TextEditBuilder::default();
+ builder.replace(range(1, 3), "aa".into());
+ builder.replace(range(3, 5), "bb".into());
+
+ let edit = builder.finish();
+ assert_eq!(edit.indels.len(), 1);
+ assert_eq!(edit.indels[0].insert, "aabb");
+ assert_eq!(edit.indels[0].delete, range(1, 5));
+ }
+
+ #[test]
+ fn test_coalesce_adjacent_series() {
+ let mut builder = TextEditBuilder::default();
+ builder.replace(range(1, 3), "au".into());
+ builder.replace(range(3, 5), "www".into());
+ builder.replace(range(5, 8), "".into());
+ builder.replace(range(8, 9), "ub".into());
+
+ let edit = builder.finish();
+ assert_eq!(edit.indels.len(), 1);
+ assert_eq!(edit.indels[0].insert, "auwwwub");
+ assert_eq!(edit.indels[0].delete, range(1, 9));
+ }
}
diff --git a/src/tools/rust-analyzer/crates/tt/Cargo.toml b/src/tools/rust-analyzer/crates/tt/Cargo.toml
index b84693831..a28ee5f1c 100644
--- a/src/tools/rust-analyzer/crates/tt/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/tt/Cargo.toml
@@ -12,6 +12,6 @@ rust-version.workspace = true
doctest = false
[dependencies]
-smol_str = "0.1.23"
+smol_str.workspace = true
stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs
index b7dbc82e1..c2ebf0374 100644
--- a/src/tools/rust-analyzer/crates/tt/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs
@@ -153,6 +153,12 @@ pub struct Ident<Span> {
pub span: Span,
}
+impl<S> Ident<S> {
+ pub fn new(text: impl Into<SmolStr>, span: S) -> Self {
+ Ident { text: text.into(), span }
+ }
+}
+
fn print_debug_subtree<Span: fmt::Debug>(
f: &mut fmt::Formatter<'_>,
subtree: &Subtree<Span>,
diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
index e06b98d81..5d61a2272 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
@@ -13,10 +13,10 @@ doctest = false
[dependencies]
tracing = "0.1.35"
-jod-thread = "0.1.2"
walkdir = "2.3.2"
crossbeam-channel = "0.5.5"
notify = "5.0"
+stdx.workspace = true
vfs.workspace = true
paths.workspace = true
diff --git a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs
index c95304e55..abfc51dfe 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/vfs-notify/src/lib.rs
@@ -21,7 +21,7 @@ use walkdir::WalkDir;
pub struct NotifyHandle {
// Relative order of fields below is significant.
sender: Sender<Message>,
- _thread: jod_thread::JoinHandle,
+ _thread: stdx::thread::JoinHandle,
}
#[derive(Debug)]
@@ -34,7 +34,7 @@ impl loader::Handle for NotifyHandle {
fn spawn(sender: loader::Sender) -> NotifyHandle {
let actor = NotifyActor::new(sender);
let (sender, receiver) = unbounded::<Message>();
- let thread = jod_thread::Builder::new()
+ let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
.name("VfsLoader".to_owned())
.spawn(move || actor.run(receiver))
.expect("failed to spawn thread");
diff --git a/src/tools/rust-analyzer/crates/vfs/Cargo.toml b/src/tools/rust-analyzer/crates/vfs/Cargo.toml
index 802a30006..3ae3dc83c 100644
--- a/src/tools/rust-analyzer/crates/vfs/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs/Cargo.toml
@@ -15,6 +15,7 @@ doctest = false
rustc-hash = "1.1.0"
fst = "0.4.7"
indexmap = "1.9.1"
+nohash-hasher.workspace = true
paths.workspace = true
stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs
index 700aebe0b..0392ef3ce 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs
@@ -5,8 +5,8 @@
use std::fmt;
use fst::{IntoStreamer, Streamer};
+use nohash_hasher::IntMap;
use rustc_hash::FxHashMap;
-use stdx::hash::NoHashHashMap;
use crate::{AnchoredPath, FileId, Vfs, VfsPath};
@@ -14,7 +14,7 @@ use crate::{AnchoredPath, FileId, Vfs, VfsPath};
#[derive(Default, Clone, Eq, PartialEq)]
pub struct FileSet {
files: FxHashMap<VfsPath, FileId>,
- paths: NoHashHashMap<FileId, VfsPath>,
+ paths: IntMap<FileId, VfsPath>,
}
impl FileSet {
diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
index 14972d290..fe3dfe619 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
@@ -62,7 +62,8 @@ pub use paths::{AbsPath, AbsPathBuf};
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct FileId(pub u32);
-impl stdx::hash::NoHashHashable for FileId {}
+/// safe because `FileId` is a newtype of `u32`
+impl nohash_hasher::IsEnabled for FileId {}
/// Storage for all files read by rust-analyzer.
///
@@ -108,13 +109,6 @@ pub enum ChangeKind {
}
impl Vfs {
- /// Amount of files currently stored.
- ///
- /// Note that this includes deleted files.
- pub fn len(&self) -> usize {
- self.data.len()
- }
-
/// Id of the given path if it exists in the `Vfs` and is not deleted.
pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
self.interner.get(path).filter(|&it| self.get(it).is_some())
@@ -139,6 +133,11 @@ impl Vfs {
self.get(file_id).as_deref().unwrap()
}
+ /// Returns the overall memory usage for the stored files.
+ pub fn memory_usage(&self) -> usize {
+ self.data.iter().flatten().map(|d| d.capacity()).sum()
+ }
+
/// Returns an iterator over the stored ids and their corresponding paths.
///
/// This will skip deleted files.
@@ -158,16 +157,18 @@ impl Vfs {
///
/// If the path does not currently exists in the `Vfs`, allocates a new
/// [`FileId`] for it.
- pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) -> bool {
+ pub fn set_file_contents(&mut self, path: VfsPath, mut contents: Option<Vec<u8>>) -> bool {
let file_id = self.alloc_file_id(path);
- let change_kind = match (&self.get(file_id), &contents) {
+ let change_kind = match (self.get(file_id), &contents) {
(None, None) => return false,
(Some(old), Some(new)) if old == new => return false,
(None, Some(_)) => ChangeKind::Create,
(Some(_), None) => ChangeKind::Delete,
(Some(_), Some(_)) => ChangeKind::Modify,
};
-
+ if let Some(contents) = &mut contents {
+ contents.shrink_to_fit();
+ }
*self.get_mut(file_id) = contents;
self.changes.push(ChangedFile { file_id, change_kind });
true
diff --git a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs
index 38501a8ba..d327f2edf 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/vfs_path.rs
@@ -107,10 +107,7 @@ impl VfsPath {
/// Returns `self`'s base name and file extension.
pub fn name_and_extension(&self) -> Option<(&str, Option<&str>)> {
match &self.0 {
- VfsPathRepr::PathBuf(p) => Some((
- p.file_stem()?.to_str()?,
- p.extension().and_then(|extension| extension.to_str()),
- )),
+ VfsPathRepr::PathBuf(p) => p.name_and_extension(),
VfsPathRepr::VirtualPath(p) => p.name_and_extension(),
}
}