Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--.config/nextest.toml2
-rw-r--r--.github/workflows/ci.yaml2
-rw-r--r--.github/workflows/coverage.yaml14
-rw-r--r--.gitignore1
-rw-r--r--.typos.toml1
-rw-r--r--Cargo.lock97
-rw-r--r--Cargo.toml22
-rw-r--r--bench_data/glorious_old_parser2
-rw-r--r--crates/base-db/src/lib.rs2
-rw-r--r--crates/hir-def/src/attrs.rs113
-rw-r--r--crates/hir-def/src/attrs/docs.rs4
-rw-r--r--crates/hir-def/src/db.rs81
-rw-r--r--crates/hir-def/src/expr_store.rs677
-rw-r--r--crates/hir-def/src/expr_store/body.rs27
-rw-r--r--crates/hir-def/src/expr_store/expander.rs24
-rw-r--r--crates/hir-def/src/expr_store/lower.rs564
-rw-r--r--crates/hir-def/src/expr_store/lower/format_args.rs61
-rw-r--r--crates/hir-def/src/expr_store/lower/generics.rs10
-rw-r--r--crates/hir-def/src/expr_store/lower/path.rs2
-rw-r--r--crates/hir-def/src/expr_store/lower/path/tests.rs2
-rw-r--r--crates/hir-def/src/expr_store/pretty.rs58
-rw-r--r--crates/hir-def/src/expr_store/scope.rs89
-rw-r--r--crates/hir-def/src/expr_store/tests/body.rs9
-rw-r--r--crates/hir-def/src/expr_store/tests/signatures.rs14
-rw-r--r--crates/hir-def/src/find_path.rs105
-rw-r--r--crates/hir-def/src/hir.rs32
-rw-r--r--crates/hir-def/src/hir/format_args.rs9
-rw-r--r--crates/hir-def/src/hir/generics.rs5
-rw-r--r--crates/hir-def/src/hir/type_ref.rs74
-rw-r--r--crates/hir-def/src/item_tree.rs42
-rw-r--r--crates/hir-def/src/item_tree/lower.rs21
-rw-r--r--crates/hir-def/src/lang_item.rs188
-rw-r--r--crates/hir-def/src/lib.rs116
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mod.rs18
-rw-r--r--crates/hir-def/src/nameres.rs12
-rw-r--r--crates/hir-def/src/nameres/assoc.rs31
-rw-r--r--crates/hir-def/src/nameres/attr_resolution.rs3
-rw-r--r--crates/hir-def/src/nameres/collector.rs4
-rw-r--r--crates/hir-def/src/nameres/tests/incremental.rs120
-rw-r--r--crates/hir-def/src/resolver.rs4
-rw-r--r--crates/hir-def/src/signatures.rs106
-rw-r--r--crates/hir-def/src/unstable_features.rs96
-rw-r--r--crates/hir-expand/src/attrs.rs4
-rw-r--r--crates/hir-expand/src/builtin/attr_macro.rs2
-rw-r--r--crates/hir-expand/src/builtin/derive_macro.rs261
-rw-r--r--crates/hir-expand/src/builtin/fn_macro.rs10
-rw-r--r--crates/hir-expand/src/builtin/quote.rs6
-rw-r--r--crates/hir-expand/src/cfg_process.rs6
-rw-r--r--crates/hir-expand/src/db.rs159
-rw-r--r--crates/hir-expand/src/declarative.rs20
-rw-r--r--crates/hir-expand/src/eager.rs41
-rw-r--r--crates/hir-expand/src/files.rs50
-rw-r--r--crates/hir-expand/src/fixup.rs29
-rw-r--r--crates/hir-expand/src/hygiene.rs2
-rw-r--r--crates/hir-expand/src/inert_attr_macro.rs1
-rw-r--r--crates/hir-expand/src/lib.rs85
-rw-r--r--crates/hir-expand/src/mod_path.rs5
-rw-r--r--crates/hir-expand/src/name.rs56
-rw-r--r--crates/hir-expand/src/prettify_macro_expansion_.rs2
-rw-r--r--crates/hir-expand/src/span_map.rs58
-rw-r--r--crates/hir-ty/Cargo.toml1
-rw-r--r--crates/hir-ty/src/autoderef.rs28
-rw-r--r--crates/hir-ty/src/builtin_derive.rs24
-rw-r--r--crates/hir-ty/src/consteval.rs321
-rw-r--r--crates/hir-ty/src/consteval/tests.rs7
-rw-r--r--crates/hir-ty/src/consteval/tests/intrinsics.rs185
-rw-r--r--crates/hir-ty/src/db.rs317
-rw-r--r--crates/hir-ty/src/diagnostics/decl_check.rs4
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs164
-rw-r--r--crates/hir-ty/src/diagnostics/match_check.rs8
-rw-r--r--crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs20
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs6
-rw-r--r--crates/hir-ty/src/display.rs453
-rw-r--r--crates/hir-ty/src/drop.rs15
-rw-r--r--crates/hir-ty/src/dyn_compatibility.rs63
-rw-r--r--crates/hir-ty/src/generics.rs381
-rw-r--r--crates/hir-ty/src/infer.rs1120
-rw-r--r--crates/hir-ty/src/infer/autoderef.rs14
-rw-r--r--crates/hir-ty/src/infer/callee.rs168
-rw-r--r--crates/hir-ty/src/infer/cast.rs51
-rw-r--r--crates/hir-ty/src/infer/closure.rs456
-rw-r--r--crates/hir-ty/src/infer/closure/analysis.rs62
-rw-r--r--crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs299
-rw-r--r--crates/hir-ty/src/infer/coerce.rs109
-rw-r--r--crates/hir-ty/src/infer/diagnostics.rs73
-rw-r--r--crates/hir-ty/src/infer/expr.rs1106
-rw-r--r--crates/hir-ty/src/infer/fallback.rs5
-rw-r--r--crates/hir-ty/src/infer/mutability.rs1
-rw-r--r--crates/hir-ty/src/infer/op.rs52
-rw-r--r--crates/hir-ty/src/infer/opaques.rs13
-rw-r--r--crates/hir-ty/src/infer/pat.rs2089
-rw-r--r--crates/hir-ty/src/infer/path.rs129
-rw-r--r--crates/hir-ty/src/infer/place_op.rs51
-rw-r--r--crates/hir-ty/src/infer/unify.rs540
-rw-r--r--crates/hir-ty/src/inhabitedness.rs9
-rw-r--r--crates/hir-ty/src/lang_items.rs68
-rw-r--r--crates/hir-ty/src/layout.rs20
-rw-r--r--crates/hir-ty/src/layout/adt.rs14
-rw-r--r--crates/hir-ty/src/layout/target.rs6
-rw-r--r--crates/hir-ty/src/layout/tests.rs5
-rw-r--r--crates/hir-ty/src/lib.rs428
-rw-r--r--crates/hir-ty/src/lower.rs1357
-rw-r--r--crates/hir-ty/src/lower/diagnostics.rs5
-rw-r--r--crates/hir-ty/src/lower/path.rs250
-rw-r--r--crates/hir-ty/src/method_resolution.rs75
-rw-r--r--crates/hir-ty/src/method_resolution/confirm.rs91
-rw-r--r--crates/hir-ty/src/method_resolution/probe.rs89
-rw-r--r--crates/hir-ty/src/mir.rs16
-rw-r--r--crates/hir-ty/src/mir/borrowck.rs91
-rw-r--r--crates/hir-ty/src/mir/eval.rs311
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs40
-rw-r--r--crates/hir-ty/src/mir/eval/shim/simd.rs9
-rw-r--r--crates/hir-ty/src/mir/lower.rs210
-rw-r--r--crates/hir-ty/src/mir/lower/as_place.rs26
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs26
-rw-r--r--crates/hir-ty/src/mir/lower/tests.rs2
-rw-r--r--crates/hir-ty/src/mir/monomorphization.rs35
-rw-r--r--crates/hir-ty/src/mir/pretty.rs20
-rw-r--r--crates/hir-ty/src/next_solver.rs15
-rw-r--r--crates/hir-ty/src/next_solver/abi.rs44
-rw-r--r--crates/hir-ty/src/next_solver/binder.rs69
-rw-r--r--crates/hir-ty/src/next_solver/consts.rs39
-rw-r--r--crates/hir-ty/src/next_solver/def_id.rs187
-rw-r--r--crates/hir-ty/src/next_solver/format_proof_tree.rs4
-rw-r--r--crates/hir-ty/src/next_solver/fulfill.rs60
-rw-r--r--crates/hir-ty/src/next_solver/generic_arg.rs124
-rw-r--r--crates/hir-ty/src/next_solver/generics.rs136
-rw-r--r--crates/hir-ty/src/next_solver/infer/at.rs35
-rw-r--r--crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs14
-rw-r--r--crates/hir-ty/src/next_solver/infer/canonical/mod.rs25
-rw-r--r--crates/hir-ty/src/next_solver/infer/context.rs50
-rw-r--r--crates/hir-ty/src/next_solver/infer/errors.rs702
-rw-r--r--crates/hir-ty/src/next_solver/infer/mod.rs282
-rw-r--r--crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs51
-rw-r--r--crates/hir-ty/src/next_solver/infer/relate/generalize.rs22
-rw-r--r--crates/hir-ty/src/next_solver/infer/relate/lattice.rs38
-rw-r--r--crates/hir-ty/src/next_solver/infer/select.rs26
-rw-r--r--crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs48
-rw-r--r--crates/hir-ty/src/next_solver/infer/snapshot/mod.rs6
-rw-r--r--crates/hir-ty/src/next_solver/infer/traits.rs58
-rw-r--r--crates/hir-ty/src/next_solver/infer/type_variable.rs93
-rw-r--r--crates/hir-ty/src/next_solver/infer/unify_key.rs63
-rw-r--r--crates/hir-ty/src/next_solver/inspect.rs75
-rw-r--r--crates/hir-ty/src/next_solver/interner.rs944
-rw-r--r--crates/hir-ty/src/next_solver/ir_print.rs10
-rw-r--r--crates/hir-ty/src/next_solver/normalize.rs5
-rw-r--r--crates/hir-ty/src/next_solver/predicate.rs33
-rw-r--r--crates/hir-ty/src/next_solver/region.rs3
-rw-r--r--crates/hir-ty/src/next_solver/solver.rs125
-rw-r--r--crates/hir-ty/src/next_solver/structural_normalize.rs6
-rw-r--r--crates/hir-ty/src/next_solver/ty.rs176
-rw-r--r--crates/hir-ty/src/next_solver/util.rs6
-rw-r--r--crates/hir-ty/src/opaques.rs28
-rw-r--r--crates/hir-ty/src/representability.rs14
-rw-r--r--crates/hir-ty/src/solver_errors.rs90
-rw-r--r--crates/hir-ty/src/specialization.rs26
-rw-r--r--crates/hir-ty/src/tests.rs95
-rw-r--r--crates/hir-ty/src/tests/closure_captures.rs33
-rw-r--r--crates/hir-ty/src/tests/coercion.rs65
-rw-r--r--crates/hir-ty/src/tests/diagnostics.rs4
-rw-r--r--crates/hir-ty/src/tests/incremental.rs41
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs2
-rw-r--r--crates/hir-ty/src/tests/never_type.rs4
-rw-r--r--crates/hir-ty/src/tests/patterns.rs110
-rw-r--r--crates/hir-ty/src/tests/regression.rs14
-rw-r--r--crates/hir-ty/src/tests/regression/new_solver.rs32
-rw-r--r--crates/hir-ty/src/tests/simple.rs201
-rw-r--r--crates/hir-ty/src/tests/traits.rs28
-rw-r--r--crates/hir-ty/src/traits.rs112
-rw-r--r--crates/hir-ty/src/upvars.rs23
-rw-r--r--crates/hir-ty/src/utils.rs22
-rw-r--r--crates/hir-ty/src/variance.rs12
-rw-r--r--crates/hir/src/attrs.rs11
-rw-r--r--crates/hir/src/diagnostics.rs343
-rw-r--r--crates/hir/src/display.rs27
-rw-r--r--crates/hir/src/from_id.rs7
-rw-r--r--crates/hir/src/has_source.rs5
-rw-r--r--crates/hir/src/lib.rs493
-rw-r--r--crates/hir/src/semantics.rs290
-rw-r--r--crates/hir/src/semantics/source_to_def.rs25
-rw-r--r--crates/hir/src/source_analyzer.rs363
-rw-r--r--crates/hir/src/term_search.rs8
-rw-r--r--crates/hir/src/term_search/tactics.rs25
-rw-r--r--crates/ide-assists/src/assist_context.rs16
-rw-r--r--crates/ide-assists/src/handlers/add_braces.rs4
-rw-r--r--crates/ide-assists/src/handlers/add_explicit_dot_deref.rs2
-rw-r--r--crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs2
-rw-r--r--crates/ide-assists/src/handlers/add_explicit_type.rs4
-rw-r--r--crates/ide-assists/src/handlers/add_label_to_loop.rs4
-rw-r--r--crates/ide-assists/src/handlers/add_lifetime_to_type.rs2
-rw-r--r--crates/ide-assists/src/handlers/add_missing_impl_members.rs29
-rw-r--r--crates/ide-assists/src/handlers/add_missing_match_arms.rs20
-rw-r--r--crates/ide-assists/src/handlers/add_return_type.rs4
-rw-r--r--crates/ide-assists/src/handlers/add_turbo_fish.rs2
-rw-r--r--crates/ide-assists/src/handlers/apply_demorgan.rs89
-rw-r--r--crates/ide-assists/src/handlers/auto_import.rs26
-rw-r--r--crates/ide-assists/src/handlers/bind_unused_param.rs2
-rw-r--r--crates/ide-assists/src/handlers/change_visibility.rs4
-rw-r--r--crates/ide-assists/src/handlers/convert_bool_then.rs10
-rw-r--r--crates/ide-assists/src/handlers/convert_bool_to_enum.rs33
-rw-r--r--crates/ide-assists/src/handlers/convert_char_literal.rs2
-rw-r--r--crates/ide-assists/src/handlers/convert_closure_to_fn.rs115
-rw-r--r--crates/ide-assists/src/handlers/convert_comment_block.rs2
-rw-r--r--crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs2
-rw-r--r--crates/ide-assists/src/handlers/convert_for_to_while_let.rs2
-rw-r--r--crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs5
-rw-r--r--crates/ide-assists/src/handlers/convert_integer_literal.rs5
-rw-r--r--crates/ide-assists/src/handlers/convert_into_to_from.rs54
-rw-r--r--crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs6
-rw-r--r--crates/ide-assists/src/handlers/convert_let_else_to_match.rs9
-rw-r--r--crates/ide-assists/src/handlers/convert_match_to_let_else.rs53
-rw-r--r--crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs12
-rw-r--r--crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs2
-rw-r--r--crates/ide-assists/src/handlers/convert_range_for_to_while.rs5
-rw-r--r--crates/ide-assists/src/handlers/convert_to_guarded_return.rs209
-rw-r--r--crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs19
-rw-r--r--crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs10
-rw-r--r--crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs2
-rw-r--r--crates/ide-assists/src/handlers/convert_while_to_loop.rs2
-rw-r--r--crates/ide-assists/src/handlers/destructure_struct_binding.rs26
-rw-r--r--crates/ide-assists/src/handlers/destructure_tuple_binding.rs31
-rw-r--r--crates/ide-assists/src/handlers/desugar_doc_comment.rs2
-rw-r--r--crates/ide-assists/src/handlers/desugar_try_expr.rs2
-rw-r--r--crates/ide-assists/src/handlers/expand_glob_import.rs57
-rw-r--r--crates/ide-assists/src/handlers/expand_rest_pattern.rs12
-rw-r--r--crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs2
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs698
-rw-r--r--crates/ide-assists/src/handlers/extract_module.rs12
-rw-r--r--crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs15
-rw-r--r--crates/ide-assists/src/handlers/extract_type_alias.rs4
-rw-r--r--crates/ide-assists/src/handlers/extract_variable.rs10
-rw-r--r--crates/ide-assists/src/handlers/fix_visibility.rs4
-rw-r--r--crates/ide-assists/src/handlers/flip_binexpr.rs4
-rw-r--r--crates/ide-assists/src/handlers/flip_comma.rs2
-rw-r--r--crates/ide-assists/src/handlers/flip_or_pattern.rs2
-rw-r--r--crates/ide-assists/src/handlers/flip_trait_bound.rs2
-rw-r--r--crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs37
-rw-r--r--crates/ide-assists/src/handlers/generate_constant.rs4
-rw-r--r--crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs2
-rw-r--r--crates/ide-assists/src/handlers/generate_default_from_new.rs7
-rw-r--r--crates/ide-assists/src/handlers/generate_delegate_methods.rs8
-rw-r--r--crates/ide-assists/src/handlers/generate_delegate_trait.rs30
-rw-r--r--crates/ide-assists/src/handlers/generate_deref.rs6
-rw-r--r--crates/ide-assists/src/handlers/generate_derive.rs2
-rw-r--r--crates/ide-assists/src/handlers/generate_documentation_template.rs22
-rw-r--r--crates/ide-assists/src/handlers/generate_enum_is_method.rs5
-rw-r--r--crates/ide-assists/src/handlers/generate_enum_projection_method.rs9
-rw-r--r--crates/ide-assists/src/handlers/generate_enum_variant.rs6
-rw-r--r--crates/ide-assists/src/handlers/generate_fn_type_alias.rs2
-rw-r--r--crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs7
-rw-r--r--crates/ide-assists/src/handlers/generate_function.rs575
-rw-r--r--crates/ide-assists/src/handlers/generate_getter_or_setter.rs18
-rw-r--r--crates/ide-assists/src/handlers/generate_impl.rs10
-rw-r--r--crates/ide-assists/src/handlers/generate_is_empty_from_len.rs7
-rw-r--r--crates/ide-assists/src/handlers/generate_mut_trait_impl.rs5
-rw-r--r--crates/ide-assists/src/handlers/generate_new.rs9
-rw-r--r--crates/ide-assists/src/handlers/generate_single_field_struct_from.rs19
-rw-r--r--crates/ide-assists/src/handlers/generate_trait_from_impl.rs7
-rw-r--r--crates/ide-assists/src/handlers/inline_call.rs336
-rw-r--r--crates/ide-assists/src/handlers/inline_const_as_literal.rs7
-rw-r--r--crates/ide-assists/src/handlers/inline_local_variable.rs3
-rw-r--r--crates/ide-assists/src/handlers/inline_macro.rs33
-rw-r--r--crates/ide-assists/src/handlers/inline_type_alias.rs21
-rw-r--r--crates/ide-assists/src/handlers/into_to_qualified_from.rs2
-rw-r--r--crates/ide-assists/src/handlers/introduce_named_lifetime.rs5
-rw-r--r--crates/ide-assists/src/handlers/introduce_named_type_parameter.rs27
-rw-r--r--crates/ide-assists/src/handlers/invert_if.rs2
-rw-r--r--crates/ide-assists/src/handlers/merge_imports.rs2
-rw-r--r--crates/ide-assists/src/handlers/merge_match_arms.rs8
-rw-r--r--crates/ide-assists/src/handlers/merge_nested_if.rs2
-rw-r--r--crates/ide-assists/src/handlers/move_bounds.rs2
-rw-r--r--crates/ide-assists/src/handlers/move_const_to_impl.rs2
-rw-r--r--crates/ide-assists/src/handlers/move_from_mod_rs.rs2
-rw-r--r--crates/ide-assists/src/handlers/move_guard.rs4
-rw-r--r--crates/ide-assists/src/handlers/move_module_to_file.rs2
-rw-r--r--crates/ide-assists/src/handlers/move_to_mod_rs.rs2
-rw-r--r--crates/ide-assists/src/handlers/normalize_import.rs2
-rw-r--r--crates/ide-assists/src/handlers/number_representation.rs5
-rw-r--r--crates/ide-assists/src/handlers/promote_local_to_const.rs2
-rw-r--r--crates/ide-assists/src/handlers/pull_assignment_up.rs8
-rw-r--r--crates/ide-assists/src/handlers/qualify_method_call.rs2
-rw-r--r--crates/ide-assists/src/handlers/qualify_path.rs6
-rw-r--r--crates/ide-assists/src/handlers/raw_string.rs10
-rw-r--r--crates/ide-assists/src/handlers/remove_dbg.rs2
-rw-r--r--crates/ide-assists/src/handlers/remove_else_branches.rs2
-rw-r--r--crates/ide-assists/src/handlers/remove_mut.rs2
-rw-r--r--crates/ide-assists/src/handlers/remove_parentheses.rs2
-rw-r--r--crates/ide-assists/src/handlers/remove_underscore.rs2
-rw-r--r--crates/ide-assists/src/handlers/remove_unused_imports.rs27
-rw-r--r--crates/ide-assists/src/handlers/remove_unused_param.rs4
-rw-r--r--crates/ide-assists/src/handlers/reorder_fields.rs4
-rw-r--r--crates/ide-assists/src/handlers/reorder_impl_items.rs4
-rw-r--r--crates/ide-assists/src/handlers/replace_arith_op.rs66
-rw-r--r--crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs53
-rw-r--r--crates/ide-assists/src/handlers/replace_if_let_with_match.rs146
-rw-r--r--crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs2
-rw-r--r--crates/ide-assists/src/handlers/replace_let_with_if_let.rs5
-rw-r--r--crates/ide-assists/src/handlers/replace_method_eager_lazy.rs10
-rw-r--r--crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs15
-rw-r--r--crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs27
-rw-r--r--crates/ide-assists/src/handlers/replace_string_with_char.rs10
-rw-r--r--crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs2
-rw-r--r--crates/ide-assists/src/handlers/sort_items.rs4
-rw-r--r--crates/ide-assists/src/handlers/split_import.rs8
-rw-r--r--crates/ide-assists/src/handlers/term_search.rs2
-rw-r--r--crates/ide-assists/src/handlers/toggle_async_sugar.rs4
-rw-r--r--crates/ide-assists/src/handlers/toggle_ignore.rs2
-rw-r--r--crates/ide-assists/src/handlers/toggle_macro_delimiter.rs10
-rw-r--r--crates/ide-assists/src/handlers/unmerge_imports.rs2
-rw-r--r--crates/ide-assists/src/handlers/unmerge_match_arm.rs2
-rw-r--r--crates/ide-assists/src/handlers/unnecessary_async.rs6
-rw-r--r--crates/ide-assists/src/handlers/unqualify_method_call.rs4
-rw-r--r--crates/ide-assists/src/handlers/unwrap_branch.rs (renamed from crates/ide-assists/src/handlers/unwrap_block.rs)461
-rw-r--r--crates/ide-assists/src/handlers/unwrap_return_type.rs2
-rw-r--r--crates/ide-assists/src/handlers/unwrap_tuple.rs2
-rw-r--r--crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs5
-rw-r--r--crates/ide-assists/src/handlers/wrap_return_type.rs4
-rw-r--r--crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs12
-rw-r--r--crates/ide-assists/src/lib.rs7
-rw-r--r--crates/ide-assists/src/tests/generated.rs23
-rw-r--r--crates/ide-assists/src/utils.rs173
-rw-r--r--crates/ide-assists/src/utils/ref_field_expr.rs6
-rw-r--r--crates/ide-completion/src/completions.rs84
-rw-r--r--crates/ide-completion/src/completions/attribute.rs43
-rw-r--r--crates/ide-completion/src/completions/attribute/cfg.rs9
-rw-r--r--crates/ide-completion/src/completions/attribute/derive.rs2
-rw-r--r--crates/ide-completion/src/completions/attribute/diagnostic.rs72
-rw-r--r--crates/ide-completion/src/completions/attribute/feature.rs30
-rw-r--r--crates/ide-completion/src/completions/attribute/lint.rs34
-rw-r--r--crates/ide-completion/src/completions/attribute/macro_use.rs8
-rw-r--r--crates/ide-completion/src/completions/attribute/repr.rs58
-rw-r--r--crates/ide-completion/src/completions/dot.rs22
-rw-r--r--crates/ide-completion/src/completions/env_vars.rs2
-rw-r--r--crates/ide-completion/src/completions/expr.rs10
-rw-r--r--crates/ide-completion/src/completions/extern_abi.rs2
-rw-r--r--crates/ide-completion/src/completions/extern_crate.rs2
-rw-r--r--crates/ide-completion/src/completions/field.rs4
-rw-r--r--crates/ide-completion/src/completions/flyimport.rs42
-rw-r--r--crates/ide-completion/src/completions/fn_param.rs8
-rw-r--r--crates/ide-completion/src/completions/format_string.rs2
-rw-r--r--crates/ide-completion/src/completions/item_list.rs10
-rw-r--r--crates/ide-completion/src/completions/item_list/trait_impl.rs122
-rw-r--r--crates/ide-completion/src/completions/keyword.rs2
-rw-r--r--crates/ide-completion/src/completions/lifetime.rs4
-rw-r--r--crates/ide-completion/src/completions/macro_def.rs2
-rw-r--r--crates/ide-completion/src/completions/mod_.rs2
-rw-r--r--crates/ide-completion/src/completions/pattern.rs4
-rw-r--r--crates/ide-completion/src/completions/postfix.rs296
-rw-r--r--crates/ide-completion/src/completions/postfix/format_like.rs4
-rw-r--r--crates/ide-completion/src/completions/ra_fixture.rs2
-rw-r--r--crates/ide-completion/src/completions/record.rs8
-rw-r--r--crates/ide-completion/src/completions/snippet.rs13
-rw-r--r--crates/ide-completion/src/completions/type.rs4
-rw-r--r--crates/ide-completion/src/completions/use_.rs2
-rw-r--r--crates/ide-completion/src/completions/vis.rs2
-rw-r--r--crates/ide-completion/src/config.rs1
-rw-r--r--crates/ide-completion/src/context.rs20
-rw-r--r--crates/ide-completion/src/context/analysis.rs33
-rw-r--r--crates/ide-completion/src/item.rs48
-rw-r--r--crates/ide-completion/src/lib.rs19
-rw-r--r--crates/ide-completion/src/render.rs360
-rw-r--r--crates/ide-completion/src/render/const_.rs7
-rw-r--r--crates/ide-completion/src/render/function.rs20
-rw-r--r--crates/ide-completion/src/render/literal.rs14
-rw-r--r--crates/ide-completion/src/render/macro_.rs8
-rw-r--r--crates/ide-completion/src/render/pattern.rs8
-rw-r--r--crates/ide-completion/src/render/type_alias.rs6
-rw-r--r--crates/ide-completion/src/render/union_literal.rs2
-rw-r--r--crates/ide-completion/src/render/variant.rs6
-rw-r--r--crates/ide-completion/src/snippet.rs7
-rw-r--r--crates/ide-completion/src/tests.rs1
-rw-r--r--crates/ide-completion/src/tests/attribute.rs24
-rw-r--r--crates/ide-completion/src/tests/expression.rs159
-rw-r--r--crates/ide-completion/src/tests/flyimport.rs14
-rw-r--r--crates/ide-completion/src/tests/item.rs72
-rw-r--r--crates/ide-completion/src/tests/item_list.rs28
-rw-r--r--crates/ide-completion/src/tests/predicate.rs16
-rw-r--r--crates/ide-completion/src/tests/record.rs2
-rw-r--r--crates/ide-completion/src/tests/special.rs22
-rw-r--r--crates/ide-completion/src/tests/type_pos.rs94
-rw-r--r--crates/ide-completion/src/tests/visibility.rs6
-rw-r--r--crates/ide-db/src/active_parameter.rs2
-rw-r--r--crates/ide-db/src/generated/lints.rs627
-rw-r--r--crates/ide-db/src/helpers.rs27
-rw-r--r--crates/ide-db/src/imports/import_assets.rs3
-rw-r--r--crates/ide-db/src/imports/insert_use.rs336
-rw-r--r--crates/ide-db/src/imports/insert_use/tests.rs11
-rw-r--r--crates/ide-db/src/imports/merge_imports.rs5
-rw-r--r--crates/ide-db/src/lib.rs16
-rw-r--r--crates/ide-db/src/path_transform.rs27
-rw-r--r--crates/ide-db/src/prime_caches.rs4
-rw-r--r--crates/ide-db/src/rename.rs149
-rw-r--r--crates/ide-db/src/search.rs10
-rw-r--r--crates/ide-db/src/source_change.rs83
-rw-r--r--crates/ide-db/src/syntax_helpers/suggest_name.rs5
-rw-r--r--crates/ide-db/src/text_edit.rs8
-rw-r--r--crates/ide-db/src/traits.rs24
-rw-r--r--crates/ide-db/src/use_trivial_constructor.rs33
-rw-r--r--crates/ide-diagnostics/src/handlers/await_outside_of_async.rs5
-rw-r--r--crates/ide-diagnostics/src/handlers/bad_rtn.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/duplicate_field.rs123
-rw-r--r--crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs50
-rw-r--r--crates/ide-diagnostics/src/handlers/expected_function.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/field_shorthand.rs20
-rw-r--r--crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs55
-rw-r--r--crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs43
-rw-r--r--crates/ide-diagnostics/src/handlers/inactive_code.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/incoherent_impl.rs5
-rw-r--r--crates/ide-diagnostics/src/handlers/incorrect_case.rs17
-rw-r--r--crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/invalid_cast.rs11
-rw-r--r--crates/ide-diagnostics/src/handlers/invalid_derive_target.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs90
-rw-r--r--crates/ide-diagnostics/src/handlers/json_is_not_rust.rs22
-rw-r--r--crates/ide-diagnostics/src/handlers/macro_error.rs7
-rw-r--r--crates/ide-diagnostics/src/handlers/malformed_derive.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs9
-rw-r--r--crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs114
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_fields.rs59
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_lifetime.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_match_arms.rs20
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_unsafe.rs54
-rw-r--r--crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/mutability_errors.rs8
-rw-r--r--crates/ide-diagnostics/src/handlers/no_such_field.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs46
-rw-r--r--crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs86
-rw-r--r--crates/ide-diagnostics/src/handlers/private_assoc_item.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/private_field.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/remove_trailing_return.rs6
-rw-r--r--crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs44
-rw-r--r--crates/ide-diagnostics/src/handlers/type_must_be_known.rs162
-rw-r--r--crates/ide-diagnostics/src/handlers/typed_hole.rs9
-rw-r--r--crates/ide-diagnostics/src/handlers/undeclared_label.rs10
-rw-r--r--crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/unimplemented_trait.rs72
-rw-r--r--crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs42
-rw-r--r--crates/ide-diagnostics/src/handlers/unlinked_file.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/unreachable_label.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_field.rs12
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_ident.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_import.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_method.rs11
-rw-r--r--crates/ide-diagnostics/src/handlers/unresolved_module.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/unused_must_use.rs132
-rw-r--r--crates/ide-diagnostics/src/handlers/unused_variables.rs2
-rw-r--r--crates/ide-diagnostics/src/lib.rs71
-rw-r--r--crates/ide-diagnostics/src/tests.rs4
-rw-r--r--crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs9
-rw-r--r--crates/ide/src/expand_macro.rs31
-rw-r--r--crates/ide/src/goto_definition.rs81
-rw-r--r--crates/ide/src/highlight_related.rs8
-rw-r--r--crates/ide/src/hover/render.rs14
-rw-r--r--crates/ide/src/hover/tests.rs10
-rw-r--r--crates/ide/src/inlay_hints.rs28
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs2
-rw-r--r--crates/ide/src/inlay_hints/binding_mode.rs14
-rw-r--r--crates/ide/src/inlay_hints/closure_captures.rs85
-rw-r--r--crates/ide/src/inlay_hints/implicit_drop.rs2
-rw-r--r--crates/ide/src/lib.rs9
-rw-r--r--crates/ide/src/navigation_target.rs10
-rw-r--r--crates/ide/src/references.rs3
-rw-r--r--crates/ide/src/rename.rs240
-rw-r--r--crates/ide/src/signature_help.rs3
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs13
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html12
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs12
-rw-r--r--crates/ide/src/view_memory_layout.rs2
-rw-r--r--crates/intern/Cargo.toml1
-rw-r--r--crates/intern/src/symbol.rs32
-rw-r--r--crates/intern/src/symbol/symbols.rs130
-rw-r--r--crates/load-cargo/src/lib.rs4
-rw-r--r--crates/macros/src/lib.rs14
-rw-r--r--crates/mbe/src/expander/transcriber.rs6
-rw-r--r--crates/mbe/src/lib.rs7
-rw-r--r--crates/mbe/src/tests.rs2
-rw-r--r--crates/parser/src/event.rs50
-rw-r--r--crates/parser/src/frontmatter.rs2
-rw-r--r--crates/parser/src/grammar/expressions.rs6
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs25
-rw-r--r--crates/parser/src/grammar/items.rs38
-rw-r--r--crates/parser/src/grammar/patterns.rs21
-rw-r--r--crates/parser/src/lib.rs19
-rw-r--r--crates/parser/src/output.rs8
-rw-r--r--crates/parser/src/parser.rs36
-rw-r--r--crates/parser/src/syntax_kind/generated.rs8
-rw-r--r--crates/parser/test_data/generated/runner.rs8
-rw-r--r--crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rast45
-rw-r--r--crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rs (renamed from crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs)1
-rw-r--r--crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast24
-rw-r--r--crates/parser/test_data/parser/inline/ok/deref_pat.rast36
-rw-r--r--crates/parser/test_data/parser/inline/ok/deref_pat.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/impl_restrictions.rast4
-rw-r--r--crates/parser/test_data/parser/inline/ok/impl_restrictions.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/return_attr.rast34
-rw-r--r--crates/parser/test_data/parser/inline/ok/return_attr.rs3
-rw-r--r--crates/profile/Cargo.toml2
-rw-r--r--crates/project-model/src/build_dependencies.rs2
-rw-r--r--crates/project-model/src/cargo_config_file.rs17
-rw-r--r--crates/query-group-macro/src/lib.rs68
-rw-r--r--crates/query-group-macro/src/queries.rs72
-rw-r--r--crates/query-group-macro/tests/interned.rs50
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs95
-rw-r--r--crates/rust-analyzer/src/cli/highlight.rs5
-rw-r--r--crates/rust-analyzer/src/cli/symbols.rs5
-rw-r--r--crates/rust-analyzer/src/config.rs6
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs2
-rw-r--r--crates/rust-analyzer/src/integrated_benchmarks.rs127
-rw-r--r--crates/rust-analyzer/src/lsp/ext.rs104
-rw-r--r--crates/rust-analyzer/src/lsp/to_proto.rs15
-rw-r--r--crates/rust-analyzer/src/main_loop.rs16
-rw-r--r--crates/rust-analyzer/src/reload.rs2
-rw-r--r--crates/rust-analyzer/tests/slow-tests/flycheck.rs1
-rw-r--r--crates/span/src/hygiene.rs2
-rw-r--r--crates/stdx/Cargo.toml2
-rw-r--r--crates/syntax-bridge/Cargo.toml1
-rw-r--r--crates/syntax-bridge/src/prettify_macro_expansion.rs391
-rw-r--r--crates/syntax/rust.ungram4
-rw-r--r--crates/syntax/src/ast/edit.rs97
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs446
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs62
-rw-r--r--crates/syntax/src/ast/make.rs18
-rw-r--r--crates/syntax/src/ast/node_ext.rs13
-rw-r--r--crates/syntax/src/ast/syntax_factory/constructors.rs200
-rw-r--r--crates/syntax/src/lib.rs4
-rw-r--r--crates/syntax/src/ptr.rs4
-rw-r--r--crates/syntax/src/syntax_editor.rs109
-rw-r--r--crates/syntax/src/syntax_editor/edit_algo.rs5
-rw-r--r--crates/syntax/src/syntax_editor/edits.rs228
-rw-r--r--crates/test-utils/src/minicore.rs198
-rw-r--r--docs/book/src/configuration_generated.md9
-rw-r--r--docs/book/src/contributing/lsp-extensions.md2
-rw-r--r--editors/code/package.json10
-rw-r--r--lib/smol_str/README.md2
-rw-r--r--lib/smol_str/src/lib.rs4
-rw-r--r--rust-version2
-rw-r--r--xtask/src/codegen/grammar/ast_src.rs1
553 files changed, 21860 insertions, 11877 deletions
diff --git a/.config/nextest.toml b/.config/nextest.toml
new file mode 100644
index 0000000000..47e5bf53d0
--- /dev/null
+++ b/.config/nextest.toml
@@ -0,0 +1,2 @@
+[profile.default]
+slow-timeout = { period = "60s", terminate-after = 5 }
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index c27d84fb0b..b9427e1927 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -120,7 +120,7 @@ jobs:
run: cargo codegen --check
- name: Run tests
- run: cargo nextest run --no-fail-fast --hide-progress-bar --status-level fail
+ run: cargo nextest run --no-fail-fast --hide-progress-bar
- name: Install cargo-machete
uses: taiki-e/install-action@cargo-machete
diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml
index 9460c6a3c7..55edbbefba 100644
--- a/.github/workflows/coverage.yaml
+++ b/.github/workflows/coverage.yaml
@@ -12,19 +12,17 @@ env:
jobs:
coverage:
runs-on: ubuntu-latest
+ env:
+ RUSTC_BOOTSTRAP: 1
steps:
- uses: actions/checkout@v6
- name: Install Rust toolchain
run: |
- rustup update --no-self-update nightly
- rustup default nightly
- rustup component add --toolchain nightly rust-src rustc-dev rustfmt
- # We also install a nightly rustfmt, because we use `--file-lines` in
- # a test.
- rustup toolchain install nightly --profile minimal --component rustfmt
-
- rustup toolchain install nightly --component llvm-tools-preview
+ rustup update --no-self-update stable
+ rustup default stable
+ rustup component add --toolchain stable rust-src rustc-dev rustfmt
+ rustup toolchain install stable --component llvm-tools-preview
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
diff --git a/.gitignore b/.gitignore
index 7192e685e2..3beabe39bc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
target/
+vendor/
/dist/
**/*.rs.bk
**/*.rs.pending-snap
diff --git a/.typos.toml b/.typos.toml
index e954b08fb1..873daa3bf3 100644
--- a/.typos.toml
+++ b/.typos.toml
@@ -34,6 +34,7 @@ thir = "thir"
jod = "jod"
tructure = "tructure"
taits = "taits"
+inh = "inh"
[default.extend-identifiers]
anc = "anc"
diff --git a/Cargo.lock b/Cargo.lock
index e6575c28c1..cbbeef0030 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -648,6 +648,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
+name = "foldhash"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+
+[[package]]
name = "form_urlencoded"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -734,7 +740,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"allocator-api2",
"equivalent",
- "foldhash",
+ "foldhash 0.1.5",
]
[[package]]
@@ -744,6 +750,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
[[package]]
+name = "hashbrown"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
+dependencies = [
+ "allocator-api2",
+ "equivalent",
+ "foldhash 0.2.0",
+]
+
+[[package]]
name = "hashlink"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -876,6 +893,7 @@ version = "0.0.0"
dependencies = [
"arrayvec",
"base-db",
+ "bitflags 2.9.4",
"cov-mark",
"either",
"ena",
@@ -1213,6 +1231,7 @@ dependencies = [
name = "intern"
version = "0.0.0"
dependencies = [
+ "arrayvec",
"dashmap",
"hashbrown 0.14.5",
"rayon",
@@ -1231,9 +1250,9 @@ dependencies = [
[[package]]
name = "inventory"
-version = "0.3.21"
+version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e"
+checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b"
dependencies = [
"rustversion",
]
@@ -1924,7 +1943,7 @@ dependencies = [
"libc",
"perf-event",
"tikv-jemalloc-ctl",
- "windows-sys 0.60.2",
+ "windows-sys 0.61.0",
]
[[package]]
@@ -2048,9 +2067,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "ra-ap-rustc_abi"
-version = "0.160.0"
+version = "0.165.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b917ab47d7036977be4c984321af3e0de089229404d68ea9a286f50aa464697"
+checksum = "2f25a779e21ca3bba6795193b16508c8ab159f96ee4b07349893fd272065b525"
dependencies = [
"bitflags 2.9.4",
"ra-ap-rustc_hashes",
@@ -2060,33 +2079,33 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_ast_ir"
-version = "0.160.0"
+version = "0.165.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "021d80bea67458b8c90cc25bfdca6f911ea818a41905e370c1f310cced1dd07e"
+checksum = "0218ca6c7b096466e85a497e6150c39be5b7bc36637fe62c1cd20370a9d9aac7"
[[package]]
name = "ra-ap-rustc_hashes"
-version = "0.160.0"
+version = "0.165.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bb89395306ecfc980d252f77a4038d8b8bb578a25c856b545cbeeb3fde8358e"
+checksum = "d6b410bacf1a7c8038f376fa6283003784d568ac012e35fc0aeefa9a5ab11a2e"
dependencies = [
"rustc-stable-hash",
]
[[package]]
name = "ra-ap-rustc_index"
-version = "0.160.0"
+version = "0.165.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84219d028a1954c4340ddde11adffe93eb83e476e942718fe926f4d99637cbbe"
+checksum = "2271b55e4a5d0cc0cbe9bdf8056c07ac69e32919a48ce66722ed0526d62588c3"
dependencies = [
"ra-ap-rustc_index_macros",
]
[[package]]
name = "ra-ap-rustc_index_macros"
-version = "0.160.0"
+version = "0.165.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3908fdfa258c663d8ee407e6b4a205b0880e323b533c0df7edceafbd54a02fb6"
+checksum = "b6a89e743fb881a1e13544e3395a5ad9ad9280d56384256a121066119abd7af2"
dependencies = [
"proc-macro2",
"quote",
@@ -2095,9 +2114,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_lexer"
-version = "0.160.0"
+version = "0.165.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34b50f19d5856b8e2b36150e89b53a6102ab096e8044e1f55fd6fef977b10d85"
+checksum = "a6d7c9cc05e0e6b72a214a455a106d9b22b0494164d50a657b17bd319534c218"
dependencies = [
"memchr",
"unicode-ident",
@@ -2106,9 +2125,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_next_trait_solver"
-version = "0.160.0"
+version = "0.165.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76f83dcc451bcee8a99e284a583d5b3d82db5a200107a256a40ef132c4988f1b"
+checksum = "cb3017c2f0ace80b8e6068b9c613aa56ed50e0374bf44a891447511f1264e40d"
dependencies = [
"derive-where",
"ra-ap-rustc_index",
@@ -2119,9 +2138,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_parse_format"
-version = "0.160.0"
+version = "0.165.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f31236bdc6cbcae8af42d0b2db2fa8d812a8715b90a2ba5afb1132b37a4d0bbc"
+checksum = "4a737f844bdef8ac5ab54dadf2f34704b4d06beef9236d71080bb34db697220b"
dependencies = [
"ra-ap-rustc_lexer",
"rustc-literal-escaper 0.0.7",
@@ -2129,9 +2148,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_pattern_analysis"
-version = "0.160.0"
+version = "0.165.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fc4edac740e896fba4b3b4d9c423083e3eac49947732561ddfb2377e1f57829"
+checksum = "6de3d4c7d6078cce3c40c55717b8b15002a80b9fa8849faea496a365324861b4"
dependencies = [
"ra-ap-rustc_index",
"rustc-hash 2.1.1",
@@ -2142,9 +2161,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_type_ir"
-version = "0.160.0"
+version = "0.165.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8efa119afc1bcadd821b27aa94332abf79c48ac0a972cb78932f63004ba4cdd9"
+checksum = "8c5d9a4d3e7bee7313599bc6d794037247ac0165f03857379cf4fc3097199e05"
dependencies = [
"arrayvec",
"bitflags 2.9.4",
@@ -2163,9 +2182,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_type_ir_macros"
-version = "0.160.0"
+version = "0.165.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6b1dc03abfabc7179393c282892c73a3f0e4bbd5f0b6c87ff42c2b142e68f57"
+checksum = "024598d1f54272acd83d28c121f8a2e82e216dd7be1e40158b66b2d12fa214c0"
dependencies = [
"proc-macro2",
"quote",
@@ -2355,7 +2374,7 @@ dependencies = [
"vfs",
"vfs-notify",
"walkdir",
- "windows-sys 0.60.2",
+ "windows-sys 0.61.0",
"xflags",
"xshell",
]
@@ -2454,14 +2473,14 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "salsa"
-version = "0.26.0"
+version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f77debccd43ba198e9cee23efd7f10330ff445e46a98a2b107fed9094a1ee676"
+checksum = "4612ff789805e65c87e9b38cb749a293212a615af065bed8a2001086801498c3"
dependencies = [
"boxcar",
"crossbeam-queue",
"crossbeam-utils",
- "hashbrown 0.15.5",
+ "hashbrown 0.17.0",
"hashlink",
"indexmap",
"intrusive-collections",
@@ -2475,19 +2494,20 @@ dependencies = [
"smallvec",
"thin-vec",
"tracing",
+ "typeid",
]
[[package]]
name = "salsa-macro-rules"
-version = "0.26.0"
+version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea07adbf42d91cc076b7daf3b38bc8168c19eb362c665964118a89bc55ef19a5"
+checksum = "58e354cbac6939b9b09cd9c11fb419a53e64b4a0f755d929f56a09f4cc752e41"
[[package]]
name = "salsa-macros"
-version = "0.26.0"
+version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d16d4d8b66451b9c75ddf740b7fc8399bc7b8ba33e854a5d7526d18708f67b05"
+checksum = "3067861075c2b80608f84ad49fb88f2c7610b94cdf8b4201e79ddee87f8980c8"
dependencies = [
"proc-macro2",
"quote",
@@ -2694,7 +2714,7 @@ dependencies = [
"libc",
"miow",
"tracing",
- "windows-sys 0.60.2",
+ "windows-sys 0.61.0",
]
[[package]]
@@ -2744,6 +2764,7 @@ dependencies = [
name = "syntax-bridge"
version = "0.0.0"
dependencies = [
+ "expect-test",
"intern",
"parser",
"rustc-hash 2.1.1",
@@ -3109,6 +3130,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
[[package]]
+name = "typeid"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
+
+[[package]]
name = "unarray"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index b8dedc6c50..a08b2e412e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
resolver = "2"
[workspace.package]
-rust-version = "1.91"
+rust-version = "1.95"
edition = "2024"
license = "MIT OR Apache-2.0"
authors = ["rust-analyzer team"]
@@ -86,14 +86,14 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
vfs = { path = "./crates/vfs", version = "0.0.0" }
edition = { path = "./crates/edition", version = "0.0.0" }
-ra-ap-rustc_lexer = { version = "0.160", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.160", default-features = false }
-ra-ap-rustc_index = { version = "0.160", default-features = false }
-ra-ap-rustc_abi = { version = "0.160", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.160", default-features = false }
-ra-ap-rustc_ast_ir = { version = "0.160", default-features = false }
-ra-ap-rustc_type_ir = { version = "0.160", default-features = false }
-ra-ap-rustc_next_trait_solver = { version = "0.160", default-features = false }
+ra-ap-rustc_lexer = { version = "0.165", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.165", default-features = false }
+ra-ap-rustc_index = { version = "0.165", default-features = false }
+ra-ap-rustc_abi = { version = "0.165", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.165", default-features = false }
+ra-ap-rustc_ast_ir = { version = "0.165", default-features = false }
+ra-ap-rustc_type_ir = { version = "0.165", default-features = false }
+ra-ap-rustc_next_trait_solver = { version = "0.165", default-features = false }
# local crates that aren't published to crates.io. These should not have versions.
@@ -135,13 +135,13 @@ rayon = "1.10.0"
rowan = "=0.15.18"
# Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work
# on impls without it
-salsa = { version = "0.26", default-features = false, features = [
+salsa = { version = "0.26.2", default-features = false, features = [
"rayon",
"salsa_unstable",
"macros",
"inventory",
] }
-salsa-macros = "0.26"
+salsa-macros = "0.26.2"
semver = "1.0.26"
serde = { version = "1.0.219" }
serde_derive = { version = "1.0.219" }
diff --git a/bench_data/glorious_old_parser b/bench_data/glorious_old_parser
index 5022514924..a6de9daa86 100644
--- a/bench_data/glorious_old_parser
+++ b/bench_data/glorious_old_parser
@@ -1,4 +1,4 @@
-//- minicore: fn
+//- minicore: fn, iterator, try, panic
use crate::ast::{AngleBracketedArgs, ParenthesizedArgs, AttrStyle, BareFnTy};
use crate::ast::{GenericBound, TraitBoundModifier};
use crate::ast::Unsafety;
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index e438505c07..a209a0e631 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -37,7 +37,7 @@ use rustc_hash::{FxHashSet, FxHasher};
use salsa::{Durability, Setter};
pub use semver::{BuildMetadata, Prerelease, Version, VersionReq};
use triomphe::Arc;
-pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet};
+pub use vfs::{AbsPathBuf, AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet};
pub type FxIndexSet<T> = indexmap::IndexSet<T, rustc_hash::FxBuildHasher>;
pub type FxIndexMap<K, V> =
diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs
index 5cf5a9b6be..90fae16ccd 100644
--- a/crates/hir-def/src/attrs.rs
+++ b/crates/hir-def/src/attrs.rs
@@ -137,8 +137,10 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow<
"deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
"ignore" => attr_flags.insert(AttrFlags::IS_IGNORE),
"lang" => attr_flags.insert(AttrFlags::LANG_ITEM),
+ "must_use" => attr_flags.insert(AttrFlags::IS_MUST_USE),
"path" => attr_flags.insert(AttrFlags::HAS_PATH),
"unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE),
+ "rustc_reservation_impl" => attr_flags.insert(AttrFlags::RUSTC_RESERVATION_IMPL),
"export_name" => {
if let Some(value) = attr.value_string()
&& *value == *"main"
@@ -227,6 +229,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow<
"unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE),
"deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
"macro_export" => attr_flags.insert(AttrFlags::IS_MACRO_EXPORT),
+ "must_use" => attr_flags.insert(AttrFlags::IS_MUST_USE),
"no_mangle" => attr_flags.insert(AttrFlags::NO_MANGLE),
"pointee" => attr_flags.insert(AttrFlags::IS_POINTEE),
"non_exhaustive" => attr_flags.insert(AttrFlags::NON_EXHAUSTIVE),
@@ -263,6 +266,12 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow<
}
_ => {}
},
+ "diagnostic" => match &*second_segment {
+ "do_not_recommend" => {
+ attr_flags.insert(AttrFlags::DIAGNOSTIC_DO_NOT_RECOMMEND)
+ }
+ _ => {}
+ },
_ => {}
},
}
@@ -335,6 +344,10 @@ bitflags::bitflags! {
const MACRO_STYLE_PARENTHESES = 1 << 48;
const PREFER_UNDERSCORE_IMPORT = 1 << 49;
+
+ const IS_MUST_USE = 1 << 50;
+
+ const DIAGNOSTIC_DO_NOT_RECOMMEND = 1 << 51;
}
}
@@ -724,52 +737,56 @@ impl AttrFlags {
return None;
}
- return repr(db, owner);
+ Self::repr_assume_has(db, owner)
+ }
- #[salsa::tracked]
- fn repr(db: &dyn DefDatabase, owner: AdtId) -> Option<ReprOptions> {
- let mut result = None;
- collect_attrs::<Infallible>(db, owner.into(), |attr| {
- let mut current = None;
- if let ast::Meta::TokenTreeMeta(attr) = &attr
- && let Some(path) = attr.path()
- && let Some(tt) = attr.token_tree()
+ /// Only call this when you've verified the type indeed has a `#[repr]` attribute!
+ ///
+ /// Prefer [`AttrFlags::repr()`] in non-perf-sensitive places as it also has a check that
+ /// that the ADT has repr.
+ #[salsa::tracked]
+ pub fn repr_assume_has(db: &dyn DefDatabase, owner: AdtId) -> Option<ReprOptions> {
+ let mut result = None;
+ collect_attrs::<Infallible>(db, owner.into(), |attr| {
+ let mut current = None;
+ if let ast::Meta::TokenTreeMeta(attr) = &attr
+ && let Some(path) = attr.path()
+ && let Some(tt) = attr.token_tree()
+ {
+ if path.is1("repr")
+ && let Some(repr) = parse_repr_tt(&tt)
{
- if path.is1("repr")
- && let Some(repr) = parse_repr_tt(&tt)
- {
- current = Some(repr);
- } else if path.is1("rustc_scalable_vector")
- && let mut tt = TokenTreeChildren::new(&tt)
- && let Some(NodeOrToken::Token(scalable)) = tt.next()
- && let Some(scalable) = ast::IntNumber::cast(scalable)
- && let Ok(scalable) = scalable.value()
- && let Ok(scalable) = scalable.try_into()
- {
- current = Some(ReprOptions {
- scalable: Some(rustc_abi::ScalableElt::ElementCount(scalable)),
- ..ReprOptions::default()
- });
- }
- } else if let ast::Meta::PathMeta(attr) = &attr
- && attr.path().is1("rustc_scalable_vector")
+ current = Some(repr);
+ } else if path.is1("rustc_scalable_vector")
+ && let mut tt = TokenTreeChildren::new(&tt)
+ && let Some(NodeOrToken::Token(scalable)) = tt.next()
+ && let Some(scalable) = ast::IntNumber::cast(scalable)
+ && let Ok(scalable) = scalable.value()
+ && let Ok(scalable) = scalable.try_into()
{
current = Some(ReprOptions {
- scalable: Some(rustc_abi::ScalableElt::Container),
+ scalable: Some(rustc_abi::ScalableElt::ElementCount(scalable)),
..ReprOptions::default()
});
}
+ } else if let ast::Meta::PathMeta(attr) = &attr
+ && attr.path().is1("rustc_scalable_vector")
+ {
+ current = Some(ReprOptions {
+ scalable: Some(rustc_abi::ScalableElt::Container),
+ ..ReprOptions::default()
+ });
+ }
- if let Some(current) = current {
- match &mut result {
- Some(existing) => merge_repr(existing, current),
- None => result = Some(current),
- }
+ if let Some(current) = current {
+ match &mut result {
+ Some(existing) => merge_repr(existing, current),
+ None => result = Some(current),
}
- ControlFlow::Continue(())
- });
- result
- }
+ }
+ ControlFlow::Continue(())
+ });
+ result
}
/// Call this only if there are legacy const generics, to save memory.
@@ -1143,6 +1160,28 @@ impl AttrFlags {
})
}
}
+
+ /// Returns `None` if there is no `#[must_use]`, `Some(None)` if there is a `#[must_use]` without a message,
+ /// and `Some(Some(message))` if there is a `#[must_use]` with a message.
+ pub fn must_use_message(db: &dyn DefDatabase, owner: AttrDefId) -> Option<Option<&str>> {
+ if !AttrFlags::query(db, owner).contains(AttrFlags::IS_MUST_USE) {
+ return None;
+ }
+ return Some(must_use_message(db, owner));
+
+ #[salsa::tracked(returns(as_deref))]
+ fn must_use_message(db: &dyn DefDatabase, owner: AttrDefId) -> Option<Box<str>> {
+ collect_attrs(db, owner, |attr| {
+ if let ast::Meta::KeyValueMeta(attr) = attr
+ && attr.path().is1("must_use")
+ && let Some(message) = attr.value_string()
+ {
+ return ControlFlow::Break(Box::from(&*message));
+ }
+ ControlFlow::Continue(())
+ })
+ }
+ }
}
fn merge_repr(this: &mut ReprOptions, other: ReprOptions) {
diff --git a/crates/hir-def/src/attrs/docs.rs b/crates/hir-def/src/attrs/docs.rs
index 9a715b1968..0d01d54786 100644
--- a/crates/hir-def/src/attrs/docs.rs
+++ b/crates/hir-def/src/attrs/docs.rs
@@ -333,7 +333,7 @@ struct DocExprSourceCtx<'db> {
resolver: Resolver<'db>,
file_id: HirFileId,
ast_id_map: &'db AstIdMap,
- span_map: SpanMap,
+ span_map: SpanMap<'db>,
}
fn expand_doc_expr_via_macro_pipeline<'db>(
@@ -390,7 +390,7 @@ fn expand_doc_macro_call<'db>(
.value?;
expander.recursion_depth += 1;
- let parse = expander.db.parse_macro_expansion(call_id).value.0;
+ let parse = expander.db.parse_macro_expansion(call_id).value.0.clone();
let expr = parse.cast::<ast::Expr>().map(|parse| parse.tree())?;
expander.recursion_depth -= 1;
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index 9dd7768ead..11e5c54246 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -7,87 +7,22 @@ use hir_expand::{
use triomphe::Arc;
use crate::{
- AnonConstId, AnonConstLoc, AssocItemId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc,
- EnumId, EnumLoc, EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId,
- ExternCrateLoc, FunctionId, FunctionLoc, ImplId, ImplLoc, Macro2Id, Macro2Loc, MacroExpander,
- MacroId, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId,
- StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId,
- UnionLoc, UseId, UseLoc,
+ AssocItemId, AttrDefId, Macro2Loc, MacroExpander, MacroId, MacroRulesLoc, MacroRulesLocFlags,
+ TraitId,
attrs::AttrFlags,
- item_tree::{ItemTree, file_item_tree_query},
+ item_tree::{ItemTree, file_item_tree},
nameres::crate_def_map,
visibility::{self, Visibility},
};
-use salsa::plumbing::AsId;
-
-#[query_group::query_group(InternDatabaseStorage)]
-pub trait InternDatabase: SourceDatabase {
- // region: items
- #[salsa::interned]
- fn intern_use(&self, loc: UseLoc) -> UseId;
-
- #[salsa::interned]
- fn intern_extern_crate(&self, loc: ExternCrateLoc) -> ExternCrateId;
-
- #[salsa::interned]
- fn intern_function(&self, loc: FunctionLoc) -> FunctionId;
-
- #[salsa::interned]
- fn intern_struct(&self, loc: StructLoc) -> StructId;
-
- #[salsa::interned]
- fn intern_union(&self, loc: UnionLoc) -> UnionId;
-
- #[salsa::interned]
- fn intern_enum(&self, loc: EnumLoc) -> EnumId;
-
- #[salsa::interned]
- fn intern_enum_variant(&self, loc: EnumVariantLoc) -> EnumVariantId;
-
- #[salsa::interned]
- fn intern_const(&self, loc: ConstLoc) -> ConstId;
-
- #[salsa::interned]
- fn intern_static(&self, loc: StaticLoc) -> StaticId;
-
- #[salsa::interned]
- fn intern_anon_const(&self, loc: AnonConstLoc) -> AnonConstId;
-
- #[salsa::interned]
- fn intern_trait(&self, loc: TraitLoc) -> TraitId;
-
- #[salsa::interned]
- fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId;
-
- #[salsa::interned]
- fn intern_impl(&self, loc: ImplLoc) -> ImplId;
-
- #[salsa::interned]
- fn intern_extern_block(&self, loc: ExternBlockLoc) -> ExternBlockId;
-
- #[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;
-}
-
#[query_group::query_group]
-pub trait DefDatabase: InternDatabase + ExpandDatabase + SourceDatabase {
+pub trait DefDatabase: ExpandDatabase + SourceDatabase {
/// Whether to expand procedural macros during name resolution.
#[salsa::input]
fn expand_proc_attr_macros(&self) -> bool;
/// Computes an [`ItemTree`] for the given file or macro expansion.
- #[salsa::invoke(file_item_tree_query)]
+ #[salsa::invoke(file_item_tree)]
#[salsa::transparent]
fn file_item_tree(&self, file_id: HirFileId, krate: Crate) -> &ItemTree;
@@ -122,11 +57,7 @@ fn include_macro_invoc(
.modules
.values()
.flat_map(|m| m.scope.iter_macro_invoc())
- .filter_map(|invoc| {
- db.lookup_intern_macro_call(*invoc.1)
- .include_file_id(db, *invoc.1)
- .map(|x| (*invoc.1, x))
- })
+ .filter_map(|invoc| invoc.1.loc(db).include_file_id(db, *invoc.1).map(|x| (*invoc.1, x)))
.collect()
}
diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs
index 497ed7d37f..4dc7267231 100644
--- a/crates/hir-def/src/expr_store.rs
+++ b/crates/hir-def/src/expr_store.rs
@@ -9,7 +9,10 @@ pub mod scope;
#[cfg(test)]
mod tests;
-use std::ops::{Deref, Index};
+use std::{
+ borrow::Borrow,
+ ops::{Deref, Index},
+};
use cfg::{CfgExpr, CfgOptions};
use either::Either;
@@ -25,14 +28,18 @@ use tt::TextRange;
use crate::{
AdtId, BlockId, ExpressionStoreOwnerId, GenericDefId, SyntheticSyntax,
db::DefDatabase,
- expr_store::path::Path,
+ expr_store::path::{AssociatedTypeBinding, GenericArg, GenericArgs, NormalPath, Path},
hir::{
- Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat,
- PatId, RecordFieldPat, RecordSpread, Statement,
+ Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, InlineAsm, Label,
+ LabelId, MatchArm, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread,
+ Statement,
},
nameres::{DefMap, block_def_map},
signatures::VariantFields,
- type_ref::{LifetimeRef, LifetimeRefId, PathId, TypeRef, TypeRefId},
+ type_ref::{
+ ArrayType, ConstRef, FnType, LifetimeRef, LifetimeRefId, PathId, RefType, TypeBound,
+ TypeRef, TypeRefId, UseArgRef,
+ },
};
pub use self::body::{Body, BodySourceMap};
@@ -91,21 +98,15 @@ pub type TypeSource = InFile<TypePtr>;
pub type LifetimePtr = AstPtr<ast::Lifetime>;
pub type LifetimeSource = InFile<LifetimePtr>;
-/// Describes where a const expression originated from.
-///
-/// Used by signature/body inference to determine the expected type for each
-/// const expression root.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum RootExprOrigin {
- /// Array length expression: `[T; <expr>]` — expected type is `usize`.
- ArrayLength,
- /// Const parameter default value: `const N: usize = <expr>`.
- ConstParam(crate::hir::generics::LocalTypeOrConstParamId),
- /// Const generic argument in a path: `SomeType::<{ <expr> }>` or `some_fn::<{ <expr> }>()`.
- /// Determining the expected type requires path resolution, so it is deferred.
- GenericArgsPath,
- /// The root expression of a body.
- BodyRoot,
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+struct ExprRoot {
+ root: ExprId,
+ // We store, for each root, the range of exprs (and pats and bindings) it holds.
+ // We store only the end (exclusive), since the start can be inferred from the previous
+ // roots or is zero.
+ exprs_end: ExprId,
+ pats_end: PatId,
+ bindings_end: BindingId,
}
// We split the store into types-only and expressions, because most stores (e.g. generics)
@@ -129,7 +130,34 @@ struct ExpressionOnlyStore {
ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
/// Maps expression roots to their origin.
- expr_roots: SmallVec<[(ExprId, RootExprOrigin); 1]>,
+ ///
+ /// Note: while every root expr is an inference root (aka. an `AnonConst`), there could be other roots that do not appear here.
+ /// This can happen when anon consts are nested, for example:
+ ///
+ /// ```
+ /// [
+ /// ();
+ /// {
+ /// // this repeat expr is anon const #1, and *only it* appears in this list.
+ /// [
+ /// ();
+ /// {
+ /// // this repeat expr is anon const #2.
+ /// 0
+ /// }
+ /// ];
+ /// 0
+ /// }
+ /// ]
+ /// ```
+ /// We do this because this allows us to search this list using a binary search,
+ /// and it does not bother us because we use this list for two things: constructing `ExprScopes`, which
+ /// works fine with nested exprs, and retrieving inference results, and we copy the inner const's inference
+ /// into the outer const.
+ // FIXME: Array repeat is not problematic indeed, but this could still break with exprs in types,
+ // which we do not visit for `ExprScopes` (they're fine for inference though). We either need to visit them,
+ // or use a more complicated search.
+ expr_roots: SmallVec<[ExprRoot; 1]>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -243,7 +271,7 @@ pub struct ExpressionStoreBuilder {
pub types: Arena<TypeRef>,
block_scopes: Vec<BlockId>,
ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
- pub inference_roots: Option<SmallVec<[(ExprId, RootExprOrigin); 1]>>,
+ inference_roots: Option<SmallVec<[ExprRoot; 1]>>,
// AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
// to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
@@ -303,6 +331,7 @@ pub enum ExpressionStoreDiagnostics {
UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String },
UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name },
+ PatternArgInExternFn { node: InFile<AstPtr<ast::Pat>> },
}
impl ExpressionStoreBuilder {
@@ -375,8 +404,8 @@ impl ExpressionStoreBuilder {
let store = {
let expr_only = if has_exprs {
- if let Some(const_expr_origins) = &mut expr_roots {
- const_expr_origins.shrink_to_fit();
+ if let Some(expr_roots) = &mut expr_roots {
+ expr_roots.shrink_to_fit();
}
Some(Box::new(ExpressionOnlyStore {
exprs,
@@ -386,7 +415,8 @@ impl ExpressionStoreBuilder {
binding_owners,
block_scopes: block_scopes.into_boxed_slice(),
ident_hygiene,
- expr_roots: expr_roots.unwrap_or_default(),
+ expr_roots: expr_roots
+ .expect("should always finish with a `Some(_)` expr_roots"),
}))
} else {
None
@@ -427,6 +457,14 @@ impl ExpressionStoreBuilder {
}
impl ExpressionStore {
+ const EMPTY: &ExpressionStore =
+ &ExpressionStore { expr_only: None, types: Arena::new(), lifetimes: Arena::new() };
+
+ #[inline]
+ pub fn empty() -> &'static ExpressionStore {
+ ExpressionStore::EMPTY
+ }
+
pub fn of(db: &dyn DefDatabase, def: ExpressionStoreOwnerId) -> &ExpressionStore {
match def {
ExpressionStoreOwnerId::Signature(def) => {
@@ -516,19 +554,35 @@ impl ExpressionStore {
}
/// Returns all expression root `ExprId`s found in this store.
- pub fn expr_roots(&self) -> impl Iterator<Item = ExprId> {
- self.const_expr_origins().iter().map(|&(id, _)| id)
+ pub fn expr_roots(&self) -> impl DoubleEndedIterator<Item = ExprId> {
+ self.expr_only
+ .as_ref()
+ .map_or(&[][..], |expr_only| &expr_only.expr_roots)
+ .iter()
+ .map(|root| root.root)
}
- /// Like [`Self::expr_roots`], but also returns the origin
- /// of each expression.
- pub fn expr_roots_with_origins(&self) -> impl Iterator<Item = (ExprId, RootExprOrigin)> {
- self.const_expr_origins().iter().map(|&(id, origin)| (id, origin))
+ fn find_root_for(
+ &self,
+ mut get: impl FnMut(&ExprRoot) -> la_arena::RawIdx,
+ find: la_arena::RawIdx,
+ ) -> ExprId {
+ let expr_only = self.assert_expr_only();
+ let find = find.into_u32();
+ let entry = expr_only.expr_roots.partition_point(|root| get(root).into_u32() <= find);
+ expr_only.expr_roots[entry].root
}
- /// Returns the map of const expression roots to their origins.
- pub fn const_expr_origins(&self) -> &[(ExprId, RootExprOrigin)] {
- self.expr_only.as_ref().map_or(&[], |it| &it.expr_roots)
+ pub fn find_root_for_expr(&self, expr: ExprId) -> ExprId {
+ self.find_root_for(|root| root.exprs_end.into_raw(), expr.into_raw())
+ }
+
+ pub fn find_root_for_pat(&self, pat: PatId) -> ExprId {
+ self.find_root_for(|root| root.pats_end.into_raw(), pat.into_raw())
+ }
+
+ pub fn find_root_for_binding(&self, binding: BindingId) -> ExprId {
+ self.find_root_for(|root| root.bindings_end.into_raw(), binding.into_raw())
}
/// Returns an iterator over all block expressions in this store that define inner items.
@@ -552,33 +606,46 @@ impl ExpressionStore {
});
}
- pub fn walk_pats_shallow(&self, pat_id: PatId, mut f: impl FnMut(PatId)) {
+ pub fn visit_pat_children(&self, pat_id: PatId, mut visitor: impl StoreVisitor) {
+ // Do not use `..` patterns or field accesses here, only destructuring, to ensure we cover all cases
+ // (we've had multiple bugs with this in the past).
let pat = &self[pat_id];
match pat {
- Pat::Range { .. }
- | Pat::Lit(..)
- | Pat::Path(..)
- | Pat::ConstBlock(..)
- | Pat::Wild
- | Pat::Missing
- | Pat::Expr(_) => {}
- &Pat::Bind { subpat, .. } => {
- if let Some(subpat) = subpat {
- f(subpat);
- }
+ Pat::Range { start, end, range_type: _ } => {
+ visitor.on_expr_opt(*start);
+ visitor.on_expr_opt(*end);
}
- Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
- args.iter().copied().for_each(f);
+ Pat::Lit(expr) | Pat::ConstBlock(expr) | Pat::Expr(expr) => visitor.on_expr(*expr),
+ Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest => {}
+ &Pat::Bind { subpat, id: _ } => visitor.on_pat_opt(subpat),
+ Pat::Or(args) | Pat::Tuple { args, ellipsis: _ } => visitor.on_pats(args),
+ Pat::TupleStruct { args, ellipsis: _, path } => {
+ visitor.on_pats(args);
+ visitor.on_path(path);
}
- Pat::Ref { pat, .. } => f(*pat),
+ Pat::Ref { pat, mutability: _ } => visitor.on_pat(*pat),
Pat::Slice { prefix, slice, suffix } => {
- let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
- total_iter.copied().for_each(f);
+ visitor.on_pats(prefix);
+ visitor.on_pat_opt(*slice);
+ visitor.on_pats(suffix);
}
- Pat::Record { args, .. } => {
- args.iter().for_each(|RecordFieldPat { pat, .. }| f(*pat));
+ Pat::Record { args, ellipsis: _, path } => {
+ args.iter().for_each(|RecordFieldPat { pat, name: _ }| visitor.on_pat(*pat));
+ visitor.on_path(path);
+ }
+ Pat::Box { inner } | Pat::Deref { inner } => visitor.on_pat(*inner),
+ }
+ }
+
+ pub fn walk_pats_shallow(&self, pat_id: PatId, f: impl FnMut(PatId)) {
+ return self.visit_pat_children(pat_id, Visitor(f));
+
+ struct Visitor<F>(F);
+
+ impl<F: FnMut(PatId)> StoreVisitor for Visitor<F> {
+ fn on_pat(&mut self, pat: PatId) {
+ (self.0)(pat);
}
- Pat::Box { inner } => f(*inner),
}
}
@@ -604,276 +671,212 @@ impl ExpressionStore {
self.expr_only.as_ref()?.binding_owners.get(&id).copied()
}
- /// Walks the immediate children expressions and calls `f` for each child expression.
- ///
- /// Note that this does not walk const blocks.
- pub fn walk_child_exprs(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) {
- let expr = &self[expr_id];
- match expr {
- Expr::Continue { .. }
- | Expr::Const(_)
- | Expr::Missing
- | Expr::Path(_)
- | Expr::OffsetOf(_)
- | Expr::Literal(_)
- | Expr::Underscore => {}
- Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
- AsmOperand::In { expr, .. }
- | AsmOperand::Out { expr: Some(expr), .. }
- | AsmOperand::InOut { expr, .. }
- | AsmOperand::Const(expr)
- | AsmOperand::Label(expr) => f(*expr),
- AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
- f(*in_expr);
- if let Some(out_expr) = out_expr {
- f(*out_expr);
+ pub fn visit_expr_children(&self, expr_id: ExprId, mut visitor: impl StoreVisitor) {
+ // Do not use `..` patterns or field accesses here, only destructuring, to ensure we cover all cases
+ // (we've had multiple bugs with this in the past).
+ match &self[expr_id] {
+ Expr::OffsetOf(OffsetOf { container, fields: _ }) => visitor.on_type(*container),
+ Expr::Path(path) => visitor.on_path(path),
+ Expr::Continue { label: _ } | Expr::Missing | Expr::Literal(_) | Expr::Underscore => {}
+ Expr::InlineAsm(InlineAsm { operands, options: _, kind: _ }) => {
+ operands.iter().for_each(|(_, op)| match op {
+ AsmOperand::In { expr, reg: _ }
+ | AsmOperand::Out { expr: Some(expr), late: _, reg: _ }
+ | AsmOperand::InOut { expr, late: _, reg: _ }
+ | AsmOperand::Const(expr)
+ | AsmOperand::Label(expr) => visitor.on_expr(*expr),
+ AsmOperand::SplitInOut { in_expr, out_expr, late: _, reg: _ } => {
+ visitor.on_expr(*in_expr);
+ visitor.on_expr_opt(*out_expr);
}
- }
- AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (),
- }),
+ AsmOperand::Out { expr: None, late: _, reg: _ } | AsmOperand::Sym(_) => (),
+ })
+ }
Expr::If { condition, then_branch, else_branch } => {
- f(*condition);
- f(*then_branch);
- if let &Some(else_branch) = else_branch {
- f(else_branch);
- }
+ visitor.on_expr(*condition);
+ visitor.on_expr(*then_branch);
+ visitor.on_expr_opt(*else_branch);
}
Expr::Let { expr, pat } => {
- self.walk_exprs_in_pat(*pat, &mut f);
- f(*expr);
+ visitor.on_pat(*pat);
+ visitor.on_expr(*expr);
}
- Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => {
- for stmt in statements.iter() {
+ Expr::Block { statements, tail, id: _, label: _ }
+ | Expr::Unsafe { statements, tail, id: _ } => {
+ for stmt in statements {
match stmt {
- Statement::Let { initializer, else_branch, pat, .. } => {
- if let &Some(expr) = initializer {
- f(expr);
- }
- if let &Some(expr) = else_branch {
- f(expr);
- }
- self.walk_exprs_in_pat(*pat, &mut f);
+ Statement::Let { initializer, else_branch, pat, type_ref } => {
+ visitor.on_expr_opt(*initializer);
+ visitor.on_expr_opt(*else_branch);
+ visitor.on_pat(*pat);
+ visitor.on_type_opt(*type_ref);
+ }
+ Statement::Expr { expr: expression, has_semi: _ } => {
+ visitor.on_expr(*expression)
}
- Statement::Expr { expr: expression, .. } => f(*expression),
Statement::Item(_) => (),
}
}
- if let &Some(expr) = tail {
- f(expr);
- }
+ visitor.on_expr_opt(*tail);
}
- Expr::Loop { body, .. } => f(*body),
- Expr::Call { callee, args, .. } => {
- f(*callee);
- args.iter().copied().for_each(f);
+ Expr::Loop { body, label: _ } => visitor.on_expr(*body),
+ Expr::Call { callee, args } => {
+ visitor.on_expr(*callee);
+ visitor.on_exprs(args);
}
- Expr::MethodCall { receiver, args, .. } => {
- f(*receiver);
- args.iter().copied().for_each(f);
+ Expr::MethodCall { receiver, args, generic_args, method_name: _ } => {
+ visitor.on_expr(*receiver);
+ visitor.on_exprs(args);
+ visitor.on_generic_args_opt(generic_args);
}
Expr::Match { expr, arms } => {
- f(*expr);
- arms.iter().for_each(|arm| {
- f(arm.expr);
- if let Some(guard) = arm.guard {
- f(guard);
- }
- self.walk_exprs_in_pat(arm.pat, &mut f);
+ visitor.on_expr(*expr);
+ arms.iter().for_each(|MatchArm { pat, guard, expr }| {
+ visitor.on_expr(*expr);
+ visitor.on_expr_opt(*guard);
+ visitor.on_pat(*pat);
});
}
- Expr::Break { expr, .. }
+ Expr::Break { expr, label: _ }
| Expr::Return { expr }
| Expr::Yield { expr }
- | Expr::Yeet { expr } => {
- if let &Some(expr) = expr {
- f(expr);
+ | Expr::Yeet { expr } => visitor.on_expr_opt(*expr),
+ Expr::Become { expr } => visitor.on_expr(*expr),
+ Expr::RecordLit { fields, spread, path } => {
+ for RecordLitField { name: _, expr } in fields.iter() {
+ visitor.on_expr(*expr);
}
- }
- Expr::Become { expr } => f(*expr),
- Expr::RecordLit { fields, spread, .. } => {
- for field in fields.iter() {
- f(field.expr);
- }
- if let RecordSpread::Expr(expr) = spread {
- f(*expr);
+ match spread {
+ RecordSpread::Expr(expr) => visitor.on_expr(*expr),
+ RecordSpread::None | RecordSpread::FieldDefaults => {}
}
+ visitor.on_path(path);
}
- Expr::Closure { body, .. } => {
- f(*body);
+ Expr::Closure { body, args, arg_types, ret_type, capture_by: _, closure_kind: _ } => {
+ visitor.on_expr(*body);
+ visitor.on_pats(args);
+ arg_types.iter().for_each(|arg_type| visitor.on_type_opt(*arg_type));
+ visitor.on_type_opt(*ret_type);
}
- Expr::BinaryOp { lhs, rhs, .. } => {
- f(*lhs);
- f(*rhs);
+ Expr::BinaryOp { lhs, rhs, op: _ } => {
+ visitor.on_expr(*lhs);
+ visitor.on_expr(*rhs);
}
- Expr::Range { lhs, rhs, .. } => {
- if let &Some(lhs) = rhs {
- f(lhs);
- }
- if let &Some(rhs) = lhs {
- f(rhs);
- }
+ Expr::Range { lhs, rhs, range_type: _ } => {
+ visitor.on_expr_opt(*lhs);
+ visitor.on_expr_opt(*rhs);
}
- Expr::Index { base, index, .. } => {
- f(*base);
- f(*index);
+ Expr::Index { base, index } => {
+ visitor.on_expr(*base);
+ visitor.on_expr(*index);
}
- Expr::Field { expr, .. }
+ Expr::Cast { expr, type_ref } => {
+ visitor.on_expr(*expr);
+ visitor.on_type(*type_ref);
+ }
+ Expr::Field { expr, name: _ }
| Expr::Await { expr }
- | Expr::Cast { expr, .. }
- | Expr::Ref { expr, .. }
- | Expr::UnaryOp { expr, .. }
- | Expr::Box { expr } => {
- f(*expr);
+ | Expr::Ref { expr, mutability: _, rawness: _ }
+ | Expr::UnaryOp { expr, op: _ }
+ | Expr::Box { expr }
+ | Expr::Const(expr) => {
+ visitor.on_expr(*expr);
}
- Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
+ Expr::Tuple { exprs } => visitor.on_exprs(exprs),
Expr::Array(a) => match a {
- Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
+ Array::ElementList { elements } => visitor.on_exprs(elements),
Array::Repeat { initializer, repeat } => {
- f(*initializer);
- f(*repeat)
+ visitor.on_expr(*initializer);
+ visitor.on_anon_const_expr(*repeat)
}
},
&Expr::Assignment { target, value } => {
- self.walk_exprs_in_pat(target, &mut f);
- f(value);
+ visitor.on_pat(target);
+ visitor.on_expr(value);
}
}
}
- /// Walks the immediate children expressions and calls `f` for each child expression but does
- /// not walk expressions within patterns.
- ///
- /// Note that this does not walk const blocks.
- pub fn walk_child_exprs_without_pats(&self, expr_id: ExprId, mut f: impl FnMut(ExprId)) {
- let expr = &self[expr_id];
- match expr {
- Expr::Continue { .. }
- | Expr::Const(_)
- | Expr::Missing
- | Expr::Path(_)
- | Expr::OffsetOf(_)
- | Expr::Literal(_)
- | Expr::Underscore => {}
- Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
- AsmOperand::In { expr, .. }
- | AsmOperand::Out { expr: Some(expr), .. }
- | AsmOperand::InOut { expr, .. }
- | AsmOperand::Const(expr)
- | AsmOperand::Label(expr) => f(*expr),
- AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
- f(*in_expr);
- if let Some(out_expr) = out_expr {
- f(*out_expr);
- }
- }
- AsmOperand::Out { expr: None, .. } | AsmOperand::Sym(_) => (),
- }),
- Expr::If { condition, then_branch, else_branch } => {
- f(*condition);
- f(*then_branch);
- if let &Some(else_branch) = else_branch {
- f(else_branch);
- }
- }
- Expr::Let { expr, .. } => {
- f(*expr);
- }
- Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => {
- for stmt in statements.iter() {
- match stmt {
- Statement::Let { initializer, else_branch, .. } => {
- if let &Some(expr) = initializer {
- f(expr);
- }
- if let &Some(expr) = else_branch {
- f(expr);
- }
- }
- Statement::Expr { expr: expression, .. } => f(*expression),
- Statement::Item(_) => (),
- }
- }
- if let &Some(expr) = tail {
- f(expr);
- }
- }
- Expr::Loop { body, .. } => f(*body),
- Expr::Call { callee, args, .. } => {
- f(*callee);
- args.iter().copied().for_each(f);
- }
- Expr::MethodCall { receiver, args, .. } => {
- f(*receiver);
- args.iter().copied().for_each(f);
+ /// Walks the immediate children expressions and calls `f` for each child expression.
+ pub fn walk_child_exprs(&self, expr_id: ExprId, callback: impl FnMut(ExprId)) {
+ return self.visit_expr_children(expr_id, Visitor { callback, store: self });
+
+ struct Visitor<'a, F> {
+ callback: F,
+ store: &'a ExpressionStore,
+ }
+
+ impl<F: FnMut(ExprId)> StoreVisitor for Visitor<'_, F> {
+ fn on_expr(&mut self, expr: ExprId) {
+ (self.callback)(expr);
}
- Expr::Match { expr, arms } => {
- f(*expr);
- arms.iter().map(|arm| arm.expr).for_each(f);
+
+ fn on_pat(&mut self, pat: PatId) {
+ self.store.walk_exprs_in_pat(pat, &mut self.callback);
}
- Expr::Break { expr, .. }
- | Expr::Return { expr }
- | Expr::Yield { expr }
- | Expr::Yeet { expr } => {
- if let &Some(expr) = expr {
- f(expr);
- }
+ }
+ }
+
+ /// Walks the immediate children expressions and calls `f` for each child expression but does
+ /// not walk expressions within patterns.
+ pub fn walk_child_exprs_without_pats(&self, expr_id: ExprId, callback: impl FnMut(ExprId)) {
+ return self.visit_expr_children(expr_id, Visitor { callback });
+
+ struct Visitor<F> {
+ callback: F,
+ }
+
+ impl<F: FnMut(ExprId)> StoreVisitor for Visitor<F> {
+ fn on_expr(&mut self, expr: ExprId) {
+ (self.callback)(expr);
}
- Expr::Become { expr } => f(*expr),
- Expr::RecordLit { fields, spread, .. } => {
- for field in fields.iter() {
- f(field.expr);
- }
- if let RecordSpread::Expr(expr) = spread {
- f(*expr);
- }
+ }
+ }
+
+ pub fn walk_exprs_in_pat(&self, pat_id: PatId, callback: impl FnMut(ExprId)) {
+ return Visitor { callback, store: self }.on_pat(pat_id);
+
+ struct Visitor<'a, F> {
+ callback: F,
+ store: &'a ExpressionStore,
+ }
+
+ impl<F: FnMut(ExprId)> StoreVisitor for Visitor<'_, F> {
+ fn on_expr(&mut self, expr: ExprId) {
+ (self.callback)(expr);
}
- Expr::Closure { body, .. } => {
- f(*body);
+
+ fn on_pat(&mut self, pat: PatId) {
+ self.store.visit_pat_children(pat, self);
}
- Expr::BinaryOp { lhs, rhs, .. } => {
- f(*lhs);
- f(*rhs);
+ }
+ }
+
+ pub fn visit_type_ref_children(&self, type_ref: TypeRefId, mut visitor: impl StoreVisitor) {
+ match &self[type_ref] {
+ TypeRef::Never | TypeRef::Placeholder | TypeRef::TypeParam(_) | TypeRef::Error => {}
+ TypeRef::Tuple(types) => visitor.on_types(types),
+ TypeRef::Path(path) => visitor.on_path(path),
+ TypeRef::RawPtr(inner, _) | TypeRef::Slice(inner) => visitor.on_type(*inner),
+ TypeRef::Reference(ref_type) => {
+ let RefType { ty, lifetime, mutability: _ } = &**ref_type;
+ visitor.on_type(*ty);
+ visitor.on_lifetime_opt(*lifetime);
}
- Expr::Range { lhs, rhs, .. } => {
- if let &Some(lhs) = rhs {
- f(lhs);
- }
- if let &Some(rhs) = lhs {
- f(rhs);
- }
+ TypeRef::Array(ArrayType { ty, len: ConstRef { expr: len } }) => {
+ visitor.on_type(*ty);
+ visitor.on_anon_const_expr(*len);
}
- Expr::Index { base, index, .. } => {
- f(*base);
- f(*index);
+ TypeRef::Fn(fn_type) => {
+ let FnType { params, is_varargs: _, is_unsafe: _, abi: _ } = &**fn_type;
+ params.iter().for_each(|(_, param_ty)| visitor.on_type(*param_ty));
}
- Expr::Field { expr, .. }
- | Expr::Await { expr }
- | Expr::Cast { expr, .. }
- | Expr::Ref { expr, .. }
- | Expr::UnaryOp { expr, .. }
- | Expr::Box { expr } => {
- f(*expr);
+ TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
+ visitor.on_type_bounds(bounds)
}
- Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f),
- Expr::Array(a) => match a {
- Array::ElementList { elements, .. } => elements.iter().copied().for_each(f),
- Array::Repeat { initializer, repeat } => {
- f(*initializer);
- f(*repeat)
- }
- },
- &Expr::Assignment { target: _, value } => f(value),
}
}
- pub fn walk_exprs_in_pat(&self, pat_id: PatId, f: &mut impl FnMut(ExprId)) {
- self.walk_pats(pat_id, &mut |pat| {
- if let Pat::Expr(expr) | Pat::ConstBlock(expr) = self[pat] {
- f(expr);
- }
- });
- }
-
#[inline]
#[track_caller]
fn assert_expr_only(&self) -> &ExpressionOnlyStore {
@@ -938,6 +941,128 @@ impl ExpressionStore {
}
}
+pub trait StoreVisitor {
+ fn on_expr(&mut self, expr: ExprId) {
+ let _ = expr;
+ }
+ fn on_anon_const_expr(&mut self, expr: ExprId) {
+ self.on_expr(expr);
+ }
+ fn on_pat(&mut self, pat: PatId) {
+ let _ = pat;
+ }
+ fn on_type(&mut self, ty: TypeRefId) {
+ let _ = ty;
+ }
+ fn on_lifetime(&mut self, lifetime: LifetimeRefId) {
+ let _ = lifetime;
+ }
+}
+
+impl<V: StoreVisitor> StoreVisitor for &mut V {
+ fn on_expr(&mut self, expr: ExprId) {
+ V::on_expr(self, expr);
+ }
+ fn on_anon_const_expr(&mut self, expr: ExprId) {
+ V::on_anon_const_expr(self, expr);
+ }
+ fn on_pat(&mut self, pat: PatId) {
+ V::on_pat(self, pat);
+ }
+ fn on_type(&mut self, ty: TypeRefId) {
+ V::on_type(self, ty);
+ }
+ fn on_lifetime(&mut self, lifetime: LifetimeRefId) {
+ V::on_lifetime(self, lifetime);
+ }
+}
+
+trait StoreVisitorExt: StoreVisitor {
+ fn on_generic_args(&mut self, args: &GenericArgs) {
+ let GenericArgs { args, bindings, parenthesized: _, has_self_type: _ } = args;
+ for arg in args {
+ match arg {
+ GenericArg::Type(arg) => self.on_type(*arg),
+ GenericArg::Const(ConstRef { expr }) => self.on_anon_const_expr(*expr),
+ GenericArg::Lifetime(arg) => self.on_lifetime(*arg),
+ }
+ }
+ for AssociatedTypeBinding { name: _, args, type_ref, bounds } in bindings {
+ self.on_generic_args_opt(args);
+ self.on_type_opt(*type_ref);
+ self.on_type_bounds(bounds);
+ }
+ }
+
+ fn on_type_bound(&mut self, bound: &TypeBound) {
+ match bound {
+ TypeBound::Path(path_id, _) => self.on_type(path_id.type_ref()),
+ TypeBound::ForLifetime(_, path_id) => self.on_type(path_id.type_ref()),
+ TypeBound::Lifetime(lifetime) => self.on_lifetime(*lifetime),
+ TypeBound::Use(args) => {
+ for arg in args {
+ match arg {
+ UseArgRef::Lifetime(lifetime) => self.on_lifetime(*lifetime),
+ UseArgRef::Name(_) => {}
+ }
+ }
+ }
+ TypeBound::Error => {}
+ }
+ }
+
+ fn on_path(&mut self, path: &Path) {
+ match path {
+ Path::Normal(path) => {
+ let NormalPath { generic_args, type_anchor, mod_path: _ } = &**path;
+ generic_args.iter().for_each(|generic_arg| self.on_generic_args_opt(generic_arg));
+ self.on_type_opt(*type_anchor);
+ }
+ Path::BarePath(_) | Path::LangItem(..) => {}
+ }
+ }
+
+ fn on_expr_opt(&mut self, expr: Option<ExprId>) {
+ if let Some(expr) = expr {
+ self.on_expr(expr);
+ }
+ }
+ fn on_pat_opt(&mut self, pat: Option<PatId>) {
+ if let Some(pat) = pat {
+ self.on_pat(pat);
+ }
+ }
+ fn on_type_opt(&mut self, ty: Option<TypeRefId>) {
+ if let Some(ty) = ty {
+ self.on_type(ty);
+ }
+ }
+ fn on_lifetime_opt(&mut self, lifetime: Option<LifetimeRefId>) {
+ if let Some(lifetime) = lifetime {
+ self.on_lifetime(lifetime);
+ }
+ }
+ fn on_generic_args_opt(&mut self, args: &Option<impl Borrow<GenericArgs>>) {
+ if let Some(args) = args {
+ self.on_generic_args(args.borrow());
+ }
+ }
+
+ fn on_exprs(&mut self, exprs: impl IntoIterator<Item: Borrow<ExprId>>) {
+ exprs.into_iter().for_each(|expr| self.on_expr(*expr.borrow()));
+ }
+ fn on_pats(&mut self, pats: impl IntoIterator<Item: Borrow<PatId>>) {
+ pats.into_iter().for_each(|pat| self.on_pat(*pat.borrow()));
+ }
+ fn on_types(&mut self, types: impl IntoIterator<Item: Borrow<TypeRefId>>) {
+ types.into_iter().for_each(|ty| self.on_type(*ty.borrow()));
+ }
+ fn on_type_bounds(&mut self, bounds: impl IntoIterator<Item: Borrow<TypeBound>>) {
+ bounds.into_iter().for_each(|bound| self.on_type_bound(bound.borrow()));
+ }
+}
+impl<V: StoreVisitor> StoreVisitorExt for V {}
+
impl Index<ExprId> for ExpressionStore {
type Output = Expr;
diff --git a/crates/hir-def/src/expr_store/body.rs b/crates/hir-def/src/expr_store/body.rs
index 6be3e49a70..2fb47e59c5 100644
--- a/crates/hir-def/src/expr_store/body.rs
+++ b/crates/hir-def/src/expr_store/body.rs
@@ -2,6 +2,7 @@
//! consts.
use std::ops;
+use arrayvec::ArrayVec;
use hir_expand::{InFile, Lookup};
use span::Edition;
use syntax::ast;
@@ -28,7 +29,12 @@ pub struct Body {
/// If this `Body` is for the body of a constant, this will just be
/// empty.
pub params: Box<[PatId]>,
- pub self_param: Option<BindingId>,
+ /// The first element, if it exists, is the real `self` binding.
+ ///
+ /// The second element is used for `async fn` (or `gen fn` etc.). These functions
+ /// have to put a `let self = self` inside the returned coroutine, and the second element
+ /// points at it.
+ pub self_params: ArrayVec<BindingId, 2>,
}
impl ops::Deref for Body {
@@ -74,6 +80,7 @@ impl Body {
let mut params = None;
let mut is_async_fn = false;
+ let mut is_gen_fn = false;
let InFile { file_id, value: body } = {
match def {
DefWithBodyId::FunctionId(f) => {
@@ -81,6 +88,7 @@ impl Body {
let src = f.source(db);
params = src.value.param_list();
is_async_fn = src.value.async_token().is_some();
+ is_gen_fn = src.value.gen_token().is_some();
src.map(|it| it.body().map(ast::Expr::from))
}
DefWithBodyId::ConstId(c) => {
@@ -101,7 +109,8 @@ impl Body {
}
};
let module = def.module(db);
- let (body, source_map) = lower_body(db, def, file_id, module, params, body, is_async_fn);
+ let (body, source_map) =
+ lower_body(db, def, file_id, module, params, body, is_async_fn, is_gen_fn);
(Arc::new(body), source_map)
}
@@ -114,7 +123,19 @@ impl Body {
impl Body {
pub fn root_expr(&self) -> ExprId {
- self.store.expr_roots().next().unwrap()
+ // A `Body` can also contain root expressions that aren't the body (in the param patterns),
+ // but the body always come last.
+ self.store.expr_roots().next_back().unwrap()
+ }
+
+ pub fn self_param(&self) -> Option<BindingId> {
+ self.self_params.first().copied()
+ }
+
+ /// `async fn` (or `gen fn` etc.), have to put a `let self = self` inside the returned coroutine.
+ /// This function returns it.
+ pub fn coroutine_self_binding(&self) -> Option<BindingId> {
+ self.self_params.get(1).copied()
}
pub fn pretty_print(
diff --git a/crates/hir-def/src/expr_store/expander.rs b/crates/hir-def/src/expr_store/expander.rs
index 2fffa02c13..c79a1db847 100644
--- a/crates/hir-def/src/expr_store/expander.rs
+++ b/crates/hir-def/src/expr_store/expander.rs
@@ -5,10 +5,8 @@ use std::mem;
use base_db::Crate;
use cfg::CfgOptions;
use drop_bomb::DropBomb;
-use hir_expand::AstId;
-use hir_expand::span_map::SpanMapRef;
use hir_expand::{
- ExpandError, ExpandErrorKind, ExpandResult, HirFileId, InFile, Lookup, MacroCallId,
+ AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, InFile, Lookup, MacroCallId,
eager::EagerCallBackFn, mod_path::ModPath, span_map::SpanMap,
};
use span::{AstIdMap, SyntaxContext};
@@ -23,7 +21,7 @@ use crate::{
#[derive(Debug)]
pub(super) struct Expander<'db> {
- span_map: SpanMap,
+ span_map: SpanMap<'db>,
current_file_id: HirFileId,
ast_id_map: &'db AstIdMap,
/// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached.
@@ -58,11 +56,11 @@ impl<'db> Expander<'db> {
}
pub(super) fn hygiene_for_range(&self, db: &dyn DefDatabase, range: TextRange) -> HygieneId {
- match self.span_map.as_ref() {
- hir_expand::span_map::SpanMapRef::ExpansionSpanMap(span_map) => {
+ match self.span_map {
+ SpanMap::ExpansionSpanMap(span_map) => {
HygieneId::new(span_map.span_at(range.start()).ctx.opaque_and_semiopaque(db))
}
- hir_expand::span_map::SpanMapRef::RealSpanMap(_) => HygieneId::ROOT,
+ SpanMap::RealSpanMap(_) => HygieneId::ROOT,
}
}
@@ -193,15 +191,15 @@ impl<'db> Expander<'db> {
let res = db.parse_macro_expansion(call_id);
- let err = err.or(res.err);
+ let err = err.or_else(|| res.err.clone());
ExpandResult {
value: {
- let parse = res.value.0.cast::<T>();
+ let parse = res.value.0.clone().cast::<T>();
self.recursion_depth += 1;
let old_file_id = std::mem::replace(&mut self.current_file_id, call_id.into());
let old_span_map =
- std::mem::replace(&mut self.span_map, db.span_map(self.current_file_id));
+ std::mem::replace(&mut self.span_map, SpanMap::ExpansionSpanMap(&res.value.1));
let prev_ast_id_map =
mem::replace(&mut self.ast_id_map, db.ast_id_map(self.current_file_id));
let mark = Mark {
@@ -222,15 +220,15 @@ impl<'db> Expander<'db> {
}
#[inline]
- pub(super) fn span_map(&self) -> SpanMapRef<'_> {
- self.span_map.as_ref()
+ pub(super) fn span_map(&self) -> SpanMap<'_> {
+ self.span_map
}
}
#[derive(Debug)]
pub(super) struct Mark<'db> {
file_id: HirFileId,
- span_map: SpanMap,
+ span_map: SpanMap<'db>,
ast_id_map: &'db AstIdMap,
bomb: DropBomb,
}
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 04437a59ac..8818096500 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -8,17 +8,20 @@ mod path;
use std::{cell::OnceCell, mem};
+use arrayvec::ArrayVec;
use base_db::FxIndexSet;
use cfg::CfgOptions;
use either::Either;
use hir_expand::{
HirFileId, InFile, MacroDefId,
+ mod_path::ModPath,
name::{AsName, Name},
- span_map::SpanMapRef,
+ span_map::SpanMap,
};
use intern::{Symbol, sym};
+use rustc_abi::ExternAbi;
use rustc_hash::FxHashMap;
-use smallvec::smallvec;
+use smallvec::SmallVec;
use stdx::never;
use syntax::{
AstNode, AstPtr, SyntaxNodePtr,
@@ -37,17 +40,17 @@ use crate::{
attrs::AttrFlags,
db::DefDatabase,
expr_store::{
- Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder,
+ Body, BodySourceMap, ExprPtr, ExprRoot, ExpressionStore, ExpressionStoreBuilder,
ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, LifetimePtr,
- PatPtr, RootExprOrigin, TypePtr,
+ PatPtr, TypePtr,
expander::Expander,
lower::generics::ImplTraitLowerFn,
path::{AssociatedTypeBinding, GenericArg, GenericArgs, GenericArgsParentheses, Path},
},
hir::{
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
- CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability,
- OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement,
+ CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm,
+ Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement,
generics::GenericParams,
},
item_scope::BuiltinShadowMode,
@@ -71,6 +74,7 @@ pub(super) fn lower_body(
parameters: Option<ast::ParamList>,
body: Option<ast::Expr>,
is_async_fn: bool,
+ is_gen_fn: bool,
) -> (Body, BodySourceMap) {
// We cannot leave the root span map empty and let any identifier from it be treated as root,
// because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved
@@ -78,10 +82,10 @@ pub(super) fn lower_body(
// even though they should be the same. Also, when the body comes from multiple expansions, their
// hygiene is different.
- let mut self_param = None;
+ let mut self_params = ArrayVec::new();
let mut source_map_self_param = None;
let mut params = vec![];
- let mut collector = ExprCollector::body(db, module, current_file_id);
+ let mut collector = ExprCollector::new(db, module, current_file_id);
let skip_body = AttrFlags::query(
db,
@@ -111,18 +115,17 @@ pub(super) fn lower_body(
BindingAnnotation::new(is_mutable, false),
hygiene,
);
- self_param = Some(binding_id);
+ self_params.push(binding_id);
source_map_self_param =
Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
}
let count = param_list.params().filter(|it| collector.check_cfg(it)).count();
params = (0..count).map(|_| collector.missing_pat()).collect();
};
- let body_expr = collector.missing_expr();
- collector.store.inference_roots = Some(smallvec![(body_expr, RootExprOrigin::BodyRoot)]);
+ collector.with_expr_root(|collector| collector.missing_expr());
let (store, source_map) = collector.store.finish();
return (
- Body { store, params: params.into_boxed_slice(), self_param },
+ Body { store, params: params.into_boxed_slice(), self_params },
BodySourceMap { self_param: source_map_self_param, store: source_map },
);
}
@@ -140,7 +143,7 @@ pub(super) fn lower_body(
BindingAnnotation::new(is_mutable, false),
hygiene,
);
- self_param = Some(binding_id);
+ self_params.push(binding_id);
source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn)));
}
@@ -162,25 +165,29 @@ pub(super) fn lower_body(
}
};
- let body_expr = collector.collect(
- &mut params,
- body,
- if is_async_fn {
- Awaitable::Yes
- } else {
- match owner {
- DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"),
- DefWithBodyId::StaticId(..) => Awaitable::No("static"),
- DefWithBodyId::ConstId(..) => Awaitable::No("constant"),
- DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
- }
- },
- );
- collector.store.inference_roots = Some(smallvec![(body_expr, RootExprOrigin::BodyRoot)]);
+ collector.with_expr_root(|collector| {
+ collector.collect(
+ &mut self_params,
+ &mut params,
+ body,
+ if is_async_fn {
+ Awaitable::Yes
+ } else {
+ match owner {
+ DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"),
+ DefWithBodyId::StaticId(..) => Awaitable::No("static"),
+ DefWithBodyId::ConstId(..) => Awaitable::No("constant"),
+ DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"),
+ }
+ },
+ is_async_fn,
+ is_gen_fn,
+ )
+ });
let (store, source_map) = collector.store.finish();
(
- Body { store, params: params.into_boxed_slice(), self_param },
+ Body { store, params: params.into_boxed_slice(), self_params },
BodySourceMap { self_param: source_map_self_param, store: source_map },
)
}
@@ -190,7 +197,7 @@ pub(crate) fn lower_type_ref(
module: ModuleId,
type_ref: InFile<Option<ast::Type>>,
) -> (ExpressionStore, ExpressionStoreSourceMap, TypeRefId) {
- let mut expr_collector = ExprCollector::signature(db, module, type_ref.file_id);
+ let mut expr_collector = ExprCollector::new(db, module, type_ref.file_id);
let type_ref =
expr_collector.lower_type_ref_opt(type_ref.value, &mut ExprCollector::impl_trait_allocator);
let (store, source_map) = expr_collector.store.finish();
@@ -205,7 +212,7 @@ pub(crate) fn lower_generic_params(
param_list: Option<ast::GenericParamList>,
where_clause: Option<ast::WhereClause>,
) -> (ExpressionStore, GenericParams, ExpressionStoreSourceMap) {
- let mut expr_collector = ExprCollector::signature(db, module, file_id);
+ let mut expr_collector = ExprCollector::new(db, module, file_id);
let mut collector = generics::GenericParamsCollector::new(def);
collector.lower(&mut expr_collector, param_list, where_clause);
let params = collector.finish();
@@ -219,7 +226,7 @@ pub(crate) fn lower_impl(
impl_syntax: InFile<ast::Impl>,
impl_id: ImplId,
) -> (ExpressionStore, ExpressionStoreSourceMap, TypeRefId, Option<TraitRef>, GenericParams) {
- let mut expr_collector = ExprCollector::signature(db, module, impl_syntax.file_id);
+ let mut expr_collector = ExprCollector::new(db, module, impl_syntax.file_id);
let self_ty =
expr_collector.lower_type_ref_opt_disallow_impl_trait(impl_syntax.value.self_ty());
let trait_ = impl_syntax.value.trait_().and_then(|it| match &it {
@@ -247,7 +254,7 @@ pub(crate) fn lower_trait(
trait_syntax: InFile<ast::Trait>,
trait_id: TraitId,
) -> (ExpressionStore, ExpressionStoreSourceMap, GenericParams) {
- let mut expr_collector = ExprCollector::signature(db, module, trait_syntax.file_id);
+ let mut expr_collector = ExprCollector::new(db, module, trait_syntax.file_id);
let mut collector = generics::GenericParamsCollector::with_self_param(
&mut expr_collector,
trait_id.into(),
@@ -270,7 +277,7 @@ pub(crate) fn lower_type_alias(
type_alias_id: TypeAliasId,
) -> (ExpressionStore, ExpressionStoreSourceMap, GenericParams, Box<[TypeBound]>, Option<TypeRefId>)
{
- let mut expr_collector = ExprCollector::signature(db, module, alias.file_id);
+ let mut expr_collector = ExprCollector::new(db, module, alias.file_id);
let bounds = alias
.value
.type_bound_list()
@@ -312,7 +319,7 @@ pub(crate) fn lower_function(
bool,
bool,
) {
- let mut expr_collector = ExprCollector::signature(db, module, fn_.file_id);
+ let mut expr_collector = ExprCollector::new(db, module, fn_.file_id);
let mut collector = generics::GenericParamsCollector::new(function_id.into());
collector.lower(&mut expr_collector, fn_.value.generic_param_list(), fn_.value.where_clause());
let mut params = vec![];
@@ -375,12 +382,20 @@ pub(crate) fn lower_function(
expr_collector.lower_type_ref_opt(ret_type.ty(), &mut ExprCollector::impl_trait_allocator)
});
- let return_type = if fn_.value.async_token().is_some() {
- let path = hir_expand::mod_path::path![core::future::Future];
+ let return_type = if fn_.value.async_token().is_some() || fn_.value.gen_token().is_some() {
+ let (path, assoc_name) =
+ match (fn_.value.async_token().is_some(), fn_.value.gen_token().is_some()) {
+ (true, true) => {
+ (hir_expand::mod_path::path![core::async_iter::AsyncIterator], sym::Item)
+ }
+ (true, false) => (hir_expand::mod_path::path![core::future::Future], sym::Output),
+ (false, true) => (hir_expand::mod_path::path![core::iter::Iterator], sym::Item),
+ (false, false) => unreachable!(),
+ };
let mut generic_args: Vec<_> =
std::iter::repeat_n(None, path.segments().len() - 1).collect();
let binding = AssociatedTypeBinding {
- name: Name::new_symbol_root(sym::Output),
+ name: Name::new_symbol_root(assoc_name),
args: None,
type_ref: Some(
return_type
@@ -531,20 +546,7 @@ impl BindingList {
}
impl<'db> ExprCollector<'db> {
- /// Creates a collector for a signature store, this will populate `const_expr_origins` to any
- /// top level const arg roots.
- pub fn signature(
- db: &dyn DefDatabase,
- module: ModuleId,
- current_file_id: HirFileId,
- ) -> ExprCollector<'_> {
- let mut this = Self::body(db, module, current_file_id);
- this.store.inference_roots = Some(Default::default());
- this
- }
-
- /// Creates a collector for a bidy store.
- pub fn body(
+ pub fn new(
db: &dyn DefDatabase,
module: ModuleId,
current_file_id: HirFileId,
@@ -552,7 +554,7 @@ impl<'db> ExprCollector<'db> {
let (def_map, local_def_map) = module.local_def_map(db);
let expander = Expander::new(db, current_file_id, def_map);
let krate = module.krate(db);
- ExprCollector {
+ let mut result = ExprCollector {
db,
cfg_options: krate.cfg_options(db),
module,
@@ -570,7 +572,9 @@ impl<'db> ExprCollector<'db> {
outer_impl_trait: false,
krate,
name_generator_index: 0,
- }
+ };
+ result.store.inference_roots = Some(SmallVec::new());
+ result
}
fn generate_new_name(&mut self) -> Name {
@@ -585,7 +589,7 @@ impl<'db> ExprCollector<'db> {
}
#[inline]
- pub(crate) fn span_map(&self) -> SpanMapRef<'_> {
+ pub(crate) fn span_map(&self) -> SpanMap<'_> {
self.expander.span_map()
}
@@ -639,9 +643,6 @@ impl<'db> ExprCollector<'db> {
}
ast::Type::ArrayType(inner) => {
let len = self.lower_const_arg_opt(inner.const_arg());
- if let Some(const_expr_origins) = &mut self.store.inference_roots {
- const_expr_origins.push((len.expr, RootExprOrigin::ArrayLength));
- }
TypeRef::Array(ArrayType {
ty: self.lower_type_ref_opt(inner.ty(), impl_trait_lower_fn),
len,
@@ -684,15 +685,13 @@ impl<'db> ExprCollector<'db> {
} else {
Vec::with_capacity(1)
};
- fn lower_abi(abi: ast::Abi) -> Symbol {
- match abi.abi_string() {
- Some(tok) => Symbol::intern(tok.text_without_quotes()),
- // `extern` default to be `extern "C"`.
- _ => sym::C,
- }
+ fn lower_abi(abi: ast::Abi) -> ExternAbi {
+ abi.abi_string()
+ .and_then(|abi| abi.text_without_quotes().parse().ok())
+ .unwrap_or(ExternAbi::FALLBACK)
}
- let abi = inner.abi().map(lower_abi);
+ let abi = inner.abi().map(lower_abi).unwrap_or(ExternAbi::Rust);
params.push((None, ret_ty));
TypeRef::Fn(Box::new(FnType {
is_varargs,
@@ -926,9 +925,6 @@ impl<'db> ExprCollector<'db> {
}
ast::GenericArg::ConstArg(arg) => {
let arg = self.lower_const_arg(arg);
- if let Some(const_expr_origins) = &mut self.store.inference_roots {
- const_expr_origins.push((arg.expr, RootExprOrigin::GenericArgsPath));
- }
args.push(GenericArg::Const(arg))
}
}
@@ -949,46 +945,125 @@ impl<'db> ExprCollector<'db> {
/// into the body. This is to make sure that the future actually owns the
/// arguments that are passed to the function, and to ensure things like
/// drop order are stable.
- fn lower_async_block_with_moved_arguments(
+ fn lower_coroutine_body_with_moved_arguments(
&mut self,
+ self_params: &mut ArrayVec<BindingId, 2>,
params: &mut [PatId],
body: ExprId,
+ kind: CoroutineKind,
coroutine_source: CoroutineSource,
) -> ExprId {
+ // Async function parameters are lowered into the closure body so that they are
+ // captured and so that the drop order matches the equivalent non-async functions.
+ //
+ // from:
+ //
+ // async fn foo(<pattern>: <ty>, <pattern>: <ty>, <pattern>: <ty>) {
+ // <body>
+ // }
+ //
+ // into:
+ //
+ // fn foo(__arg0: <ty>, __arg1: <ty>, __arg2: <ty>) {
+ // async move {
+ // let __arg2 = __arg2;
+ // let <pattern> = __arg2;
+ // let __arg1 = __arg1;
+ // let <pattern> = __arg1;
+ // let __arg0 = __arg0;
+ // let <pattern> = __arg0;
+ // drop-temps { <body> } // see comments later in fn for details
+ // }
+ // }
+ //
+ // If `<pattern>` is a simple ident, then it is lowered to a single
+ // `let <pattern> = <pattern>;` statement as an optimization.
+
let mut statements = Vec::new();
+
+ if let Some(&self_param) = self_params.first() {
+ let Binding { ref name, mode, hygiene, .. } = self.store.bindings[self_param];
+ let name = name.clone();
+ let child_binding_id = self.alloc_binding(name.clone(), mode, hygiene);
+ let child_pat_id =
+ self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None });
+ self.add_definition_to_binding(child_binding_id, child_pat_id);
+ let expr = self.alloc_expr_desugared(Expr::Path(name.into()));
+ if !hygiene.is_root() {
+ self.store.ident_hygiene.insert(expr.into(), hygiene);
+ }
+ statements.push(Statement::Let {
+ pat: child_pat_id,
+ type_ref: None,
+ initializer: Some(expr),
+ else_branch: None,
+ });
+ self_params.push(child_binding_id);
+ }
+
for param in params {
- let (name, hygiene) = match self.store.pats[*param] {
- Pat::Bind { id, .. }
+ let (name, hygiene, is_simple_parameter) = match self.store.pats[*param] {
+ // Check if this is a binding pattern, if so, we can optimize and avoid adding a
+ // `let <pat> = __argN;` statement. In this case, we do not rename the parameter.
+ Pat::Bind { id, subpat: None, .. }
if matches!(
self.store.bindings[id].mode,
BindingAnnotation::Unannotated | BindingAnnotation::Mutable
) =>
{
- // If this is a direct binding, we can leave it as-is, as it'll always be captured anyway.
- continue;
+ (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, true)
}
Pat::Bind { id, .. } => {
// If this is a `ref` binding, we can't leave it as is but we can at least reuse the name, for better display.
- (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene)
+ (self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene, false)
}
- _ => (self.generate_new_name(), HygieneId::ROOT),
+ _ => (self.generate_new_name(), HygieneId::ROOT, false),
};
- let binding_id = self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene);
- let pat_id = self.alloc_pat_desugared(Pat::Bind { id: binding_id, subpat: None });
- let expr = self.alloc_expr_desugared(Expr::Path(name.into()));
+ let pat_syntax = self.store.pat_map_back.get(*param).copied();
+ let child_binding_id =
+ self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene);
+ let child_pat_id =
+ self.alloc_pat_desugared(Pat::Bind { id: child_binding_id, subpat: None });
+ self.add_definition_to_binding(child_binding_id, child_pat_id);
+ if let Some(pat_syntax) = pat_syntax {
+ self.store.pat_map_back.insert(child_pat_id, pat_syntax);
+ }
+ let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into()));
if !hygiene.is_root() {
self.store.ident_hygiene.insert(expr.into(), hygiene);
}
statements.push(Statement::Let {
- pat: *param,
+ pat: child_pat_id,
type_ref: None,
initializer: Some(expr),
else_branch: None,
});
- *param = pat_id;
+ if !is_simple_parameter {
+ let expr = self.alloc_expr_desugared(Expr::Path(name.clone().into()));
+ if !hygiene.is_root() {
+ self.store.ident_hygiene.insert(expr.into(), hygiene);
+ }
+ statements.push(Statement::Let {
+ pat: *param,
+ type_ref: None,
+ initializer: Some(expr),
+ else_branch: None,
+ });
+
+ let parent_binding_id =
+ self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene);
+ let parent_pat_id =
+ self.alloc_pat_desugared(Pat::Bind { id: parent_binding_id, subpat: None });
+ self.add_definition_to_binding(parent_binding_id, parent_pat_id);
+ if let Some(pat_syntax) = pat_syntax {
+ self.store.pat_map_back.insert(parent_pat_id, pat_syntax);
+ }
+ *param = parent_pat_id;
+ }
}
- let async_ = self.async_block(
+ let coroutine = self.desugared_coroutine_expr(
+ kind,
coroutine_source,
// The default capture mode here is by-ref. Later on during upvar analysis,
// we will force the captured arguments to by-move, but for async closures,
@@ -1000,11 +1075,12 @@ impl<'db> ExprCollector<'db> {
Some(body),
);
// It's important that this comes last, see the lowering of async closures for why.
- self.alloc_expr_desugared(async_)
+ self.alloc_expr_desugared(coroutine)
}
- fn async_block(
+ fn desugared_coroutine_expr(
&mut self,
+ kind: CoroutineKind,
source: CoroutineSource,
capture_by: CaptureBy,
id: Option<BlockId>,
@@ -1017,22 +1093,37 @@ impl<'db> ExprCollector<'db> {
arg_types: Box::default(),
ret_type: None,
body: block,
- closure_kind: ClosureKind::AsyncBlock { source },
+ closure_kind: ClosureKind::Coroutine { kind, source },
capture_by,
}
}
fn collect(
&mut self,
+ self_params: &mut ArrayVec<BindingId, 2>,
params: &mut [PatId],
expr: Option<ast::Expr>,
awaitable: Awaitable,
+ is_async_fn: bool,
+ is_gen_fn: bool,
) -> ExprId {
self.awaitable_context.replace(awaitable);
self.with_label_rib(RibKind::Closure, |this| {
let body = this.collect_expr_opt(expr);
- if awaitable == Awaitable::Yes {
- this.lower_async_block_with_moved_arguments(params, body, CoroutineSource::Fn)
+ if is_async_fn || is_gen_fn {
+ let kind = match (is_async_fn, is_gen_fn) {
+ (true, true) => CoroutineKind::AsyncGen,
+ (true, false) => CoroutineKind::Async,
+ (false, true) => CoroutineKind::Gen,
+ (false, false) => unreachable!(),
+ };
+ this.lower_coroutine_body_with_moved_arguments(
+ self_params,
+ params,
+ body,
+ kind,
+ CoroutineSource::Fn,
+ )
} else {
body
}
@@ -1109,17 +1200,17 @@ impl<'db> ExprCollector<'db> {
}
fn lower_const_arg_opt(&mut self, arg: Option<ast::ConstArg>) -> ConstRef {
- let const_expr_origins = self.store.inference_roots.take();
- let r = ConstRef { expr: self.collect_expr_opt(arg.and_then(|it| it.expr())) };
- self.store.inference_roots = const_expr_origins;
- r
+ ConstRef {
+ expr: self.with_fresh_binding_expr_root(|this| {
+ this.collect_expr_opt(arg.and_then(|arg| arg.expr()))
+ }),
+ }
}
pub fn lower_const_arg(&mut self, arg: ast::ConstArg) -> ConstRef {
- let const_expr_origins = self.store.inference_roots.take();
- let r = ConstRef { expr: self.collect_expr_opt(arg.expr()) };
- self.store.inference_roots = const_expr_origins;
- r
+ ConstRef {
+ expr: self.with_fresh_binding_expr_root(|this| this.collect_expr_opt(arg.expr())),
+ }
}
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
@@ -1191,7 +1282,44 @@ impl<'db> ExprCollector<'db> {
self.with_label_rib(RibKind::Closure, |this| {
this.with_awaitable_block(Awaitable::Yes, |this| {
this.collect_block_(e, |this, id, statements, tail| {
- this.async_block(
+ this.desugared_coroutine_expr(
+ CoroutineKind::Async,
+ CoroutineSource::Block,
+ capture_by,
+ id,
+ statements,
+ tail,
+ )
+ })
+ })
+ })
+ }
+ Some(ast::BlockModifier::Gen(_)) => {
+ let capture_by =
+ if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
+ self.with_label_rib(RibKind::Closure, |this| {
+ this.with_awaitable_block(Awaitable::No("non-async gen block"), |this| {
+ this.collect_block_(e, |this, id, statements, tail| {
+ this.desugared_coroutine_expr(
+ CoroutineKind::Gen,
+ CoroutineSource::Block,
+ capture_by,
+ id,
+ statements,
+ tail,
+ )
+ })
+ })
+ })
+ }
+ Some(ast::BlockModifier::AsyncGen(_)) => {
+ let capture_by =
+ if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
+ self.with_label_rib(RibKind::Closure, |this| {
+ this.with_awaitable_block(Awaitable::Yes, |this| {
+ this.collect_block_(e, |this, id, statements, tail| {
+ this.desugared_coroutine_expr(
+ CoroutineKind::AsyncGen,
CoroutineSource::Block,
capture_by,
id,
@@ -1212,14 +1340,6 @@ impl<'db> ExprCollector<'db> {
})
})
}
- // FIXME
- Some(ast::BlockModifier::AsyncGen(_)) => {
- self.with_awaitable_block(Awaitable::Yes, |this| this.collect_block(e))
- }
- Some(ast::BlockModifier::Gen(_)) => self
- .with_awaitable_block(Awaitable::No("non-async gen block"), |this| {
- this.collect_block(e)
- }),
None => self.collect_block(e),
},
ast::Expr::LoopExpr(e) => {
@@ -1347,8 +1467,10 @@ impl<'db> ExprCollector<'db> {
ast::Expr::RecordExpr(e) => {
let path = e
.path()
- .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator))
- .map(Box::new);
+ .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator));
+ let Some(path) = path else {
+ return Some(self.missing_expr());
+ };
let record_lit = if let Some(nfl) = e.record_expr_field_list() {
let fields = nfl
.fields()
@@ -1425,11 +1547,11 @@ impl<'db> ExprCollector<'db> {
}
}
ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
- this.with_binding_owner_and_return(|this| {
+ let mut is_coroutine_closure = false;
+ let closure = this.with_binding_owner_and_return(|this| {
let mut args = Vec::new();
let mut arg_types = Vec::new();
// For coroutine closures, the body, aka. the coroutine is the bindings owner, and not the closure.
- let mut body_is_bindings_owner = false;
if let Some(pl) = e.param_list() {
let num_params = pl.params().count();
args.reserve_exact(num_params);
@@ -1457,25 +1579,38 @@ impl<'db> ExprCollector<'db> {
};
let mut body = this
.with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body()));
-
- let closure_kind = if this.is_lowering_coroutine {
- let movability = if e.static_token().is_some() {
- Movability::Static
+ let kind = {
+ if e.async_token().is_some() && e.gen_token().is_some() {
+ Some(CoroutineKind::AsyncGen)
+ } else if e.async_token().is_some() {
+ Some(CoroutineKind::Async)
+ } else if e.gen_token().is_some() {
+ Some(CoroutineKind::Gen)
} else {
- Movability::Movable
- };
- ClosureKind::Coroutine(movability)
- } else if e.async_token().is_some() {
+ None
+ }
+ };
+
+ let closure_kind = if let Some(kind) = kind {
// It's important that this expr is allocated immediately before the closure.
// We rely on it for `coroutine_for_closure()`.
- body = this.lower_async_block_with_moved_arguments(
+ body = this.lower_coroutine_body_with_moved_arguments(
+ &mut ArrayVec::new(),
&mut args,
body,
+ kind,
CoroutineSource::Closure,
);
- body_is_bindings_owner = true;
+ is_coroutine_closure = true;
- ClosureKind::AsyncClosure
+ ClosureKind::CoroutineClosure(kind)
+ } else if this.is_lowering_coroutine {
+ let movability = if e.static_token().is_some() {
+ Movability::Static
+ } else {
+ Movability::Movable
+ };
+ ClosureKind::OldCoroutine(movability)
} else {
ClosureKind::Closure
};
@@ -1495,8 +1630,23 @@ impl<'db> ExprCollector<'db> {
syntax_ptr,
);
- (if body_is_bindings_owner { body } else { closure }, closure)
- })
+ (if is_coroutine_closure { body } else { closure }, closure)
+ });
+
+ if is_coroutine_closure {
+ let Expr::Closure { args, .. } = &this.store.exprs[closure] else {
+ unreachable!()
+ };
+ for &arg in args {
+ let Pat::Bind { id, .. } = this.store.pats[arg] else {
+ never!("`lower_coroutine_body_with_moved_arguments()` should make sure the coroutine closure only have simple bind args");
+ continue;
+ };
+ this.store.binding_owners.insert(id, closure);
+ }
+ }
+
+ closure
}),
ast::Expr::BinExpr(e) => {
let op = e.op_kind();
@@ -1611,6 +1761,37 @@ impl<'db> ExprCollector<'db> {
})
}
+ /// Whether this path should be lowered as destructuring assignment, or as a normal assignment.
+ fn path_is_destructuring_assignment(&self, path: &ModPath) -> bool {
+ // rustc has access to a full resolver here, including local variables and generic params, and it checks the following
+ // criteria: a path not lowered as destructuring assignment if it can *fully resolve* to something that is *not*
+ // a const, a unit struct or a variant.
+ // We don't have access to a full resolver here. So we should do the same as rustc, but assuming that local variables
+ // could be resolved to nothing (fortunately, there cannot be a local variable shadowing a unit struct/variant/const,
+ // as that is an error). We don't need to consider const params as it's an error to refer to these in patterns.
+ let (resolution, unresolved_idx, _) = self.def_map.resolve_path_locally(
+ self.local_def_map,
+ self.db,
+ self.module,
+ path,
+ BuiltinShadowMode::Other,
+ );
+ match unresolved_idx {
+ Some(_) => {
+ // If `Some(_)`, path could be resolved to unit struct/variant/const with type information, i.e. an assoc type or const.
+ // If `None`, path could be a local variable.
+ resolution.take_types().is_some()
+ }
+ None => match resolution.take_values() {
+ // We don't need to consider non-unit structs/variants, as those are not value types.
+ Some(ModuleDefId::EnumVariantId(_))
+ | Some(ModuleDefId::AdtId(_))
+ | Some(ModuleDefId::ConstId(_)) => true,
+ _ => false,
+ },
+ }
+ }
+
fn collect_expr_as_pat_opt(&mut self, expr: Option<ast::Expr>) -> PatId {
match expr {
Some(expr) => self.collect_expr_as_pat(expr),
@@ -1670,21 +1851,25 @@ impl<'db> ExprCollector<'db> {
let path = collect_path(self, e.expr()?)?;
let path = path
.path()
- .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator))
- .map(Box::new);
+ .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator));
+ let Some(path) = path else {
+ return Some(self.missing_pat());
+ };
let (ellipsis, args) = collect_tuple(self, e.arg_list()?.args());
self.alloc_pat_from_expr(Pat::TupleStruct { path, args, ellipsis }, syntax_ptr)
}
ast::Expr::PathExpr(e) => {
- let (path, hygiene) = self
- .collect_expr_path(e.clone())
- .map(|(path, hygiene)| (Pat::Path(path), hygiene))
- .unwrap_or((Pat::Missing, HygieneId::ROOT));
- let pat_id = self.alloc_pat_from_expr(path, syntax_ptr);
- if !hygiene.is_root() {
- self.store.ident_hygiene.insert(pat_id.into(), hygiene);
+ let (path, hygiene) = self.collect_expr_path(e.clone())?;
+ let mod_path = path.mod_path().expect("should not lower to lang path");
+ if self.path_is_destructuring_assignment(mod_path) {
+ let pat_id = self.alloc_pat_from_expr(Pat::Path(path), syntax_ptr);
+ if !hygiene.is_root() {
+ self.store.ident_hygiene.insert(pat_id.into(), hygiene);
+ }
+ pat_id
+ } else {
+ return None;
}
- pat_id
}
ast::Expr::MacroExpr(e) => {
let e = e.macro_call()?;
@@ -1699,11 +1884,15 @@ impl<'db> ExprCollector<'db> {
ast::Expr::RecordExpr(e) => {
let path = e
.path()
- .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator))
- .map(Box::new);
+ .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator));
+ let Some(path) = path else {
+ return Some(self.missing_pat());
+ };
let record_field_list = e.record_expr_field_list()?;
let ellipsis = record_field_list.dotdot_token().is_some();
- // FIXME: Report an error here if `record_field_list.spread().is_some()`.
+ // We wanted to emit an error here if `record_field_list.spread().is_some()`,
+ // but that's already a syntax error in rustc, so we decided not to.
+ // See https://github.com/rust-lang/rust-analyzer/pull/22206#discussion_r3156097370
let args = record_field_list
.fields()
.filter_map(|f| {
@@ -1954,24 +2143,27 @@ impl<'db> ExprCollector<'db> {
/// ```
fn collect_for_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::ForExpr) -> ExprId {
let lang_items = self.lang_items();
- let into_iter_fn = self.lang_path(lang_items.IntoIterIntoIter);
- let iter_next_fn = self.lang_path(lang_items.IteratorNext);
- let option_some = self.lang_path(lang_items.OptionSome);
- let option_none = self.lang_path(lang_items.OptionNone);
+ let (Some(into_iter_fn), Some(iter_next_fn), Some(option_some), Some(option_none)) = (
+ self.lang_path(lang_items.IntoIterIntoIter),
+ self.lang_path(lang_items.IteratorNext),
+ self.lang_path(lang_items.OptionSome),
+ self.lang_path(lang_items.OptionNone),
+ ) else {
+ return self.missing_expr();
+ };
let head = self.collect_expr_opt(e.iterable());
- let into_iter_fn_expr =
- self.alloc_expr(into_iter_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr);
+ let into_iter_fn_expr = self.alloc_expr(Expr::Path(into_iter_fn), syntax_ptr);
let iterator = self.alloc_expr(
Expr::Call { callee: into_iter_fn_expr, args: Box::new([head]) },
syntax_ptr,
);
let none_arm = MatchArm {
- pat: self.alloc_pat_desugared(option_none.map_or(Pat::Missing, Pat::Path)),
+ pat: self.alloc_pat_desugared(Pat::Path(option_none)),
guard: None,
expr: self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr),
};
let some_pat = Pat::TupleStruct {
- path: option_some.map(Box::new),
+ path: option_some,
args: Box::new([self.collect_pat_top(e.pat())]),
ellipsis: None,
};
@@ -1991,8 +2183,7 @@ impl<'db> ExprCollector<'db> {
Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
syntax_ptr,
);
- let iter_next_fn_expr =
- self.alloc_expr(iter_next_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr);
+ let iter_next_fn_expr = self.alloc_expr(Expr::Path(iter_next_fn), syntax_ptr);
let iter_next_expr = self.alloc_expr(
Expr::Call { callee: iter_next_fn_expr, args: Box::new([iter_expr_mut]) },
syntax_ptr,
@@ -2040,11 +2231,15 @@ impl<'db> ExprCollector<'db> {
/// ```
fn collect_try_operator(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::TryExpr) -> ExprId {
let lang_items = self.lang_items();
- let try_branch = self.lang_path(lang_items.TryTraitBranch);
- let cf_continue = self.lang_path(lang_items.ControlFlowContinue);
- let cf_break = self.lang_path(lang_items.ControlFlowBreak);
+ let (Some(try_branch), Some(cf_continue), Some(cf_break)) = (
+ self.lang_path(lang_items.TryTraitBranch),
+ self.lang_path(lang_items.ControlFlowContinue),
+ self.lang_path(lang_items.ControlFlowBreak),
+ ) else {
+ return self.missing_expr();
+ };
let operand = self.collect_expr_opt(e.expr());
- let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr);
+ let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr);
let expr = self
.alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr);
let continue_name = self.generate_new_name();
@@ -2058,7 +2253,7 @@ impl<'db> ExprCollector<'db> {
self.add_definition_to_binding(continue_binding, continue_bpat);
let continue_arm = MatchArm {
pat: self.alloc_pat_desugared(Pat::TupleStruct {
- path: cf_continue.map(Box::new),
+ path: cf_continue,
args: Box::new([continue_bpat]),
ellipsis: None,
}),
@@ -2072,7 +2267,7 @@ impl<'db> ExprCollector<'db> {
self.add_definition_to_binding(break_binding, break_bpat);
let break_arm = MatchArm {
pat: self.alloc_pat_desugared(Pat::TupleStruct {
- path: cf_break.map(Box::new),
+ path: cf_break,
args: Box::new([break_bpat]),
ellipsis: None,
}),
@@ -2306,7 +2501,7 @@ impl<'db> ExprCollector<'db> {
) -> ExprId {
let block_id = self.expander.ast_id_map().ast_id_for_block(&block).map(|file_local_id| {
let ast_id = self.expander.in_file(file_local_id);
- self.db.intern_block(BlockLoc { ast_id, module: self.module })
+ BlockId::new(self.db, BlockLoc { ast_id, module: self.module })
});
let (module, def_map) =
@@ -2373,9 +2568,7 @@ impl<'db> ExprCollector<'db> {
let Some(pat) = pat else { return self.missing_pat() };
match &pat {
- ast::Pat::IdentPat(bp) => {
- // FIXME: Emit an error if `!bp.is_simple_ident()`.
-
+ ast::Pat::IdentPat(bp) if bp.is_simple_ident() => {
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
let hygiene = bp
.name()
@@ -2387,8 +2580,12 @@ impl<'db> ExprCollector<'db> {
self.add_definition_to_binding(binding, pat);
pat
}
- // FIXME: Emit an error.
- _ => self.missing_pat(),
+ _ => {
+ self.store.diagnostics.push(ExpressionStoreDiagnostics::PatternArgInExternFn {
+ node: self.expander.in_file(AstPtr::new(&pat)),
+ });
+ self.missing_pat()
+ }
}
}
@@ -2465,8 +2662,10 @@ impl<'db> ExprCollector<'db> {
ast::Pat::TupleStructPat(p) => {
let path = p
.path()
- .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator))
- .map(Box::new);
+ .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator));
+ let Some(path) = path else {
+ return self.missing_pat();
+ };
let (args, ellipsis) = self.collect_tuple_pat(
p.fields(),
comma_follows_token(p.l_paren_token()),
@@ -2531,8 +2730,10 @@ impl<'db> ExprCollector<'db> {
ast::Pat::RecordPat(p) => {
let path = p
.path()
- .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator))
- .map(Box::new);
+ .and_then(|path| self.lower_path(path, &mut Self::impl_trait_error_allocator));
+ let Some(path) = path else {
+ return self.missing_pat();
+ };
let record_pat_field_list =
&p.record_pat_field_list().expect("every struct should have a field list");
let args = record_pat_field_list
@@ -2572,19 +2773,15 @@ impl<'db> ExprCollector<'db> {
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
- // to a Pat. Here we are using `Pat::Missing` as a fallback for
- // when `RestPat` is mapped to `Pat`, which can easily happen
- // when the source code being analyzed has a malformed pattern
- // which includes `..` in a place where it isn't valid.
-
- Pat::Missing
- }
+ ast::Pat::RestPat(_) => Pat::Rest,
ast::Pat::BoxPat(boxpat) => {
let inner = self.collect_pat_opt(boxpat.pat(), binding_list);
Pat::Box { inner }
}
+ ast::Pat::DerefPat(inner) => {
+ let inner = self.collect_pat_opt(inner.pat(), binding_list);
+ Pat::Deref { inner }
+ }
ast::Pat::ConstBlockPat(const_block_pat) => {
if let Some(block) = const_block_pat.block_expr() {
let expr_id = self.with_label_rib(RibKind::Constant, |this| {
@@ -2712,7 +2909,7 @@ impl<'db> ExprCollector<'db> {
// endregion: patterns
- /// Returns `None` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `Some(())` when
+ /// Returns `false` (and emits diagnostics) when `owner` if `#[cfg]`d out, and `true` when
/// not.
fn check_cfg(&mut self, owner: &dyn ast::HasAttrs) -> bool {
let enabled = self.expander.is_cfg_enabled(owner, self.cfg_options);
@@ -2756,7 +2953,7 @@ impl<'db> ExprCollector<'db> {
None
} else {
hygiene_id.syntax_context().outer_expn(self.db).map(|expansion| {
- let expansion = self.db.lookup_intern_macro_call(expansion.into());
+ let expansion = hir_expand::MacroCallId::from(expansion).loc(self.db);
(hygiene_id.syntax_context().parent(self.db), expansion.def)
})
};
@@ -2786,7 +2983,7 @@ impl<'db> ExprCollector<'db> {
hygiene_id = HygieneId::new(parent_ctx.opaque_and_semiopaque(self.db));
hygiene_info = parent_ctx.outer_expn(self.db).map(|expansion| {
- let expansion = self.db.lookup_intern_macro_call(expansion.into());
+ let expansion = hir_expand::MacroCallId::from(expansion).loc(self.db);
(parent_ctx.parent(self.db), expansion.def)
});
}
@@ -2897,6 +3094,31 @@ fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)>
}
impl ExprCollector<'_> {
+ fn with_fresh_binding_expr_root(&mut self, f: impl FnOnce(&mut Self) -> ExprId) -> ExprId {
+ self.with_expr_root(|this| this.with_binding_owner(f))
+ }
+
+ fn with_expr_root(&mut self, f: impl FnOnce(&mut Self) -> ExprId) -> ExprId {
+ let inference_roots = self.store.inference_roots.take();
+ let root = f(self);
+ self.store.inference_roots = inference_roots;
+
+ if let Some(inference_roots) = &mut self.store.inference_roots {
+ inference_roots.push(ExprRoot {
+ root,
+ exprs_end: end(&self.store.exprs),
+ pats_end: end(&self.store.pats),
+ bindings_end: end(&self.store.bindings),
+ });
+ }
+
+ return root;
+
+ fn end<T>(arena: &la_arena::Arena<T>) -> la_arena::Idx<T> {
+ la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(arena.len() as u32))
+ }
+ }
+
fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
let src = self.expander.in_file(ptr);
let id = self.store.exprs.alloc(expr);
diff --git a/crates/hir-def/src/expr_store/lower/format_args.rs b/crates/hir-def/src/expr_store/lower/format_args.rs
index 51616afb38..b058ad6d9a 100644
--- a/crates/hir-def/src/expr_store/lower/format_args.rs
+++ b/crates/hir-def/src/expr_store/lower/format_args.rs
@@ -30,12 +30,14 @@ impl<'db> ExprCollector<'db> {
) -> ExprId {
let mut args = FormatArgumentsCollector::default();
f.args().for_each(|arg| {
+ let expr = arg.expr();
args.add(FormatArgument {
kind: match arg.arg_name() {
Some(name) => FormatArgumentKind::Named(Name::new_root(name.name().text())),
None => FormatArgumentKind::Normal,
},
- expr: self.collect_expr_opt(arg.expr()),
+ syntax: expr.as_ref().map(AstPtr::new),
+ expr: self.collect_expr_opt(expr),
});
});
let template = f.template();
@@ -53,8 +55,10 @@ impl<'db> ExprCollector<'db> {
Some(((s, is_direct_literal), template)) => {
let call_ctx = SyntaxContext::root(self.def_map.edition());
let hygiene = self.hygiene_id_for(s.syntax().text_range());
+ let template_ptr = AstPtr::new(&template);
let fmt = format_args::parse(
&s,
+ template_ptr,
fmt_snippet,
args,
is_direct_literal,
@@ -65,10 +69,7 @@ impl<'db> ExprCollector<'db> {
.template_map
.get_or_insert_with(Default::default)
.implicit_capture_to_source
- .insert(
- expr_id,
- self.expander.in_file((AstPtr::new(&template), range)),
- );
+ .insert(expr_id, self.expander.in_file((template_ptr, range)));
}
if !hygiene.is_root() {
self.store.ident_hygiene.insert(expr_id.into(), hygiene);
@@ -340,7 +341,8 @@ impl<'db> ExprCollector<'db> {
expr: args_ident_expr,
name: Name::new_tuple_field(arg_index),
});
- self.make_argument(arg, ty)
+ let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);
+ self.make_argument(arg_ptr, arg, ty)
})
.collect();
let args =
@@ -557,7 +559,8 @@ impl<'db> ExprCollector<'db> {
rawness: Rawness::Ref,
mutability: Mutability::Shared,
});
- self.make_argument(arg, ty)
+ let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);
+ self.make_argument(arg_ptr, arg, ty)
})
.collect();
let array =
@@ -649,7 +652,8 @@ impl<'db> ExprCollector<'db> {
rawness: Rawness::Ref,
mutability: Mutability::Shared,
});
- self.make_argument(ref_arg, ty)
+ let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);
+ self.make_argument(arg_ptr, ref_arg, ty)
})
.collect();
let args =
@@ -716,7 +720,8 @@ impl<'db> ExprCollector<'db> {
expr: args_ident_expr,
name: Name::new_tuple_field(arg_index),
});
- self.make_argument(arg, ty)
+ let arg_ptr = arguments.get(arg_index).and_then(|it| it.syntax);
+ self.make_argument(arg_ptr, arg, ty)
})
.collect();
let array =
@@ -867,11 +872,14 @@ impl<'db> ExprCollector<'db> {
};
let width =
RecordLitField { name: Name::new_symbol_root(sym::width), expr: width_expr };
- self.alloc_expr_desugared(Expr::RecordLit {
- path: self.lang_path(lang_items.FormatPlaceholder).map(Box::new),
- fields: Box::new([position, flags, precision, width]),
- spread: RecordSpread::None,
- })
+ match self.lang_path(lang_items.FormatPlaceholder) {
+ Some(path) => self.alloc_expr_desugared(Expr::RecordLit {
+ path,
+ fields: Box::new([position, flags, precision, width]),
+ spread: RecordSpread::None,
+ }),
+ None => self.missing_expr(),
+ }
} else {
let format_placeholder_new =
self.ty_rel_lang_path_desugared_expr(lang_items.FormatPlaceholder, sym::new);
@@ -973,11 +981,16 @@ impl<'db> ExprCollector<'db> {
/// ```text
/// <core::fmt::Argument>::new_…(arg)
/// ```
- fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId {
+ fn make_argument(
+ &mut self,
+ arg_ptr: Option<AstPtr<ast::Expr>>,
+ arg: ExprId,
+ ty: ArgumentType,
+ ) -> ExprId {
use ArgumentType::*;
use FormatTrait::*;
- let new_fn = self.ty_rel_lang_path_desugared_expr(
+ let new_fn = self.ty_rel_lang_path(
self.lang_items().FormatArgument,
match ty {
Format(Display) => sym::new_display,
@@ -992,6 +1005,22 @@ impl<'db> ExprCollector<'db> {
Usize => sym::from_usize,
},
);
+ let new_fn = match new_fn {
+ Some(new_fn) => {
+ let new_fn = self.store.exprs.alloc(Expr::Path(new_fn));
+ if let Some(arg_ptr) = arg_ptr {
+ // Trait errors (the argument does not implement the expected fmt trait) will show
+ // on this path, so to not end up with synthetic syntax we insert this mapping. We
+ // don't want to insert the other way's mapping in order to not override the source
+ // for the argument.
+ self.store
+ .expr_map_back
+ .insert(new_fn, self.expander.in_file(arg_ptr.wrap_left()));
+ }
+ new_fn
+ }
+ None => self.missing_expr(),
+ };
self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) })
}
diff --git a/crates/hir-def/src/expr_store/lower/generics.rs b/crates/hir-def/src/expr_store/lower/generics.rs
index 5ffc4f5851..7ef9c80de1 100644
--- a/crates/hir-def/src/expr_store/lower/generics.rs
+++ b/crates/hir-def/src/expr_store/lower/generics.rs
@@ -128,15 +128,7 @@ impl GenericParamsCollector {
);
let default = const_param.default_val().map(|it| ec.lower_const_arg(it));
let param = ConstParamData { name, ty, default };
- let idx = self.type_or_consts.alloc(param.into());
- if let Some(default) = default
- && let Some(const_expr_origins) = &mut ec.store.inference_roots
- {
- const_expr_origins.push((
- default.expr,
- crate::expr_store::RootExprOrigin::ConstParam(idx),
- ));
- }
+ self.type_or_consts.alloc(param.into());
}
ast::GenericParam::LifetimeParam(lifetime_param) => {
let lifetime = ec.lower_lifetime_ref_opt(lifetime_param.lifetime());
diff --git a/crates/hir-def/src/expr_store/lower/path.rs b/crates/hir-def/src/expr_store/lower/path.rs
index 579465e10f..236255c404 100644
--- a/crates/hir-def/src/expr_store/lower/path.rs
+++ b/crates/hir-def/src/expr_store/lower/path.rs
@@ -217,7 +217,7 @@ pub(super) fn lower_path(
{
let syn_ctxt = collector.expander.ctx_for_range(path.segment()?.syntax().text_range());
if let Some(macro_call_id) = syn_ctxt.outer_expn(collector.db)
- && collector.db.lookup_intern_macro_call(macro_call_id.into()).def.local_inner
+ && hir_expand::MacroCallId::from(macro_call_id).loc(collector.db).def.local_inner
{
kind = match resolve_crate_root(collector.db, syn_ctxt) {
Some(crate_root) => PathKind::DollarCrate(crate_root),
diff --git a/crates/hir-def/src/expr_store/lower/path/tests.rs b/crates/hir-def/src/expr_store/lower/path/tests.rs
index 6819eb3deb..f507841a91 100644
--- a/crates/hir-def/src/expr_store/lower/path/tests.rs
+++ b/crates/hir-def/src/expr_store/lower/path/tests.rs
@@ -21,7 +21,7 @@ fn lower_path(path: ast::Path) -> (TestDB, ExpressionStore, Option<Path>) {
let (db, file_id) = TestDB::with_single_file("");
let krate = db.fetch_test_crate();
let mut ctx =
- ExprCollector::signature(&db, crate_def_map(&db, krate).root_module_id(), file_id.into());
+ ExprCollector::new(&db, crate_def_map(&db, krate).root_module_id(), file_id.into());
let lowered_path = ctx.lower_path(path, &mut ExprCollector::impl_trait_allocator);
let (store, _) = ctx.store.finish();
(db, store, lowered_path)
diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs
index 70ea54c734..34cedbd728 100644
--- a/crates/hir-def/src/expr_store/pretty.rs
+++ b/crates/hir-def/src/expr_store/pretty.rs
@@ -8,6 +8,7 @@ use std::{
use hir_expand::{Lookup, mod_path::PathKind};
use itertools::Itertools;
+use rustc_abi::ExternAbi;
use span::Edition;
use stdx::never;
use syntax::ast::{HasName, RangeOp};
@@ -17,8 +18,8 @@ use crate::{
attrs::AttrFlags,
expr_store::path::{GenericArg, GenericArgs},
hir::{
- Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, RecordSpread,
- Statement,
+ Array, BindingAnnotation, CaptureBy, ClosureKind, CoroutineKind, Literal, Movability,
+ RecordSpread, Statement,
generics::{GenericParams, WherePredicate},
},
lang_item::LangItemTarget,
@@ -91,7 +92,7 @@ pub fn print_body_hir(
};
if let DefWithBodyId::FunctionId(_) = owner {
p.buf.push('(');
- if let Some(self_param) = body.self_param {
+ if let Some(self_param) = body.self_param() {
p.print_binding(self_param);
p.buf.push_str(", ");
}
@@ -292,7 +293,7 @@ pub fn print_function(
if flags.contains(FnFlags::EXPLICIT_SAFE) {
w!(p, "safe ");
}
- if let Some(abi) = abi {
+ if *abi != ExternAbi::Rust {
w!(p, "extern \"{}\" ", abi.as_str());
}
w!(p, "fn ");
@@ -668,10 +669,7 @@ impl Printer<'_> {
}
}
Expr::RecordLit { path, fields, spread } => {
- match path {
- Some(path) => self.print_path(path),
- None => w!(self, "�"),
- }
+ self.print_path(path);
w!(self, "{{");
let edition = self.edition;
@@ -764,28 +762,36 @@ impl Printer<'_> {
let mut body = *body;
let mut print_pipes = true;
match closure_kind {
- ClosureKind::Coroutine(Movability::Static) => {
+ ClosureKind::OldCoroutine(Movability::Static) => {
w!(self, "static ");
}
- ClosureKind::AsyncClosure => {
+ ClosureKind::CoroutineClosure(kind) => {
if let Expr::Closure {
body: inner_body,
- closure_kind: ClosureKind::AsyncBlock { .. },
+ closure_kind: ClosureKind::Coroutine { .. },
..
} = self.store[body]
{
body = inner_body;
} else {
- never!("async closure should always have an async block body");
+ never!("coroutine closure should always have a coroutine body");
}
- w!(self, "async ");
+ match kind {
+ CoroutineKind::Async => w!(self, "async "),
+ CoroutineKind::Gen => w!(self, "gen "),
+ CoroutineKind::AsyncGen => w!(self, "async gen "),
+ }
}
- ClosureKind::AsyncBlock { .. } => {
- w!(self, "async ");
+ ClosureKind::Coroutine { kind, .. } => {
+ match kind {
+ CoroutineKind::Async => w!(self, "async "),
+ CoroutineKind::Gen => w!(self, "gen "),
+ CoroutineKind::AsyncGen => w!(self, "async gen "),
+ }
print_pipes = false;
}
- ClosureKind::Closure | ClosureKind::Coroutine(Movability::Movable) => (),
+ ClosureKind::Closure | ClosureKind::OldCoroutine(Movability::Movable) => (),
}
match capture_by {
CaptureBy::Value => {
@@ -898,6 +904,7 @@ impl Printer<'_> {
match pat {
Pat::Missing => w!(self, "�"),
+ Pat::Rest => w!(self, ".."),
Pat::Wild => w!(self, "_"),
Pat::Tuple { args, ellipsis } => {
w!(self, "(");
@@ -923,10 +930,7 @@ impl Printer<'_> {
w!(self, ")");
}
Pat::Record { path, args, ellipsis } => {
- match path {
- Some(path) => self.print_path(path),
- None => w!(self, "�"),
- }
+ self.print_path(path);
w!(self, " {{");
let edition = self.edition;
@@ -1004,10 +1008,7 @@ impl Printer<'_> {
}
}
Pat::TupleStruct { path, args, ellipsis } => {
- match path {
- Some(path) => self.print_path(path),
- None => w!(self, "�"),
- }
+ self.print_path(path);
w!(self, "(");
for (i, arg) in args.iter().enumerate() {
if i != 0 {
@@ -1031,6 +1032,11 @@ impl Printer<'_> {
w!(self, "box ");
self.print_pat(*inner);
}
+ Pat::Deref { inner } => {
+ w!(self, "deref!(");
+ self.print_pat(*inner);
+ w!(self, ")");
+ }
Pat::ConstBlock(c) => {
w!(self, "const ");
self.print_expr(*c);
@@ -1310,9 +1316,9 @@ impl Printer<'_> {
if fn_.is_unsafe {
w!(self, "unsafe ");
}
- if let Some(abi) = &fn_.abi {
+ if fn_.abi != ExternAbi::Rust {
w!(self, "extern ");
- w!(self, "{}", abi.as_str());
+ w!(self, "{}", fn_.abi.as_str());
w!(self, " ");
}
w!(self, "fn(");
diff --git a/crates/hir-def/src/expr_store/scope.rs b/crates/hir-def/src/expr_store/scope.rs
index c6ba0241b7..ddb8285137 100644
--- a/crates/hir-def/src/expr_store/scope.rs
+++ b/crates/hir-def/src/expr_store/scope.rs
@@ -7,7 +7,7 @@ use crate::{
db::DefDatabase,
expr_store::{Body, ExpressionStore, HygieneId},
hir::{
- Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement,
+ Array, Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement,
generics::GenericParams,
},
signatures::VariantFields,
@@ -147,11 +147,11 @@ impl ExprScopes {
),
};
let mut root = scopes.root_scope();
- if let Some(self_param) = body.self_param {
+ if let Some(self_param) = body.self_param() {
scopes.add_bindings(body, root, self_param, body.binding_hygiene(self_param));
}
scopes.add_params_bindings(body, root, &body.params);
- compute_expr_scopes(body.root_expr(), body, &mut scopes, &mut root);
+ compute_expr_scopes(body.root_expr(), body, &mut scopes, &mut { root }, &mut root);
scopes
}
@@ -166,7 +166,7 @@ impl ExprScopes {
let root = scopes.root_scope();
for root_expr in roots {
let mut scope = scopes.new_scope(root);
- compute_expr_scopes(root_expr, store, &mut scopes, &mut scope);
+ compute_expr_scopes(root_expr, store, &mut scopes, &mut { scope }, &mut scope);
}
scopes
}
@@ -270,31 +270,33 @@ fn compute_block_scopes(
store: &ExpressionStore,
scopes: &mut ExprScopes,
scope: &mut ScopeId,
+ const_scope: &mut ScopeId,
) {
for stmt in statements {
match stmt {
Statement::Let { pat, initializer, else_branch, .. } => {
if let Some(expr) = initializer {
- compute_expr_scopes(*expr, store, scopes, scope);
+ compute_expr_scopes(*expr, store, scopes, scope, const_scope);
}
if let Some(expr) = else_branch {
- compute_expr_scopes(*expr, store, scopes, scope);
+ compute_expr_scopes(*expr, store, scopes, scope, const_scope);
}
*scope = scopes.new_scope(*scope);
scopes.add_pat_bindings(store, *scope, *pat);
}
Statement::Expr { expr, .. } => {
- compute_expr_scopes(*expr, store, scopes, scope);
+ compute_expr_scopes(*expr, store, scopes, scope, const_scope);
}
Statement::Item(Item::MacroDef(macro_id)) => {
*scope = scopes.new_macro_def_scope(*scope, macro_id.clone());
+ *const_scope = scopes.new_macro_def_scope(*const_scope, macro_id.clone());
}
Statement::Item(Item::Other) => (),
}
}
if let Some(expr) = tail {
- compute_expr_scopes(expr, store, scopes, scope);
+ compute_expr_scopes(expr, store, scopes, scope, const_scope);
}
}
@@ -303,69 +305,86 @@ fn compute_expr_scopes(
store: &ExpressionStore,
scopes: &mut ExprScopes,
scope: &mut ScopeId,
+ const_scope: &mut ScopeId,
) {
- let make_label =
- |label: &Option<LabelId>| label.map(|label| (label, store[label].name.clone()));
+ let make_label = |label: Option<LabelId>| label.map(|label| (label, store[label].name.clone()));
- let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| {
- compute_expr_scopes(expr, store, scopes, scope)
+ let compute_expr_scopes =
+ |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId, const_scope: &mut ScopeId| {
+ compute_expr_scopes(expr, store, scopes, scope, const_scope)
+ };
+ let handle_block = |id,
+ statements,
+ tail,
+ label,
+ scopes: &mut ExprScopes,
+ scope: &mut ScopeId,
+ const_scope: &mut ScopeId| {
+ let mut scope = scopes.new_block_scope(*scope, id, make_label(label));
+ let mut const_scope = if id.is_some() {
+ scopes.new_block_scope(*const_scope, id, None)
+ } else {
+ // We don't need to allocate a new scope, since only items matter to us.
+ *const_scope
+ };
+ // 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, store, scopes, &mut scope, &mut const_scope);
};
scopes.set_scope(expr, *scope);
match &store[expr] {
Expr::Block { statements, tail, id, label } => {
- let mut scope = scopes.new_block_scope(*scope, *id, make_label(label));
- // 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, store, scopes, &mut scope);
+ handle_block(*id, statements, *tail, *label, scopes, scope, const_scope);
}
Expr::Const(id) => {
- let mut scope = scopes.root_scope();
- compute_expr_scopes(scopes, *id, &mut scope);
+ let mut scope = *const_scope;
+ compute_expr_scopes(scopes, *id, &mut scope, const_scope);
+ }
+ Expr::Array(Array::Repeat { initializer, repeat }) => {
+ compute_expr_scopes(scopes, *initializer, scope, const_scope);
+ let mut repeat_scope = *const_scope;
+ compute_expr_scopes(scopes, *repeat, &mut repeat_scope, const_scope);
}
Expr::Unsafe { 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, store, scopes, &mut scope);
+ handle_block(*id, statements, *tail, None, scopes, scope, const_scope);
}
Expr::Loop { body: body_expr, label } => {
- let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
- compute_expr_scopes(scopes, *body_expr, &mut scope);
+ let mut scope = scopes.new_labeled_scope(*scope, make_label(*label));
+ compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope);
}
Expr::Closure { args, body: body_expr, .. } => {
let mut scope = scopes.new_scope(*scope);
scopes.add_params_bindings(store, scope, args);
- compute_expr_scopes(scopes, *body_expr, &mut scope);
+ compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope);
}
Expr::Match { expr, arms } => {
- compute_expr_scopes(scopes, *expr, scope);
+ compute_expr_scopes(scopes, *expr, scope, const_scope);
for arm in arms.iter() {
let mut scope = scopes.new_scope(*scope);
scopes.add_pat_bindings(store, scope, arm.pat);
if let Some(guard) = arm.guard {
scope = scopes.new_scope(scope);
- compute_expr_scopes(scopes, guard, &mut scope);
+ compute_expr_scopes(scopes, guard, &mut scope, const_scope);
}
- compute_expr_scopes(scopes, arm.expr, &mut scope);
+ compute_expr_scopes(scopes, arm.expr, &mut scope, const_scope);
}
}
&Expr::If { condition, then_branch, else_branch } => {
let mut then_branch_scope = scopes.new_scope(*scope);
- compute_expr_scopes(scopes, condition, &mut then_branch_scope);
- compute_expr_scopes(scopes, then_branch, &mut then_branch_scope);
+ compute_expr_scopes(scopes, condition, &mut then_branch_scope, const_scope);
+ compute_expr_scopes(scopes, then_branch, &mut then_branch_scope, const_scope);
if let Some(else_branch) = else_branch {
- compute_expr_scopes(scopes, else_branch, scope);
+ compute_expr_scopes(scopes, else_branch, scope, const_scope);
}
}
&Expr::Let { pat, expr } => {
- compute_expr_scopes(scopes, expr, scope);
+ compute_expr_scopes(scopes, expr, scope, const_scope);
*scope = scopes.new_scope(*scope);
scopes.add_pat_bindings(store, *scope, pat);
}
- _ => store.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)),
+ _ => store.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope, const_scope)),
};
}
diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs
index db12775df9..e97718ca22 100644
--- a/crates/hir-def/src/expr_store/tests/body.rs
+++ b/crates/hir-def/src/expr_store/tests/body.rs
@@ -652,9 +652,16 @@ fn async_fn_weird_param_patterns() {
async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, 123: i32) {}
"#,
expect![[r#"
- fn main(self, param1, mut param2, mut <ra@gennew>0, param4 @ _, mut <ra@gennew>1) async {
+ fn main(self, param1, mut param2, mut <ra@gennew>0, mut param4, mut <ra@gennew>1) async {
+ let self = self;
+ let mut param1 = param1;
+ let mut param2 = param2;
let ref mut param2 = param2;
+ let mut <ra@gennew>0 = <ra@gennew>0;
let _ = <ra@gennew>0;
+ let mut param4 = param4;
+ let param4 @ _ = param4;
+ let mut <ra@gennew>1 = <ra@gennew>1;
let 123 = <ra@gennew>1;
{}
}"#]],
diff --git a/crates/hir-def/src/expr_store/tests/signatures.rs b/crates/hir-def/src/expr_store/tests/signatures.rs
index 5e0184dfad..24bdc6ece3 100644
--- a/crates/hir-def/src/expr_store/tests/signatures.rs
+++ b/crates/hir-def/src/expr_store/tests/signatures.rs
@@ -210,3 +210,17 @@ fn foo(v: for<'a> Trait1 + Trait2) {}
"#]],
);
}
+
+#[test]
+fn extern_block_abi() {
+ lower_and_print(
+ r#"
+extern "C" {
+ fn extern_fn();
+}
+ "#,
+ expect![[r#"
+ extern "C" fn extern_fn() {...}
+ "#]],
+ );
+}
diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs
index 8308203693..2a1b7f7cb0 100644
--- a/crates/hir-def/src/find_path.rs
+++ b/crates/hir-def/src/find_path.rs
@@ -177,7 +177,7 @@ fn find_path_for_module(
path: ModPath::from_segments(PathKind::Crate, None),
path_text_len: 5,
stability: Stable,
- prefer_due_to_prelude: false,
+ has_prelude_segment: false,
});
}
// - otherwise if the item is the crate root of a dependency crate, return the name from the extern prelude
@@ -205,7 +205,7 @@ fn find_path_for_module(
} else {
PathKind::Plain
};
- return Some(Choice::new(ctx.cfg.prefer_prelude, kind, name.clone(), Stable));
+ return Some(Choice::new(kind, name.clone(), Stable));
}
}
@@ -223,12 +223,7 @@ fn find_path_for_module(
);
if let Some(scope_name) = scope_name {
// - if the item is already in scope, return the name under which it is
- return Some(Choice::new(
- ctx.cfg.prefer_prelude,
- ctx.prefix.path_kind(),
- scope_name,
- Stable,
- ));
+ return Some(Choice::new(ctx.prefix.path_kind(), scope_name, Stable));
}
}
@@ -240,7 +235,7 @@ fn find_path_for_module(
path: ModPath::from_segments(kind, None),
path_text_len: path_kind_len(kind),
stability: Stable,
- prefer_due_to_prelude: false,
+ has_prelude_segment: false,
});
}
@@ -302,7 +297,7 @@ fn find_in_prelude(
});
if found_and_same_def.unwrap_or(true) {
- Some(Choice::new(false, PathKind::Plain, name.clone(), Stable))
+ Some(Choice::new(PathKind::Plain, name.clone(), Stable))
} else {
None
}
@@ -439,13 +434,7 @@ fn find_in_dep(
// Determine best path for containing module and append last segment from `info`.
// FIXME: we should guide this to look up the path locally, or from the same crate again?
- let choice = find_path_for_module(
- ctx,
- visited_modules,
- info.container,
- true,
- best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1,
- );
+ let choice = find_path_for_module(ctx, visited_modules, info.container, true, max_len - 1);
let Some(mut choice) = choice else {
continue;
};
@@ -471,15 +460,11 @@ fn calculate_best_path_local(
) {
// FIXME: cache the `find_local_import_locations` output?
find_local_import_locations(ctx, item, visited_modules, |visited_modules, name, module_id| {
- // we are looking for paths of length up to best_path_len, any longer will make it be
- // less optimal. The -1 is due to us pushing name onto it afterwards.
- if let Some(choice) = find_path_for_module(
- ctx,
- visited_modules,
- module_id,
- false,
- best_choice.as_ref().map_or(max_len, |it| it.path.len()) - 1,
- ) {
+ // The container path may be at most `max_len - 1` segments since we push
+ // `name` on top of it.
+ if let Some(choice) =
+ find_path_for_module(ctx, visited_modules, module_id, false, max_len - 1)
+ {
Choice::try_select(best_choice, choice, ctx.cfg.prefer_prelude, name.clone());
}
});
@@ -492,23 +477,23 @@ struct Choice {
path_text_len: usize,
/// The stability of the path
stability: Stability,
- /// Whether this path contains a prelude segment and preference for it has been signaled
- prefer_due_to_prelude: bool,
+ /// Whether any segment of this path is named `prelude`
+ has_prelude_segment: bool,
}
impl Choice {
- fn new(prefer_prelude: bool, kind: PathKind, name: Name, stability: Stability) -> Self {
+ fn new(kind: PathKind, name: Name, stability: Stability) -> Self {
Self {
path_text_len: path_kind_len(kind) + name.as_str().len(),
stability,
- prefer_due_to_prelude: prefer_prelude && name == sym::prelude,
+ has_prelude_segment: name == sym::prelude,
path: ModPath::from_segments(kind, iter::once(name)),
}
}
- fn push(mut self, prefer_prelude: bool, name: Name) -> Self {
+ fn push(mut self, name: Name) -> Self {
self.path_text_len += name.as_str().len();
- self.prefer_due_to_prelude |= prefer_prelude && name == sym::prelude;
+ self.has_prelude_segment |= name == sym::prelude;
self.path.push_segment(name);
self
}
@@ -520,13 +505,19 @@ impl Choice {
name: Name,
) {
let Some(current) = current else {
- *current = Some(other.push(prefer_prelude, name));
+ *current = Some(other.push(name));
return;
};
match other
.stability
.cmp(&current.stability)
- .then_with(|| other.prefer_due_to_prelude.cmp(&current.prefer_due_to_prelude))
+ .then_with(|| {
+ if prefer_prelude {
+ other.has_prelude_segment.cmp(&current.has_prelude_segment)
+ } else {
+ current.has_prelude_segment.cmp(&other.has_prelude_segment)
+ }
+ })
.then_with(|| (current.path.len()).cmp(&(other.path.len() + 1)))
{
Ordering::Less => return,
@@ -684,7 +675,7 @@ mod tests {
let ast_path =
parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
let mod_path = ModPath::from_src(&db, ast_path, &mut |range| {
- db.span_map(pos.file_id.into()).as_ref().span_for_range(range).ctx
+ db.span_map(pos.file_id.into()).span_for_range(range).ctx
})
.unwrap();
@@ -2032,6 +2023,50 @@ pub mod foo {
}
#[test]
+ fn avoids_prelude_when_prefer_prelude_false() {
+ let ra_fixture = r#"
+//- /main.rs crate:main deps:krate
+$0
+//- /krate.rs crate:krate
+pub mod prelude {
+ pub use crate::module::sub::*;
+}
+pub mod module {
+ pub mod sub {
+ pub struct Foo;
+ }
+}
+"#;
+ // krate::prelude::Foo (3 segs) is shorter than krate::module::sub::Foo (4 segs),
+ // but prefer_prelude=false should pick the longer canonical path.
+ check_found_path(
+ ra_fixture,
+ "krate::module::sub::Foo",
+ expect![[r#"
+ Plain (imports ✔): krate::module::sub::Foo
+ Plain (imports ✖): krate::module::sub::Foo
+ ByCrate(imports ✔): krate::module::sub::Foo
+ ByCrate(imports ✖): krate::module::sub::Foo
+ BySelf (imports ✔): krate::module::sub::Foo
+ BySelf (imports ✖): krate::module::sub::Foo
+ "#]],
+ );
+ // prefer_prelude=true should still pick the shorter prelude path.
+ check_found_path_prelude(
+ ra_fixture,
+ "krate::prelude::Foo",
+ expect![[r#"
+ Plain (imports ✔): krate::prelude::Foo
+ Plain (imports ✖): krate::prelude::Foo
+ ByCrate(imports ✔): krate::prelude::Foo
+ ByCrate(imports ✖): krate::prelude::Foo
+ BySelf (imports ✔): krate::prelude::Foo
+ BySelf (imports ✖): krate::prelude::Foo
+ "#]],
+ );
+ }
+
+ #[test]
fn respects_absolute_setting() {
let ra_fixture = r#"
//- /main.rs crate:main deps:krate
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index 9e51d0eac9..6eba859264 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -259,7 +259,7 @@ pub enum Expr {
expr: Option<ExprId>,
},
RecordLit {
- path: Option<Box<Path>>,
+ path: Path,
fields: Box<[RecordLitField]>,
spread: RecordSpread,
},
@@ -524,12 +524,19 @@ pub enum InlineAsmRegOrRegClass {
RegClass(Symbol),
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum CoroutineKind {
+ Async,
+ Gen,
+ AsyncGen,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ClosureKind {
Closure,
- Coroutine(Movability),
- AsyncBlock { source: CoroutineSource },
- AsyncClosure,
+ OldCoroutine(Movability),
+ Coroutine { kind: CoroutineKind, source: CoroutineSource },
+ CoroutineClosure(CoroutineKind),
}
/// In the case of a coroutine created as part of an async/gen construct,
@@ -557,7 +564,7 @@ pub enum CaptureBy {
Ref,
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Movability {
Static,
Movable,
@@ -666,6 +673,8 @@ pub struct RecordFieldPat {
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Pat {
Missing,
+ /// A rest pattern. Not valid outside special context.
+ Rest,
Wild,
Tuple {
args: Box<[PatId]>,
@@ -673,7 +682,7 @@ pub enum Pat {
},
Or(Box<[PatId]>),
Record {
- path: Option<Box<Path>>,
+ path: Path,
args: Box<[RecordFieldPat]>,
ellipsis: bool,
},
@@ -687,7 +696,6 @@ pub enum Pat {
slice: Option<PatId>,
suffix: Box<[PatId]>,
},
- /// This might refer to a variable if a single segment path (specifically, on destructuring assignment).
Path(Path),
Lit(ExprId),
Bind {
@@ -695,7 +703,7 @@ pub enum Pat {
subpat: Option<PatId>,
},
TupleStruct {
- path: Option<Box<Path>>,
+ path: Path,
args: Box<[PatId]>,
ellipsis: Option<u32>,
},
@@ -706,6 +714,9 @@ pub enum Pat {
Box {
inner: PatId,
},
+ Deref {
+ inner: PatId,
+ },
ConstBlock(ExprId),
/// An expression inside a pattern. That can only occur inside assignments.
///
@@ -722,6 +733,7 @@ impl Pat {
| Pat::ConstBlock(..)
| Pat::Wild
| Pat::Missing
+ | Pat::Rest
| Pat::Expr(_) => {}
Pat::Bind { subpat, .. } => {
subpat.iter().copied().for_each(f);
@@ -737,7 +749,7 @@ impl Pat {
Pat::Record { args, .. } => {
args.iter().map(|f| f.pat).for_each(f);
}
- Pat::Box { inner } => f(*inner),
+ Pat::Box { inner } | Pat::Deref { inner } => f(*inner),
}
}
}
diff --git a/crates/hir-def/src/hir/format_args.rs b/crates/hir-def/src/hir/format_args.rs
index 271484da7b..366857f233 100644
--- a/crates/hir-def/src/hir/format_args.rs
+++ b/crates/hir-def/src/hir/format_args.rs
@@ -7,7 +7,7 @@ use rustc_parse_format as parse;
use span::SyntaxContext;
use stdx::TupleExt;
use syntax::{
- TextRange,
+ AstPtr, TextRange,
ast::{self, IsString},
};
@@ -146,6 +146,7 @@ pub enum FormatCount {
pub struct FormatArgument {
pub kind: FormatArgumentKind,
pub expr: ExprId,
+ pub syntax: Option<AstPtr<ast::Expr>>,
}
#[derive(Clone, PartialEq, Eq, Debug)]
@@ -171,6 +172,7 @@ use PositionUsedAs::*;
#[allow(clippy::unnecessary_lazy_evaluations)]
pub(crate) fn parse(
s: &ast::String,
+ string_ptr: AstPtr<ast::Expr>,
fmt_snippet: Option<String>,
mut args: FormatArgumentsCollector,
is_direct_literal: bool,
@@ -273,6 +275,11 @@ pub(crate) fn parse(
// FIXME: This is problematic, we might want to synthesize a dummy
// expression proper and/or desugar these.
expr: synth(name, span),
+ // FIXME: This will lead us to show failed trait bounds (e.g. `T: Display`)
+ // on the whole template string for implicit arguments instead of just their name.
+ // Fixing this is hard since we don't have an `AstPtr` for the arg, and it's
+ // only part of an expression.
+ syntax: Some(string_ptr),
}))
}
}
diff --git a/crates/hir-def/src/hir/generics.rs b/crates/hir-def/src/hir/generics.rs
index 43dd7d1c54..04ccc7bf90 100644
--- a/crates/hir-def/src/hir/generics.rs
+++ b/crates/hir-def/src/hir/generics.rs
@@ -188,6 +188,11 @@ impl GenericParams {
pub const SELF_PARAM_ID_IN_SELF: la_arena::Idx<TypeOrConstParamData> =
LocalTypeOrConstParamId::from_raw(RawIdx::from_u32(0));
+ #[inline]
+ pub fn empty() -> &'static GenericParams {
+ LazyLock::force(&EMPTY)
+ }
+
pub fn of(db: &dyn DefDatabase, def: GenericDefId) -> &GenericParams {
Self::with_store(db, def).0
}
diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs
index b64199fa26..43e29c1f5f 100644
--- a/crates/hir-def/src/hir/type_ref.rs
+++ b/crates/hir-def/src/hir/type_ref.rs
@@ -2,16 +2,13 @@
//! be directly created from an ast::TypeRef, without further queries.
use hir_expand::name::Name;
-use intern::Symbol;
use la_arena::Idx;
+use rustc_abi::ExternAbi;
use thin_vec::ThinVec;
use crate::{
LifetimeParamId, TypeParamId,
- expr_store::{
- ExpressionStore,
- path::{GenericArg, Path},
- },
+ expr_store::{ExpressionStore, path::Path},
hir::ExprId,
};
@@ -100,7 +97,7 @@ pub struct FnType {
pub params: Box<[(Option<Name>, TypeRefId)]>,
pub is_varargs: bool,
pub is_unsafe: bool,
- pub abi: Option<Symbol>,
+ pub abi: ExternAbi,
}
impl FnType {
@@ -191,71 +188,6 @@ impl TypeRef {
pub(crate) fn unit() -> TypeRef {
TypeRef::Tuple(ThinVec::new())
}
-
- pub fn walk(this: TypeRefId, map: &ExpressionStore, f: &mut impl FnMut(TypeRefId, &TypeRef)) {
- go(this, f, map);
-
- fn go(
- type_ref_id: TypeRefId,
- f: &mut impl FnMut(TypeRefId, &TypeRef),
- map: &ExpressionStore,
- ) {
- let type_ref = &map[type_ref_id];
- f(type_ref_id, type_ref);
- match type_ref {
- TypeRef::Fn(fn_) => {
- fn_.params.iter().for_each(|&(_, param_type)| go(param_type, f, map))
- }
- TypeRef::Tuple(types) => types.iter().for_each(|&t| go(t, f, map)),
- TypeRef::RawPtr(type_ref, _) | TypeRef::Slice(type_ref) => go(*type_ref, f, map),
- TypeRef::Reference(it) => go(it.ty, f, map),
- TypeRef::Array(it) => go(it.ty, f, map),
- TypeRef::ImplTrait(bounds) | TypeRef::DynTrait(bounds) => {
- for bound in bounds {
- match bound {
- &TypeBound::Path(path, _) | &TypeBound::ForLifetime(_, path) => {
- go_path(&map[path], f, map)
- }
- TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (),
- }
- }
- }
- TypeRef::Path(path) => go_path(path, f, map),
- TypeRef::Never | TypeRef::Placeholder | TypeRef::Error | TypeRef::TypeParam(_) => {}
- };
- }
-
- fn go_path(path: &Path, f: &mut impl FnMut(TypeRefId, &TypeRef), map: &ExpressionStore) {
- if let Some(type_ref) = path.type_anchor() {
- go(type_ref, f, map);
- }
- for segment in path.segments().iter() {
- if let Some(args_and_bindings) = segment.args_and_bindings {
- for arg in args_and_bindings.args.iter() {
- match arg {
- GenericArg::Type(type_ref) => {
- go(*type_ref, f, map);
- }
- GenericArg::Const(_) | GenericArg::Lifetime(_) => {}
- }
- }
- for binding in args_and_bindings.bindings.iter() {
- if let Some(type_ref) = binding.type_ref {
- go(type_ref, f, map);
- }
- for bound in binding.bounds.iter() {
- match bound {
- &TypeBound::Path(path, _) | &TypeBound::ForLifetime(_, path) => {
- go_path(&map[path], f, map)
- }
- TypeBound::Lifetime(_) | TypeBound::Error | TypeBound::Use(_) => (),
- }
- }
- }
- }
- }
- }
- }
}
impl TypeBound {
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index e7ab2b390f..4b50161c04 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -61,7 +61,6 @@ use span::{
use stdx::never;
use syntax::{SourceFile, SyntaxKind, ast, match_ast};
use thin_vec::ThinVec;
-use triomphe::Arc;
use tt::TextRange;
use crate::{BlockId, Lookup, attrs::parse_extra_crate_attrs, db::DefDatabase};
@@ -121,14 +120,30 @@ fn lower_extra_crate_attrs<'a>(
AttrsOrCfg::lower(db, &crate_attrs_as_src, cfg_options, span_map)
}
-#[salsa_macros::tracked(returns(deref))]
-pub(crate) fn file_item_tree_query(
+pub(crate) fn file_item_tree(db: &dyn DefDatabase, file_id: HirFileId, krate: Crate) -> &ItemTree {
+ match file_item_tree_query(db, file_id, krate) {
+ Some(item_tree) => item_tree,
+ None => {
+ static EMPTY: OnceLock<ItemTree> = OnceLock::new();
+ EMPTY.get_or_init(|| ItemTree {
+ top_level: Box::new([]),
+ attrs: FxHashMap::default(),
+ small_data: FxHashMap::default(),
+ big_data: FxHashMap::default(),
+ top_attrs: AttrsOrCfg::empty(),
+ vis: ItemVisibilities { arena: ThinVec::new() },
+ })
+ }
+ }
+}
+
+#[salsa_macros::tracked(returns(ref))]
+fn file_item_tree_query(
db: &dyn DefDatabase,
file_id: HirFileId,
krate: Crate,
-) -> Arc<ItemTree> {
+) -> Option<Box<ItemTree>> {
let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered();
- static EMPTY: OnceLock<Arc<ItemTree>> = OnceLock::new();
let ctx = lower::Ctx::new(db, file_id, krate);
let syntax = db.parse_or_expand(file_id);
@@ -174,21 +189,10 @@ pub(crate) fn file_item_tree_query(
&& top_attrs.is_empty()
&& vis.arena.is_empty()
{
- EMPTY
- .get_or_init(|| {
- Arc::new(ItemTree {
- top_level: Box::new([]),
- attrs: FxHashMap::default(),
- small_data: FxHashMap::default(),
- big_data: FxHashMap::default(),
- top_attrs: AttrsOrCfg::empty(),
- vis: ItemVisibilities { arena: ThinVec::new() },
- })
- })
- .clone()
+ None
} else {
item_tree.shrink_to_fit();
- Arc::new(item_tree)
+ Some(Box::new(item_tree))
}
}
@@ -343,7 +347,7 @@ impl TreeId {
pub(crate) fn item_tree<'db>(&self, db: &'db dyn DefDatabase, krate: Crate) -> &'db ItemTree {
match self.block {
Some(block) => block_item_tree_query(db, block, krate),
- None => file_item_tree_query(db, self.file, krate),
+ None => file_item_tree(db, self.file, krate),
}
}
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index 31e409d86e..91c2e60fd7 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -4,12 +4,7 @@ use std::cell::OnceCell;
use base_db::{Crate, FxIndexSet};
use cfg::CfgOptions;
-use hir_expand::{
- HirFileId,
- mod_path::PathKind,
- name::AsName,
- span_map::{SpanMap, SpanMapRef},
-};
+use hir_expand::{HirFileId, mod_path::PathKind, name::AsName, span_map::SpanMap};
use la_arena::Arena;
use span::{AstIdMap, FileAstId, SyntaxContext};
use syntax::{
@@ -32,7 +27,7 @@ pub(super) struct Ctx<'db> {
pub(super) db: &'db dyn DefDatabase,
tree: ItemTree,
source_ast_id_map: &'db AstIdMap,
- span_map: OnceCell<SpanMap>,
+ span_map: OnceCell<SpanMap<'db>>,
file: HirFileId,
cfg_options: OnceCell<&'db CfgOptions>,
krate: Crate,
@@ -60,12 +55,12 @@ impl<'db> Ctx<'db> {
self.cfg_options.get_or_init(|| self.krate.cfg_options(self.db))
}
- pub(super) fn span_map(&self) -> SpanMapRef<'_> {
- self.span_map.get_or_init(|| self.db.span_map(self.file)).as_ref()
+ pub(super) fn span_map(&self) -> SpanMap<'_> {
+ *self.span_map.get_or_init(|| self.db.span_map(self.file))
}
pub(super) fn lower_module_items(mut self, item_owner: &dyn HasModuleItem) -> ItemTree {
- self.top_level = item_owner.items().flat_map(|item| self.lower_mod_item(&item)).collect();
+ self.top_level = item_owner.items().filter_map(|item| self.lower_mod_item(&item)).collect();
self.tree.vis.arena = self.visibilities.into_iter().collect();
self.tree.top_level = self.top_level.into_boxed_slice();
self.tree
@@ -89,7 +84,7 @@ impl<'db> Ctx<'db> {
_ => None,
}
})
- .flat_map(|item| self.lower_mod_item(&item))
+ .filter_map(|item| self.lower_mod_item(&item))
.collect();
if let Some(ast::Expr::MacroExpr(tail_macro)) = stmts.expr()
@@ -245,7 +240,9 @@ impl<'db> Ctx<'db> {
ModKind::Inline {
items: module
.item_list()
- .map(|list| list.items().flat_map(|item| self.lower_mod_item(&item)).collect())
+ .map(|list| {
+ list.items().filter_map(|item| self.lower_mod_item(&item)).collect()
+ })
.unwrap_or_else(|| {
cov_mark::hit!(name_res_works_for_broken_modules);
Box::new([]) as Box<[_]>
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index 37d70b1e33..c2cbb9eda7 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -7,8 +7,8 @@ use intern::{Symbol, sym};
use stdx::impl_from;
use crate::{
- AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, MacroId,
- ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
+ AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId,
+ ItemContainerId, MacroId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
attrs::AttrFlags,
db::DefDatabase,
nameres::{DefMap, assoc::TraitItems, crate_def_map, crate_local_def_map},
@@ -40,7 +40,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt
let crate_def_map = crate_def_map(db, krate);
- if !crate_def_map.is_unstable_feature_enabled(&sym::lang_items) {
+ if !crate_def_map.features().lang_items {
return None;
}
@@ -102,6 +102,8 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt
lang_items.fill_non_lang_core_items(db, crate_def_map);
}
+ lang_items.resolve_manually(db);
+
if lang_items.is_empty() { None } else { Some(Box::new(lang_items)) }
}
@@ -190,6 +192,122 @@ fn resolve_core_macro(
current.scope.makro(&Name::new_symbol_root(name))
}
+impl LangItems {
+ fn resolve_manually(&mut self, db: &dyn DefDatabase) {
+ let parent_trait =
+ |lang_item: &mut Option<TraitId>, def: Option<FunctionId>| match def?.loc(db).container
+ {
+ ItemContainerId::TraitId(trait_) => {
+ *lang_item = Some(trait_);
+ Some(trait_)
+ }
+ _ => None,
+ };
+ let assoc_types =
+ |trait_: TraitId, assoc_types: &mut [(&mut Option<TypeAliasId>, Symbol)]| {
+ let trait_items = trait_.trait_items(db);
+ for (assoc_type, name) in assoc_types {
+ **assoc_type =
+ trait_items.associated_type_by_name(&Name::new_symbol_root(name.clone()));
+ }
+ };
+ let methods = |trait_: TraitId, assoc_types: &mut [(&mut Option<FunctionId>, Symbol)]| {
+ let trait_items = trait_.trait_items(db);
+ for (assoc_type, name) in assoc_types {
+ **assoc_type = trait_items.method_by_name(&Name::new_symbol_root(name.clone()));
+ }
+ };
+ (|| {
+ let into_future = parent_trait(&mut self.IntoFuture, self.IntoFutureIntoFuture)?;
+ assoc_types(into_future, &mut [(&mut self.IntoFutureOutput, sym::Output)]);
+ Some(())
+ })();
+
+ (|| {
+ let into_iterator = parent_trait(&mut self.IntoIterator, self.IntoIterIntoIter)?;
+ assoc_types(
+ into_iterator,
+ &mut [
+ (&mut self.IntoIteratorItem, sym::Item),
+ (&mut self.IntoIterIntoIterType, sym::IntoIter),
+ ],
+ );
+ Some(())
+ })();
+
+ (|| {
+ assoc_types(self.Iterator?, &mut [(&mut self.IteratorItem, sym::Item)]);
+ Some(())
+ })();
+
+ (|| {
+ assoc_types(self.AsyncIterator?, &mut [(&mut self.AsyncIteratorItem, sym::Item)]);
+ Some(())
+ })();
+
+ for (op_trait, op_method, op_method_name) in [
+ (self.Fn, &mut self.Fn_call, sym::call),
+ (self.FnMut, &mut self.FnMut_call_mut, sym::call_mut),
+ (self.FnOnce, &mut self.FnOnce_call_once, sym::call_once),
+ (self.AsyncFn, &mut self.AsyncFn_async_call, sym::async_call),
+ (self.AsyncFnMut, &mut self.AsyncFnMut_async_call_mut, sym::async_call_mut),
+ (self.AsyncFnOnce, &mut self.AsyncFnOnce_async_call_once, sym::async_call_once),
+ (self.Not, &mut self.Not_not, sym::not),
+ (self.Neg, &mut self.Neg_neg, sym::neg),
+ (self.Add, &mut self.Add_add, sym::add),
+ (self.Mul, &mut self.Mul_mul, sym::mul),
+ (self.Sub, &mut self.Sub_sub, sym::sub),
+ (self.Div, &mut self.Div_div, sym::div),
+ (self.Rem, &mut self.Rem_rem, sym::rem),
+ (self.Shl, &mut self.Shl_shl, sym::shl),
+ (self.Shr, &mut self.Shr_shr, sym::shr),
+ (self.BitXor, &mut self.BitXor_bitxor, sym::bitxor),
+ (self.BitOr, &mut self.BitOr_bitor, sym::bitor),
+ (self.BitAnd, &mut self.BitAnd_bitand, sym::bitand),
+ (self.AddAssign, &mut self.AddAssign_add_assign, sym::add_assign),
+ (self.MulAssign, &mut self.MulAssign_mul_assign, sym::mul_assign),
+ (self.SubAssign, &mut self.SubAssign_sub_assign, sym::sub_assign),
+ (self.DivAssign, &mut self.DivAssign_div_assign, sym::div_assign),
+ (self.RemAssign, &mut self.RemAssign_rem_assign, sym::rem_assign),
+ (self.ShlAssign, &mut self.ShlAssign_shl_assign, sym::shl_assign),
+ (self.ShrAssign, &mut self.ShrAssign_shr_assign, sym::shr_assign),
+ (self.BitXorAssign, &mut self.BitXorAssign_bitxor_assign, sym::bitxor_assign),
+ (self.BitOrAssign, &mut self.BitOrAssign_bitor_assign, sym::bitor_assign),
+ (self.BitAndAssign, &mut self.BitAndAssign_bitand_assign, sym::bitand_assign),
+ (self.Drop, &mut self.Drop_drop, sym::drop),
+ (self.Debug, &mut self.Debug_fmt, sym::fmt),
+ (self.Deref, &mut self.Deref_deref, sym::deref),
+ (self.DerefMut, &mut self.DerefMut_deref_mut, sym::deref_mut),
+ (self.Index, &mut self.Index_index, sym::index),
+ (self.IndexMut, &mut self.IndexMut_index_mut, sym::index_mut),
+ ] {
+ (|| {
+ methods(op_trait?, &mut [(op_method, op_method_name)]);
+ Some(())
+ })();
+ }
+ (|| {
+ methods(
+ self.PartialEq?,
+ &mut [(&mut self.PartialEq_eq, sym::eq), (&mut self.PartialEq_ne, sym::ne)],
+ );
+ Some(())
+ })();
+ (|| {
+ methods(
+ self.PartialOrd?,
+ &mut [
+ (&mut self.PartialOrd_le, sym::le),
+ (&mut self.PartialOrd_lt, sym::lt),
+ (&mut self.PartialOrd_ge, sym::ge),
+ (&mut self.PartialOrd_gt, sym::gt),
+ ],
+ );
+ Some(())
+ })();
+ }
+}
+
#[salsa::tracked(returns(as_deref))]
pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option<Box<[TraitId]>> {
let mut traits = Vec::new();
@@ -221,6 +339,10 @@ macro_rules! language_item_table {
@non_lang_core_macros:
$( core::$($non_lang_macro_module:ident)::*, $non_lang_macro:ident, $non_lang_macro_field:ident; )*
+
+ @resolve_manually:
+
+ $( $resolve_manually:ident, $resolve_manually_type:ident; )*
) => {
#[allow(non_snake_case)] // FIXME: Should we remove this?
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
@@ -235,6 +357,9 @@ macro_rules! language_item_table {
$(
pub $non_lang_macro_field: Option<MacroId>,
)*
+ $(
+ pub $resolve_manually: Option<$resolve_manually_type>,
+ )*
}
impl LangItems {
@@ -247,6 +372,7 @@ macro_rules! language_item_table {
$( self.$lang_item = self.$lang_item.or(other.$lang_item); )*
$( self.$non_lang_trait = self.$non_lang_trait.or(other.$non_lang_trait); )*
$( self.$non_lang_macro_field = self.$non_lang_macro_field.or(other.$non_lang_macro_field); )*
+ $( self.$resolve_manually = self.$resolve_manually.or(other.$resolve_manually); )*
}
fn assign_lang_item(&mut self, name: Symbol, target: LangItemTarget) {
@@ -365,9 +491,10 @@ language_item_table! { LangItems =>
Deref, sym::deref, TraitId;
DerefMut, sym::deref_mut, TraitId;
+ DerefPure, sym::deref_pure, TraitId;
DerefTarget, sym::deref_target, TypeAliasId;
Receiver, sym::receiver, TraitId;
- ReceiverTarget, sym::receiver_target, TypeAliasId;
+ ReceiverTarget, sym::receiver_target, TypeAliasId;
Fn, sym::fn_, TraitId;
FnMut, sym::fn_mut, TraitId;
@@ -385,6 +512,7 @@ language_item_table! { LangItems =>
FnOnceOutput, sym::fn_once_output, TypeAliasId;
Future, sym::future_trait, TraitId;
+ AsyncIterator, sym::async_iterator, TraitId;
CoroutineState, sym::coroutine_state, EnumId;
Coroutine, sym::coroutine, TraitId;
CoroutineReturn, sym::coroutine_return, TypeAliasId;
@@ -494,7 +622,6 @@ language_item_table! { LangItems =>
IteratorNext, sym::next, FunctionId;
Iterator, sym::iterator, TraitId;
FusedIterator, sym::fused_iterator, TraitId;
- AsyncIterator, sym::async_iterator, TraitId;
PinNewUnchecked, sym::new_unchecked, FunctionId;
@@ -537,4 +664,55 @@ language_item_table! { LangItems =>
core::marker, CoercePointee, CoercePointeeDerive;
core::marker, Copy, CopyDerive;
core::clone, Clone, CloneDerive;
+
+ @resolve_manually:
+
+ IntoFuture, TraitId;
+ IntoFutureOutput, TypeAliasId;
+ IntoIterator, TraitId;
+ IntoIteratorItem, TypeAliasId;
+ IntoIterIntoIterType, TypeAliasId;
+ IteratorItem, TypeAliasId;
+ AsyncIteratorItem, TypeAliasId;
+
+ Fn_call, FunctionId;
+ FnMut_call_mut, FunctionId;
+ FnOnce_call_once, FunctionId;
+ AsyncFn_async_call, FunctionId;
+ AsyncFnMut_async_call_mut, FunctionId;
+ AsyncFnOnce_async_call_once, FunctionId;
+ Not_not, FunctionId;
+ Neg_neg, FunctionId;
+ Add_add, FunctionId;
+ Mul_mul, FunctionId;
+ Sub_sub, FunctionId;
+ Div_div, FunctionId;
+ Rem_rem, FunctionId;
+ Shl_shl, FunctionId;
+ Shr_shr, FunctionId;
+ BitXor_bitxor, FunctionId;
+ BitOr_bitor, FunctionId;
+ BitAnd_bitand, FunctionId;
+ AddAssign_add_assign, FunctionId;
+ MulAssign_mul_assign, FunctionId;
+ SubAssign_sub_assign, FunctionId;
+ DivAssign_div_assign, FunctionId;
+ RemAssign_rem_assign, FunctionId;
+ ShlAssign_shl_assign, FunctionId;
+ ShrAssign_shr_assign, FunctionId;
+ BitXorAssign_bitxor_assign, FunctionId;
+ BitOrAssign_bitor_assign, FunctionId;
+ BitAndAssign_bitand_assign, FunctionId;
+ PartialEq_eq, FunctionId;
+ PartialEq_ne, FunctionId;
+ PartialOrd_le, FunctionId;
+ PartialOrd_lt, FunctionId;
+ PartialOrd_ge, FunctionId;
+ PartialOrd_gt, FunctionId;
+ Drop_drop, FunctionId;
+ Debug_fmt, FunctionId;
+ Deref_deref, FunctionId;
+ DerefMut_deref_mut, FunctionId;
+ Index_index, FunctionId;
+ IndexMut_index_mut, FunctionId;
}
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 22c9073a58..3aaf89102f 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -15,8 +15,8 @@ extern crate rustc_parse_format;
#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_parse_format as rustc_parse_format;
-extern crate ra_ap_rustc_abi as rustc_abi;
pub extern crate ra_ap_rustc_abi as layout;
+pub extern crate ra_ap_rustc_abi as rustc_abi;
pub mod db;
@@ -33,6 +33,7 @@ pub mod item_tree;
pub mod builtin_derive;
pub mod lang_item;
+pub mod unstable_features;
pub mod hir;
pub use self::hir::type_ref;
@@ -47,7 +48,8 @@ pub mod find_path;
pub mod import_map;
pub mod visibility;
-use intern::{Interned, Symbol};
+use intern::Interned;
+use rustc_abi::ExternAbi;
use thin_vec::ThinVec;
pub use crate::signatures::LocalFieldId;
@@ -85,19 +87,14 @@ use crate::{
builtin_type::BuiltinType,
db::DefDatabase,
expr_store::ExpressionStoreSourceMap,
- hir::{
- ExprId,
- generics::{GenericParams, LocalLifetimeParamId, LocalTypeOrConstParamId},
- },
+ hir::generics::{GenericParams, LocalLifetimeParamId, LocalTypeOrConstParamId},
nameres::{
LocalDefMap,
assoc::{ImplItems, TraitItems},
block_def_map, crate_def_map, crate_local_def_map,
diagnostics::DefDiagnostics,
},
- signatures::{
- ConstSignature, EnumVariants, InactiveEnumVariantCode, StaticSignature, VariantFields,
- },
+ signatures::{EnumVariants, InactiveEnumVariantCode, VariantFields},
};
type FxIndexMap<K, V> = indexmap::IndexMap<K, V, rustc_hash::FxBuildHasher>;
@@ -223,9 +220,9 @@ impl<N: AstIdNode> AstIdLoc for AssocItemLoc<N> {
}
macro_rules! impl_intern {
- ($id:ident, $loc:ident, $intern:ident, $lookup:ident) => {
+ ($id:ident, $loc:ident) => {
impl_intern_key!($id, $loc);
- impl_intern_lookup!(DefDatabase, $id, $loc, $intern, $lookup);
+ impl_intern_lookup!(DefDatabase, $id, $loc);
};
}
@@ -252,10 +249,10 @@ macro_rules! impl_loc {
}
type FunctionLoc = AssocItemLoc<ast::Fn>;
-impl_intern!(FunctionId, FunctionLoc, intern_function, lookup_intern_function);
+impl_intern!(FunctionId, FunctionLoc);
type StructLoc = ItemLoc<ast::Struct>;
-impl_intern!(StructId, StructLoc, intern_struct, lookup_intern_struct);
+impl_intern!(StructId, StructLoc);
impl StructId {
pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields {
@@ -272,7 +269,7 @@ impl StructId {
}
pub type UnionLoc = ItemLoc<ast::Union>;
-impl_intern!(UnionId, UnionLoc, intern_union, lookup_intern_union);
+impl_intern!(UnionId, UnionLoc);
impl UnionId {
pub fn fields(self, db: &dyn DefDatabase) -> &VariantFields {
@@ -289,7 +286,7 @@ impl UnionId {
}
pub type EnumLoc = ItemLoc<ast::Enum>;
-impl_intern!(EnumId, EnumLoc, intern_enum, lookup_intern_enum);
+impl_intern!(EnumId, EnumLoc);
impl EnumId {
#[inline]
@@ -307,26 +304,13 @@ impl EnumId {
}
type ConstLoc = AssocItemLoc<ast::Const>;
-impl_intern!(ConstId, ConstLoc, intern_const, lookup_intern_const);
+impl_intern!(ConstId, ConstLoc);
pub type StaticLoc = AssocItemLoc<ast::Static>;
-impl_intern!(StaticId, StaticLoc, intern_static, lookup_intern_static);
-
-/// An anonymous const expression that appears in a type position (e.g., array lengths,
-/// const generic arguments like `{ N + 1 }`). Unlike named constants, these don't have
-/// their own `Body` — their expressions live in the parent's signature `ExpressionStore`.
-#[derive(Debug, Hash, PartialEq, Eq, Clone)]
-pub struct AnonConstLoc {
- /// The owner store containing this expression.
- pub owner: ExpressionStoreOwnerId,
- /// The ExprId within the owner's ExpressionStore that is the root
- /// of this anonymous const expression.
- pub expr: ExprId,
-}
-impl_intern!(AnonConstId, AnonConstLoc, intern_anon_const, lookup_intern_anon_const);
+impl_intern!(StaticId, StaticLoc);
pub type TraitLoc = ItemLoc<ast::Trait>;
-impl_intern!(TraitId, TraitLoc, intern_trait, lookup_intern_trait);
+impl_intern!(TraitId, TraitLoc);
impl TraitId {
#[inline]
@@ -336,10 +320,10 @@ impl TraitId {
}
type TypeAliasLoc = AssocItemLoc<ast::TypeAlias>;
-impl_intern!(TypeAliasId, TypeAliasLoc, intern_type_alias, lookup_intern_type_alias);
+impl_intern!(TypeAliasId, TypeAliasLoc);
type ImplLoc = ItemLoc<ast::Impl>;
-impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl);
+impl_intern!(ImplId, ImplLoc);
impl ImplId {
#[inline]
@@ -369,18 +353,16 @@ pub struct BuiltinDeriveImplId {
}
type UseLoc = ItemLoc<ast::Use>;
-impl_intern!(UseId, UseLoc, intern_use, lookup_intern_use);
+impl_intern!(UseId, UseLoc);
type ExternCrateLoc = ItemLoc<ast::ExternCrate>;
-impl_intern!(ExternCrateId, ExternCrateLoc, intern_extern_crate, lookup_intern_extern_crate);
+impl_intern!(ExternCrateId, ExternCrateLoc);
type ExternBlockLoc = ItemLoc<ast::ExternBlock>;
-impl_intern!(ExternBlockId, ExternBlockLoc, intern_extern_block, lookup_intern_extern_block);
+impl_intern!(ExternBlockId, ExternBlockLoc);
-#[salsa::tracked]
impl ExternBlockId {
- #[salsa::tracked]
- pub fn abi(self, db: &dyn DefDatabase) -> Option<Symbol> {
+ pub fn abi(self, db: &dyn DefDatabase) -> ExternAbi {
signatures::extern_block_abi(db, self)
}
}
@@ -391,7 +373,7 @@ pub struct EnumVariantLoc {
pub parent: EnumId,
pub index: u32,
}
-impl_intern!(EnumVariantId, EnumVariantLoc, intern_enum_variant, lookup_intern_enum_variant);
+impl_intern!(EnumVariantId, EnumVariantLoc);
impl_loc!(EnumVariantLoc, id: Variant, parent: EnumId);
impl EnumVariantId {
@@ -416,7 +398,7 @@ pub struct Macro2Loc {
pub allow_internal_unsafe: bool,
pub edition: Edition,
}
-impl_intern!(Macro2Id, Macro2Loc, intern_macro2, lookup_intern_macro2);
+impl_intern!(Macro2Id, Macro2Loc);
impl_loc!(Macro2Loc, id: MacroDef, container: ModuleId);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -427,7 +409,7 @@ pub struct MacroRulesLoc {
pub flags: MacroRulesLocFlags,
pub edition: Edition,
}
-impl_intern!(MacroRulesId, MacroRulesLoc, intern_macro_rules, lookup_intern_macro_rules);
+impl_intern!(MacroRulesId, MacroRulesLoc);
impl_loc!(MacroRulesLoc, id: MacroRules, container: ModuleId);
bitflags::bitflags! {
@@ -455,7 +437,7 @@ pub struct ProcMacroLoc {
pub kind: ProcMacroKind,
pub edition: Edition,
}
-impl_intern!(ProcMacroId, ProcMacroLoc, intern_proc_macro, lookup_intern_proc_macro);
+impl_intern!(ProcMacroId, ProcMacroLoc);
impl_loc!(ProcMacroLoc, id: Fn, container: ModuleId);
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
@@ -464,7 +446,7 @@ pub struct BlockLoc {
/// The containing module.
pub module: ModuleId,
}
-impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block);
+impl_intern!(BlockId, BlockLoc);
#[salsa_macros::tracked(debug)]
#[derive(PartialOrd, Ord)]
@@ -720,42 +702,6 @@ impl From<DefWithBodyId> for ModuleDefId {
}
}
-/// A constant, which might appears as a const item, an anonymous const block in expressions
-/// or patterns, or as a constant in types with const generics.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
-pub enum GeneralConstId {
- ConstId(ConstId),
- StaticId(StaticId),
- AnonConstId(AnonConstId),
-}
-
-impl_from!(ConstId, StaticId, AnonConstId for GeneralConstId);
-
-impl GeneralConstId {
- pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
- match self {
- GeneralConstId::ConstId(it) => Some(it.into()),
- GeneralConstId::StaticId(it) => Some(it.into()),
- GeneralConstId::AnonConstId(it) => Some(it.lookup(db).owner.generic_def(db)),
- }
- }
-
- pub fn name(self, db: &dyn DefDatabase) -> String {
- match self {
- GeneralConstId::StaticId(it) => {
- StaticSignature::of(db, it).name.display(db, Edition::CURRENT).to_string()
- }
- GeneralConstId::ConstId(const_id) => {
- ConstSignature::of(db, const_id).name.as_ref().map_or_else(
- || "_".to_owned(),
- |name| name.display(db, Edition::CURRENT).to_string(),
- )
- }
- GeneralConstId::AnonConstId(_) => "{anon const}".to_owned(),
- }
- }
-}
-
/// The defs which have a body.
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum DefWithBodyId {
@@ -777,12 +723,12 @@ impl From<EnumVariantId> for DefWithBodyId {
}
impl DefWithBodyId {
- pub fn as_generic_def_id(self, db: &dyn DefDatabase) -> Option<GenericDefId> {
+ pub fn generic_def(self, db: &dyn DefDatabase) -> GenericDefId {
match self {
- DefWithBodyId::FunctionId(f) => Some(f.into()),
- DefWithBodyId::StaticId(s) => Some(s.into()),
- DefWithBodyId::ConstId(c) => Some(c.into()),
- DefWithBodyId::VariantId(c) => Some(c.lookup(db).parent.into()),
+ DefWithBodyId::FunctionId(f) => f.into(),
+ DefWithBodyId::StaticId(s) => s.into(),
+ DefWithBodyId::ConstId(c) => c.into(),
+ DefWithBodyId::VariantId(c) => c.lookup(db).parent.into(),
}
}
}
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index eabdada67c..1d6812ad56 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -22,7 +22,7 @@ use hir_expand::{
builtin::quote::quote,
db::ExpandDatabase,
proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind},
- span_map::SpanMapRef,
+ span_map::SpanMap,
};
use intern::{Symbol, sym};
use itertools::Itertools;
@@ -64,7 +64,7 @@ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect)
.filter_map(|macro_call| {
let errors = db.parse_macro_expansion_error(macro_call)?;
let errors = errors.err.as_ref()?.render_to_string(&db);
- let macro_loc = db.lookup_intern_macro_call(macro_call);
+ let macro_loc = macro_call.loc(&db);
let ast_id = match macro_loc.kind {
MacroCallKind::FnLike { ast_id, .. } => ast_id.map(|it| it.erase()),
MacroCallKind::Derive { ast_id, .. } => ast_id.map(|it| it.erase()),
@@ -142,10 +142,10 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
}
let mut expn_text = String::new();
- if let Some(err) = exp.err {
+ if let Some(err) = &exp.err {
format_to!(expn_text, "/* error: {} */", err.render_to_string(&db).message);
}
- let (parse, token_map) = exp.value;
+ let (parse, token_map) = &exp.value;
if expect_errors {
assert!(!parse.errors().is_empty(), "no parse errors in expansion");
for e in parse.errors() {
@@ -161,7 +161,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
}
let pp = pretty_print_macro_expansion(
parse.syntax_node(),
- SpanMapRef::ExpansionSpanMap(&token_map),
+ SpanMap::ExpansionSpanMap(token_map),
show_spans,
show_ctxt,
);
@@ -215,7 +215,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
}
let pp = pretty_print_macro_expansion(
src.value,
- db.span_map(src.file_id).as_ref(),
+ db.span_map(src.file_id),
show_spans,
show_ctxt,
);
@@ -230,7 +230,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
if let Some(macro_file) = src.file_id.macro_file() {
let pp = pretty_print_macro_expansion(
src.value.syntax().clone(),
- db.span_map(macro_file.into()).as_ref(),
+ db.span_map(macro_file.into()),
false,
false,
);
@@ -245,7 +245,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
{
let pp = pretty_print_macro_expansion(
src.value.syntax().clone(),
- db.span_map(macro_file.into()).as_ref(),
+ db.span_map(macro_file.into()),
false,
false,
);
@@ -309,7 +309,7 @@ fn reindent(indent: IndentLevel, pp: String) -> String {
fn pretty_print_macro_expansion(
expn: SyntaxNode,
- map: SpanMapRef<'_>,
+ map: SpanMap<'_>,
show_spans: bool,
show_ctxt: bool,
) -> String {
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index 56b3f03f7b..88e3408a33 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -68,7 +68,7 @@ use hir_expand::{
};
use intern::Symbol;
use itertools::Itertools;
-use rustc_hash::{FxHashMap, FxHashSet};
+use rustc_hash::FxHashMap;
use span::{Edition, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID};
use stdx::format_to;
use syntax::{AstNode, SmolStr, SyntaxNode, ToSmolStr, ast};
@@ -83,6 +83,7 @@ use crate::{
item_tree::TreeId,
nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode},
per_ns::PerNs,
+ unstable_features::UnstableFeatures,
visibility::{Visibility, VisibilityExplicitness},
};
@@ -216,7 +217,7 @@ struct DefMapCrateData {
/// Custom tool modules registered with `#![register_tool]`.
registered_tools: Vec<Symbol>,
/// Unstable features of Rust enabled with `#![feature(A, B)]`.
- unstable_features: FxHashSet<Symbol>,
+ unstable_features: UnstableFeatures,
/// `#[rustc_coherence_is_core]`
rustc_coherence_is_core: bool,
no_core: bool,
@@ -233,7 +234,7 @@ impl DefMapCrateData {
fn_proc_macro_mapping: FxHashMap::default(),
fn_proc_macro_mapping_back: FxHashMap::default(),
registered_tools: PREDEFINED_TOOLS.iter().map(|it| Symbol::intern(it)).collect(),
- unstable_features: FxHashSet::default(),
+ unstable_features: UnstableFeatures::default(),
rustc_coherence_is_core: false,
no_core: false,
no_std: false,
@@ -554,8 +555,9 @@ impl DefMap {
&self.data.registered_tools
}
- pub fn is_unstable_feature_enabled(&self, feature: &Symbol) -> bool {
- self.data.unstable_features.contains(feature)
+ #[inline]
+ pub fn features(&self) -> &UnstableFeatures {
+ &self.data.unstable_features
}
pub fn is_rustc_coherence_is_core(&self) -> bool {
diff --git a/crates/hir-def/src/nameres/assoc.rs b/crates/hir-def/src/nameres/assoc.rs
index f5a852b39c..b1d554738f 100644
--- a/crates/hir-def/src/nameres/assoc.rs
+++ b/crates/hir-def/src/nameres/assoc.rs
@@ -138,7 +138,7 @@ struct AssocItemCollector<'db> {
def_map: &'db DefMap,
local_def_map: &'db LocalDefMap,
ast_id_map: &'db AstIdMap,
- span_map: SpanMap,
+ span_map: SpanMap<'db>,
cfg_options: &'db CfgOptions,
file_id: HirFileId,
diagnostics: Vec<DefDiagnostic>,
@@ -191,19 +191,18 @@ impl<'db> AssocItemCollector<'db> {
fn collect_item(&mut self, item: ast::AssocItem) {
let ast_id = self.ast_id_map.ast_id(&item);
- let attrs =
- match AttrsOrCfg::lower(self.db, &item, &|| self.cfg_options, self.span_map.as_ref()) {
- AttrsOrCfg::Enabled { attrs } => attrs,
- AttrsOrCfg::CfgDisabled(cfg) => {
- self.diagnostics.push(DefDiagnostic::unconfigured_code(
- self.module_id,
- InFile::new(self.file_id, ast_id.erase()),
- cfg.0,
- self.cfg_options.clone(),
- ));
- return;
- }
- };
+ let attrs = match AttrsOrCfg::lower(self.db, &item, &|| self.cfg_options, self.span_map) {
+ AttrsOrCfg::Enabled { attrs } => attrs,
+ AttrsOrCfg::CfgDisabled(cfg) => {
+ self.diagnostics.push(DefDiagnostic::unconfigured_code(
+ self.module_id,
+ InFile::new(self.file_id, ast_id.erase()),
+ cfg.0,
+ self.cfg_options.clone(),
+ ));
+ return;
+ }
+ };
let ast_id = InFile::new(self.file_id, ast_id.upcast());
'attrs: for (attr_id, attr) in attrs.as_ref().iter() {
@@ -218,7 +217,7 @@ impl<'db> AssocItemCollector<'db> {
attr_id,
) {
Ok(ResolvedAttr::Macro(call_id)) => {
- let loc = self.db.lookup_intern_macro_call(call_id);
+ let loc = call_id.loc(self.db);
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
@@ -357,7 +356,7 @@ impl<'db> AssocItemCollector<'db> {
return;
}
- let (syntax, span_map) = self.db.parse_macro_expansion(macro_call_id).value;
+ let (syntax, span_map) = &self.db.parse_macro_expansion(macro_call_id).value;
let old_file_id = mem::replace(&mut self.file_id, macro_call_id.into());
let old_ast_id_map = mem::replace(&mut self.ast_id_map, self.db.ast_id_map(self.file_id));
let old_span_map = mem::replace(&mut self.span_map, SpanMap::ExpansionSpanMap(span_map));
diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs
index 062b55fcef..5aabd7dbc6 100644
--- a/crates/hir-def/src/nameres/attr_resolution.rs
+++ b/crates/hir-def/src/nameres/attr_resolution.rs
@@ -9,7 +9,6 @@ use hir_expand::{
};
use span::SyntaxContext;
use syntax::ast;
-use triomphe::Arc;
use crate::{
AstIdWithPath, MacroId, ModuleId, UnresolvedMacro,
@@ -126,7 +125,7 @@ pub(super) fn attr_macro_as_call_id(
krate,
MacroCallKind::Attr {
ast_id: item_attr.ast_id,
- attr_args: arg.map(Arc::new),
+ attr_args: arg.map(Box::new),
censored_attr_ids,
},
macro_attr.ctxt,
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 703f070dba..5ef51dbb2c 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -316,7 +316,7 @@ impl<'db> DefCollector<'db> {
_ => None,
},
);
- crate_data.unstable_features.extend(features);
+ features.for_each(|feature| crate_data.unstable_features.enable(feature));
}
() if *attr_name == sym::register_tool => {
if let Some(ident) = attr.single_ident_value() {
@@ -2840,6 +2840,6 @@ foo!(KABOOM);
assert_eq!(def_map.recursion_limit(), 4);
assert!(def_map.is_no_core());
assert!(def_map.is_no_std());
- assert!(def_map.is_unstable_feature_enabled(&sym::register_tool));
+ assert!(def_map.features().is_enabled(&sym::register_tool));
}
}
diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs
index 0f1828abce..08f672aa0c 100644
--- a/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/crates/hir-def/src/nameres/tests/incremental.rs
@@ -168,15 +168,15 @@ fn no() {}
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"EnumVariants::of_",
]
"#]],
@@ -185,7 +185,7 @@ fn no() {}
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
+ "real_span_map",
"EnumVariants::of_",
]
"#]],
@@ -226,21 +226,21 @@ pub struct S {}
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
- "decl_macro_expander_shim",
+ "real_span_map",
+ "DeclarativeMacroExpander::expander_",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"macro_def_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_macro_expansion_shim",
- "macro_arg_shim",
+ "parse_macro_expansion",
+ "macro_arg",
]
"#]],
expect![[r#"
@@ -248,9 +248,9 @@ pub struct S {}
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
- "macro_arg_shim",
- "parse_macro_expansion_shim",
+ "real_span_map",
+ "macro_arg",
+ "parse_macro_expansion",
"ast_id_map",
"file_item_tree_query",
]
@@ -284,27 +284,27 @@ fn f() { foo }
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"crate_local_def_map",
"proc_macros_for_crate_shim",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"macro_def_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_macro_expansion_shim",
- "expand_proc_macro_shim",
- "macro_arg_shim",
+ "parse_macro_expansion",
+ "expand_proc_macro",
+ "macro_arg",
"proc_macro_span_shim",
]
"#]],
@@ -313,10 +313,10 @@ fn f() { foo }
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
- "macro_arg_shim",
- "expand_proc_macro_shim",
- "parse_macro_expansion_shim",
+ "real_span_map",
+ "macro_arg",
+ "expand_proc_macro",
+ "parse_macro_expansion",
"ast_id_map",
"file_item_tree_query",
]
@@ -408,39 +408,39 @@ pub struct S {}
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"crate_local_def_map",
"proc_macros_for_crate_shim",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
- "decl_macro_expander_shim",
+ "real_span_map",
+ "DeclarativeMacroExpander::expander_",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"macro_def_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_macro_expansion_shim",
- "macro_arg_shim",
- "decl_macro_expander_shim",
+ "parse_macro_expansion",
+ "macro_arg",
+ "DeclarativeMacroExpander::expander_",
"macro_def_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_macro_expansion_shim",
- "macro_arg_shim",
+ "parse_macro_expansion",
+ "macro_arg",
"macro_def_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_macro_expansion_shim",
- "expand_proc_macro_shim",
- "macro_arg_shim",
+ "parse_macro_expansion",
+ "expand_proc_macro",
+ "macro_arg",
"proc_macro_span_shim",
]
"#]],
@@ -449,11 +449,11 @@ pub struct S {}
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
- "macro_arg_shim",
- "decl_macro_expander_shim",
- "macro_arg_shim",
- "macro_arg_shim",
+ "real_span_map",
+ "macro_arg",
+ "DeclarativeMacroExpander::expander_",
+ "macro_arg",
+ "macro_arg",
]
"#]],
);
@@ -518,36 +518,36 @@ m!(Z);
[crate_def_map.modules_for_file(&db, pos.file_id.file_id(&db)).next().unwrap()];
assert_eq!(module_data.scope.resolutions().count(), 4);
},
- &[("file_item_tree_query", 6), ("parse_macro_expansion_shim", 3)],
+ &[("file_item_tree_query", 6), ("parse_macro_expansion", 3)],
expect![[r#"
[
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
- "decl_macro_expander_shim",
+ "real_span_map",
+ "DeclarativeMacroExpander::expander_",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"macro_def_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_macro_expansion_shim",
- "macro_arg_shim",
+ "parse_macro_expansion",
+ "macro_arg",
"file_item_tree_query",
"ast_id_map",
- "parse_macro_expansion_shim",
- "macro_arg_shim",
+ "parse_macro_expansion",
+ "macro_arg",
"file_item_tree_query",
"ast_id_map",
- "parse_macro_expansion_shim",
- "macro_arg_shim",
+ "parse_macro_expansion",
+ "macro_arg",
]
"#]],
);
@@ -568,16 +568,16 @@ m!(Z);
[crate_def_map.modules_for_file(&db, pos.file_id.file_id(&db)).next().unwrap()];
assert_eq!(module_data.scope.resolutions().count(), 4);
},
- &[("file_item_tree_query", 1), ("parse_macro_expansion_shim", 0)],
+ &[("file_item_tree_query", 1), ("parse_macro_expansion", 0)],
expect![[r#"
[
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
- "macro_arg_shim",
- "macro_arg_shim",
- "macro_arg_shim",
+ "real_span_map",
+ "macro_arg",
+ "macro_arg",
+ "macro_arg",
]
"#]],
);
@@ -612,7 +612,7 @@ pub type Ty = ();
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
]
"#]],
);
@@ -632,7 +632,7 @@ pub type Ty = ();
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
+ "real_span_map",
]
"#]],
);
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index bb292ac1a6..8320221ffc 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -936,7 +936,7 @@ fn handle_macro_def_scope(
// and use its parent expansion.
*hygiene_id = HygieneId::new(parent_ctx.opaque_and_semiopaque(db));
*hygiene_info = parent_ctx.outer_expn(db).map(|expansion| {
- let expansion = db.lookup_intern_macro_call(expansion.into());
+ let expansion = hir_expand::MacroCallId::from(expansion).loc(db);
(parent_ctx.parent(db), expansion.def)
});
}
@@ -950,7 +950,7 @@ fn hygiene_info(
if !hygiene_id.is_root() {
let ctx = hygiene_id.syntax_context();
ctx.outer_expn(db).map(|expansion| {
- let expansion = db.lookup_intern_macro_call(expansion.into());
+ let expansion = hir_expand::MacroCallId::from(expansion).loc(db);
(ctx.parent(db), expansion.def)
})
} else {
diff --git a/crates/hir-def/src/signatures.rs b/crates/hir-def/src/signatures.rs
index 6d704274f4..e58befae20 100644
--- a/crates/hir-def/src/signatures.rs
+++ b/crates/hir-def/src/signatures.rs
@@ -8,9 +8,9 @@ use hir_expand::{
InFile, Intern, Lookup,
name::{AsName, Name},
};
-use intern::{Symbol, sym};
+use intern::sym;
use la_arena::{Arena, Idx};
-use rustc_abi::{IntegerType, ReprOptions};
+use rustc_abi::{ExternAbi, IntegerType, ReprOptions};
use syntax::{
AstNode, NodeOrToken, SyntaxNodePtr, T,
ast::{self, HasGenericParams, HasName, HasVisibility, IsString},
@@ -128,13 +128,11 @@ impl StructSignature {
source_map,
)
}
-}
-impl StructSignature {
#[inline]
pub fn repr(&self, db: &dyn DefDatabase, id: StructId) -> Option<ReprOptions> {
if self.flags.contains(StructFlags::HAS_REPR) {
- AttrFlags::repr(db, id.into())
+ AttrFlags::repr_assume_has(db, id.into())
} else {
None
}
@@ -202,6 +200,15 @@ impl UnionSignature {
source_map,
)
}
+
+ #[inline]
+ pub fn repr(&self, db: &dyn DefDatabase, id: UnionId) -> Option<ReprOptions> {
+ if self.flags.contains(StructFlags::HAS_REPR) {
+ AttrFlags::repr_assume_has(db, id.into())
+ } else {
+ None
+ }
+ }
}
bitflags! {
@@ -211,6 +218,8 @@ bitflags! {
const HAS_REPR = 1 << 0;
/// Indicates whether the enum has a `#[rustc_has_incoherent_inherent_impls]` attribute.
const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 1;
+ /// Whether this enum has `#[fundamental]`.
+ const FUNDAMENTAL = 1 << 2;
}
}
@@ -243,6 +252,9 @@ impl EnumSignature {
if attrs.contains(AttrFlags::HAS_REPR) {
flags |= EnumFlags::HAS_REPR;
}
+ if attrs.contains(AttrFlags::FUNDAMENTAL) {
+ flags |= EnumFlags::FUNDAMENTAL;
+ }
let InFile { file_id, value: source } = loc.source(db);
let (store, generic_params, source_map) = lower_generic_params(
@@ -276,7 +288,11 @@ impl EnumSignature {
#[inline]
pub fn repr(&self, db: &dyn DefDatabase, id: EnumId) -> Option<ReprOptions> {
- if self.flags.contains(EnumFlags::HAS_REPR) { AttrFlags::repr(db, id.into()) } else { None }
+ if self.flags.contains(EnumFlags::HAS_REPR) {
+ AttrFlags::repr_assume_has(db, id.into())
+ } else {
+ None
+ }
}
}
bitflags::bitflags! {
@@ -567,19 +583,20 @@ bitflags! {
const DEFAULT = 1 << 2;
const CONST = 1 << 3;
const ASYNC = 1 << 4;
- const UNSAFE = 1 << 5;
- const HAS_VARARGS = 1 << 6;
- const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 7;
- const HAS_SELF_PARAM = 1 << 8;
+ const GEN = 1 << 5;
+ const UNSAFE = 1 << 6;
+ const HAS_VARARGS = 1 << 7;
+ const RUSTC_ALLOW_INCOHERENT_IMPL = 1 << 8;
+ const HAS_SELF_PARAM = 1 << 9;
/// The `#[target_feature]` attribute is necessary to check safety (with RFC 2396),
/// but keeping it for all functions will consume a lot of memory when there are
/// only very few functions with it. So we only encode its existence here, and lookup
/// it if needed.
- const HAS_TARGET_FEATURE = 1 << 9;
- const DEPRECATED_SAFE_2024 = 1 << 10;
- const EXPLICIT_SAFE = 1 << 11;
- const HAS_LEGACY_CONST_GENERICS = 1 << 12;
- const RUSTC_INTRINSIC = 1 << 13;
+ const HAS_TARGET_FEATURE = 1 << 10;
+ const DEPRECATED_SAFE_2024 = 1 << 11;
+ const EXPLICIT_SAFE = 1 << 12;
+ const HAS_LEGACY_CONST_GENERICS = 1 << 13;
+ const RUSTC_INTRINSIC = 1 << 14;
}
}
@@ -590,7 +607,7 @@ pub struct FunctionSignature {
pub store: ExpressionStore,
pub params: Box<[TypeRefId]>,
pub ret_type: Option<TypeRefId>,
- pub abi: Option<Symbol>,
+ pub abi: ExternAbi,
pub flags: FnFlags,
}
@@ -638,6 +655,9 @@ impl FunctionSignature {
if source.value.async_token().is_some() {
flags.insert(FnFlags::ASYNC);
}
+ if source.value.gen_token().is_some() {
+ flags.insert(FnFlags::GEN);
+ }
if source.value.const_token().is_some() {
flags.insert(FnFlags::CONST);
}
@@ -652,9 +672,23 @@ impl FunctionSignature {
}
let name = as_name_opt(source.value.name());
- let abi = source.value.abi().map(|abi| {
- abi.abi_string().map_or_else(|| sym::C, |it| Symbol::intern(it.text_without_quotes()))
- });
+ let abi = source
+ .value
+ .abi()
+ .map(|abi| {
+ abi.abi_string()
+ .and_then(|abi| abi.text_without_quotes().parse().ok())
+ .unwrap_or(ExternAbi::FALLBACK)
+ })
+ .or_else(|| match loc.container {
+ ItemContainerId::ExternBlockId(extern_block) => {
+ Some(extern_block_abi(db, extern_block))
+ }
+ ItemContainerId::ModuleId(_)
+ | ItemContainerId::ImplId(_)
+ | ItemContainerId::TraitId(_) => None,
+ })
+ .unwrap_or(ExternAbi::Rust);
let (store, source_map, generic_params, params, ret_type, self_param, variadic) =
lower_function(db, module, source, id);
if self_param {
@@ -701,6 +735,10 @@ impl FunctionSignature {
self.flags.contains(FnFlags::ASYNC)
}
+ pub fn is_gen(&self) -> bool {
+ self.flags.contains(FnFlags::GEN)
+ }
+
pub fn is_unsafe(&self) -> bool {
self.flags.contains(FnFlags::UNSAFE)
}
@@ -737,16 +775,6 @@ impl FunctionSignature {
pub fn is_intrinsic(db: &dyn DefDatabase, id: FunctionId) -> bool {
let data = FunctionSignature::of(db, id);
data.flags.contains(FnFlags::RUSTC_INTRINSIC)
- // Keep this around for a bit until extern "rustc-intrinsic" abis are no longer used
- || match &data.abi {
- Some(abi) => *abi == sym::rust_dash_intrinsic,
- None => match id.lookup(db).container {
- ItemContainerId::ExternBlockId(block) => {
- block.abi(db) == Some(sym::rust_dash_intrinsic)
- }
- _ => false,
- },
- }
}
}
@@ -958,7 +986,7 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>(
override_visibility: Option<Option<ast::Visibility>>,
) -> Option<(Arena<FieldData>, ExpressionStore, ExpressionStoreSourceMap)> {
let cfg_options = module.krate(db).cfg_options(db);
- let mut col = ExprCollector::signature(db, module, fields.file_id);
+ let mut col = ExprCollector::new(db, module, fields.file_id);
let override_visibility = override_visibility.map(|vis| {
LazyCell::new(|| {
let span_map = db.span_map(fields.file_id);
@@ -1110,16 +1138,12 @@ impl EnumVariants {
}
}
-pub(crate) fn extern_block_abi(
- db: &dyn DefDatabase,
- extern_block: ExternBlockId,
-) -> Option<Symbol> {
+#[salsa::tracked]
+pub(crate) fn extern_block_abi(db: &dyn DefDatabase, extern_block: ExternBlockId) -> ExternAbi {
let source = extern_block.lookup(db).source(db);
- source.value.abi().map(|abi| {
- match abi.abi_string() {
- Some(tok) => Symbol::intern(tok.text_without_quotes()),
- // `extern` default to be `extern "C"`.
- _ => sym::C,
- }
- })
+ source
+ .value
+ .abi()
+ .and_then(|abi| abi.abi_string()?.text_without_quotes().parse().ok())
+ .unwrap_or(ExternAbi::FALLBACK)
}
diff --git a/crates/hir-def/src/unstable_features.rs b/crates/hir-def/src/unstable_features.rs
new file mode 100644
index 0000000000..f581f02617
--- /dev/null
+++ b/crates/hir-def/src/unstable_features.rs
@@ -0,0 +1,96 @@
+//! Handling of unstable features.
+//!
+//! We define two kinds of handling: we have a map of all unstable features for a crate
+//! as `Symbol`s. This is mostly for external consumers.
+//!
+//! For analysis, we store them as a struct of bools, for fast access.
+
+use std::fmt;
+
+use base_db::Crate;
+use intern::{Symbol, sym};
+use rustc_hash::FxHashSet;
+
+use crate::db::DefDatabase;
+
+impl fmt::Debug for UnstableFeatures {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_set().entries(&self.all).finish()
+ }
+}
+
+impl PartialEq for UnstableFeatures {
+ fn eq(&self, other: &Self) -> bool {
+ self.all == other.all
+ }
+}
+
+impl Eq for UnstableFeatures {}
+
+impl UnstableFeatures {
+ #[inline]
+ pub fn is_enabled(&self, feature: &Symbol) -> bool {
+ self.all.contains(feature)
+ }
+
+ #[inline]
+ pub fn iter(&self) -> impl Iterator<Item = Symbol> {
+ self.all.iter().cloned()
+ }
+
+ pub(crate) fn shrink_to_fit(&mut self) {
+ self.all.shrink_to_fit();
+ }
+}
+
+#[salsa::tracked]
+impl UnstableFeatures {
+ /// Query unstable features for a crate.
+ ///
+ /// This is also available as `DefMap::features()`. Use that if you have a DefMap available.
+ /// Otherwise, use this, to not draw a dependency to the def map.
+ #[salsa::tracked(returns(ref))]
+ pub fn query(db: &dyn DefDatabase, krate: Crate) -> UnstableFeatures {
+ crate::crate_def_map(db, krate).features().clone()
+ }
+}
+
+macro_rules! define_unstable_features {
+ ( $( $feature:ident, )* ) => {
+ #[derive(Clone, Default)]
+ pub struct UnstableFeatures {
+ all: FxHashSet<Symbol>,
+
+ $( pub $feature: bool, )*
+ }
+
+ impl UnstableFeatures {
+ pub(crate) fn enable(&mut self, feature: Symbol) {
+ match () {
+ $( () if feature == sym::$feature => self.$feature = true, )*
+ _ => {}
+ }
+
+ self.all.insert(feature);
+ }
+ }
+ };
+}
+
+define_unstable_features! {
+ lang_items,
+ exhaustive_patterns,
+ generic_associated_type_extended,
+ arbitrary_self_types,
+ arbitrary_self_types_pointers,
+ supertrait_item_shadowing,
+ new_range,
+ never_type_fallback,
+ specialization,
+ min_specialization,
+ ref_pat_eat_one_layer_2024,
+ ref_pat_eat_one_layer_2024_structural,
+ deref_patterns,
+ mut_ref,
+ type_changing_struct_update,
+}
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index d1f962f7ff..395b997972 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -32,7 +32,7 @@ use crate::{
AstId,
db::ExpandDatabase,
mod_path::ModPath,
- span_map::SpanMapRef,
+ span_map::SpanMap,
tt::{self, TopSubtree},
};
@@ -422,7 +422,7 @@ impl AttrId {
));
let tt = syntax_bridge::syntax_node_to_token_tree(
tt.syntax(),
- SpanMapRef::RealSpanMap(&span_map),
+ SpanMap::RealSpanMap(&span_map),
span_map.span_for_range(tt.syntax().text_range()),
DocCommentDesugarMode::ProcMacro,
);
diff --git a/crates/hir-expand/src/builtin/attr_macro.rs b/crates/hir-expand/src/builtin/attr_macro.rs
index c94663ca0c..9b13f9fb00 100644
--- a/crates/hir-expand/src/builtin/attr_macro.rs
+++ b/crates/hir-expand/src/builtin/attr_macro.rs
@@ -122,7 +122,7 @@ fn derive_expand(
tt: &tt::TopSubtree,
span: Span,
) -> ExpandResult<tt::TopSubtree> {
- let loc = db.lookup_intern_macro_call(id);
+ let loc = id.loc(db);
let derives = match &loc.kind {
MacroCallKind::Attr { attr_args: Some(attr_args), .. } if loc.def.is_attribute_derive() => {
attr_args
diff --git a/crates/hir-expand/src/builtin/derive_macro.rs b/crates/hir-expand/src/builtin/derive_macro.rs
index 8f513a2bcf..8b031e3647 100644
--- a/crates/hir-expand/src/builtin/derive_macro.rs
+++ b/crates/hir-expand/src/builtin/derive_macro.rs
@@ -25,7 +25,6 @@ use syntax::{
HasName, HasTypeBounds, make,
},
syntax_editor::{GetOrCreateWhereClause, SyntaxEditor},
- ted,
};
macro_rules! register_builtin {
@@ -1007,7 +1006,8 @@ fn coerce_pointee_expand(
return ExpandResult::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), err);
}
};
- let adt = adt.clone_for_update();
+ let (editor, adt) = SyntaxEditor::with_ast_node(&adt);
+ let make = editor.make();
let ast::Adt::Struct(strukt) = &adt else {
return ExpandResult::new(
tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
@@ -1078,7 +1078,7 @@ fn coerce_pointee_expand(
if is_pointee {
// Remove the `#[pointee]` attribute so it won't be present in the generated
// impls (where we cannot resolve it).
- ted::remove(attr.syntax());
+ editor.delete(attr.syntax());
}
is_pointee
})
@@ -1181,25 +1181,28 @@ fn coerce_pointee_expand(
// If the target type is the pointee, duplicate the bound as whole.
// Otherwise, duplicate only bounds that mention the pointee.
let is_pointee = param_name.text() == pointee_param_name.text();
- let new_bounds = bounds
- .bounds()
- .map(|bound| bound.clone_subtree().clone_for_update())
- .filter(|bound| {
- bound.ty().is_some_and(|ty| {
- substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM)
- || is_pointee
- })
- });
+ let new_bounds = bounds.bounds().filter_map(|bound| {
+ let new_bound = substitute_type_bound(
+ bound.clone(),
+ &pointee_param_name.text(),
+ ADDED_PARAM,
+ );
+
+ if is_pointee {
+ return new_bound.or(Some(bound));
+ }
+ new_bound
+ });
+
let new_bounds_target = if is_pointee {
- make::name_ref(ADDED_PARAM)
+ make.name_ref(ADDED_PARAM)
} else {
- make::name_ref(&param_name.text())
+ make.name_ref(&param_name.text())
};
- new_predicates.push(make::where_pred(
- Either::Right(make::ty_path(make::path_from_segments(
- [make::path_segment(new_bounds_target)],
- false,
- ))),
+ new_predicates.push(make.where_pred(
+ Either::Right(
+ make.ty_path_from_segments([make.path_segment(new_bounds_target)], false),
+ ),
new_bounds,
));
}
@@ -1232,37 +1235,19 @@ fn coerce_pointee_expand(
// We should also write a few new `where` bounds from `#[pointee] T` to `__S`
// as well as any bound that indirectly involves the `#[pointee] T` type.
for predicate in strukt.where_clause().into_iter().flat_map(|wc| wc.predicates()) {
- let predicate = predicate.clone_subtree().clone_for_update();
let Some(pred_target) = predicate.ty() else { continue };
// If the target type references the pointee, duplicate the bound as whole.
// Otherwise, duplicate only bounds that mention the pointee.
- if substitute_type_in_bound(
- pred_target.clone(),
- &pointee_param_name.text(),
- ADDED_PARAM,
- ) {
- if let Some(bounds) = predicate.type_bound_list() {
- for bound in bounds.bounds() {
- if let Some(ty) = bound.ty() {
- substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM);
- }
- }
- }
-
- new_predicates.push(predicate);
+ if let Some(predicate_with_substituted_target) =
+ substitute_where_pred(&predicate, &pointee_param_name.text(), ADDED_PARAM)
+ {
+ new_predicates.push(predicate_with_substituted_target);
} else if let Some(bounds) = predicate.type_bound_list() {
- let new_bounds = bounds
- .bounds()
- .map(|bound| bound.clone_subtree().clone_for_update())
- .filter(|bound| {
- bound.ty().is_some_and(|ty| {
- substitute_type_in_bound(ty, &pointee_param_name.text(), ADDED_PARAM)
- })
- });
- new_predicates.push(
- make::where_pred(Either::Right(pred_target), new_bounds).clone_for_update(),
- );
+ let new_bounds = bounds.bounds().filter_map(|bound| {
+ substitute_type_bound(bound, &pointee_param_name.text(), ADDED_PARAM)
+ });
+ new_predicates.push(make.where_pred(Either::Right(pred_target), new_bounds));
}
}
}
@@ -1271,35 +1256,34 @@ fn coerce_pointee_expand(
// # Add `Unsize<__S>` bound to `#[pointee]` at the generic parameter location
//
// Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it.
- new_predicates.push(make::where_pred(
- Either::Right(make::ty_path(make::path_from_segments(
- [make::path_segment(make::name_ref(&pointee_param_name.text()))],
- false,
- ))),
- [make::type_bound(make::ty_path(make::path_from_segments(
- [
- make::path_segment(make::name_ref("core")),
- make::path_segment(make::name_ref("marker")),
- make::generic_ty_path_segment(
- make::name_ref("Unsize"),
- [make::type_arg(make::ty_path(make::path_from_segments(
- [make::path_segment(make::name_ref(ADDED_PARAM))],
- false,
- )))
- .into()],
+ new_predicates.push(
+ make.where_pred(
+ Either::Right(make.ty_path_from_segments(
+ [make.path_segment(make.name_ref(&pointee_param_name.text()))],
+ false,
+ )),
+ [make.type_bound(
+ make.ty_path_from_segments(
+ [
+ make.path_segment(make.name_ref("core")),
+ make.path_segment(make.name_ref("marker")),
+ make.generic_ty_path_segment(
+ make.name_ref("Unsize"),
+ [make
+ .type_arg(make.ty_path_from_segments(
+ [make.path_segment(make.name_ref(ADDED_PARAM))],
+ false,
+ ))
+ .into()],
+ ),
+ ],
+ true,
),
- ],
- true,
- )))],
- ));
+ )],
+ ),
+ );
}
- let (editor, strukt) = SyntaxEditor::with_ast_node(strukt);
- strukt.get_or_create_where_clause(&editor, new_predicates.into_iter());
- let edit = editor.finish();
- let strukt = ast::Struct::cast(edit.new_root().clone()).unwrap();
- let adt = ast::Adt::Struct(strukt.clone());
-
let self_for_traits = {
// Replace the `#[pointee]` with `__S`.
let mut type_param_idx = 0;
@@ -1310,37 +1294,38 @@ fn coerce_pointee_expand(
.filter_map(|param| {
Some(match param {
ast::GenericParam::ConstParam(param) => {
- ast::GenericArg::ConstArg(make::expr_const_value(&param.name()?.text()))
+ ast::GenericArg::ConstArg(make.expr_const_value(&param.name()?.text()))
}
ast::GenericParam::LifetimeParam(param) => {
- make::lifetime_arg(param.lifetime()?).into()
+ make.lifetime_arg(param.lifetime()?).into()
}
ast::GenericParam::TypeParam(param) => {
let name = if pointee_param_idx == type_param_idx {
- make::name_ref(ADDED_PARAM)
+ make.name_ref(ADDED_PARAM)
} else {
- make::name_ref(&param.name()?.text())
+ make.name_ref(&param.name()?.text())
};
type_param_idx += 1;
- make::type_arg(make::ty_path(make::path_from_segments(
- [make::path_segment(name)],
- false,
- )))
- .into()
+ make.type_arg(make.ty_path_from_segments([make.path_segment(name)], false))
+ .into()
}
})
});
- make::path_from_segments(
- [make::generic_ty_path_segment(
- make::name_ref(&struct_name.text()),
+ make.path_from_segments(
+ [make.generic_ty_path_segment(
+ make.name_ref(&struct_name.text()),
self_params_for_traits,
)],
false,
)
- .clone_for_update()
};
+ strukt.get_or_create_where_clause(&editor, new_predicates.into_iter());
+ let edit = editor.finish();
+ let strukt = ast::Struct::cast(edit.new_root().clone()).unwrap();
+ let adt = ast::Adt::Struct(strukt.clone());
+
let mut span_map = span::SpanMap::empty();
// One span for them all.
span_map.push(adt.syntax().text_range().end(), span);
@@ -1403,37 +1388,84 @@ fn coerce_pointee_expand(
}
/// Returns true if any substitution was performed.
- fn substitute_type_in_bound(ty: ast::Type, param_name: &str, replacement: &str) -> bool {
+ fn substitute_type_bound(
+ bound: ast::TypeBound,
+ param_name: &str,
+ replacement: &str,
+ ) -> Option<ast::TypeBound> {
+ let (editor, bound) = SyntaxEditor::with_ast_node(&bound);
+ let substituted = bound
+ .ty()
+ .is_some_and(|ty| substitute_type_in_bound(&editor, ty, param_name, replacement));
+ if !substituted {
+ return None;
+ }
+
+ let edit = editor.finish();
+ Some(ast::TypeBound::cast(edit.new_root().clone()).unwrap())
+ }
+
+ fn substitute_where_pred(
+ predicate: &ast::WherePred,
+ param_name: &str,
+ replacement: &str,
+ ) -> Option<ast::WherePred> {
+ let (editor, predicate) = SyntaxEditor::with_ast_node(predicate);
+ let substituted = predicate
+ .ty()
+ .is_some_and(|ty| substitute_type_in_bound(&editor, ty, param_name, replacement));
+ if substituted && let Some(bounds) = predicate.type_bound_list() {
+ for bound in bounds.bounds() {
+ if let Some(ty) = bound.ty() {
+ substitute_type_in_bound(&editor, ty, param_name, replacement);
+ }
+ }
+ }
+ if !substituted {
+ return None;
+ }
+
+ let edit = editor.finish();
+ Some(ast::WherePred::cast(edit.new_root().clone()).unwrap())
+ }
+
+ fn substitute_type_in_bound(
+ editor: &SyntaxEditor,
+ ty: ast::Type,
+ param_name: &str,
+ replacement: &str,
+ ) -> bool {
return match ty {
- ast::Type::ArrayType(ty) => {
- ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement))
+ ast::Type::ArrayType(ty) => ty
+ .ty()
+ .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)),
+ ast::Type::DynTraitType(ty) => {
+ go_bounds(editor, ty.type_bound_list(), param_name, replacement)
}
- ast::Type::DynTraitType(ty) => go_bounds(ty.type_bound_list(), param_name, replacement),
ast::Type::FnPtrType(ty) => any_long(
ty.param_list()
.into_iter()
.flat_map(|params| params.params().filter_map(|param| param.ty()))
.chain(ty.ret_type().and_then(|it| it.ty())),
- |ty| substitute_type_in_bound(ty, param_name, replacement),
+ |ty| substitute_type_in_bound(editor, ty, param_name, replacement),
),
- ast::Type::ForType(ty) => {
- ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement))
- }
+ ast::Type::ForType(ty) => ty
+ .ty()
+ .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)),
ast::Type::ImplTraitType(ty) => {
- go_bounds(ty.type_bound_list(), param_name, replacement)
- }
- ast::Type::ParenType(ty) => {
- ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement))
+ go_bounds(editor, ty.type_bound_list(), param_name, replacement)
}
+ ast::Type::ParenType(ty) => ty
+ .ty()
+ .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)),
ast::Type::PathType(ty) => ty.path().is_some_and(|path| {
if path.as_single_name_ref().is_some_and(|name| name.text() == param_name) {
- ted::replace(
+ editor.replace(
path.syntax(),
make::path_from_segments(
[make::path_segment(make::name_ref(replacement))],
false,
)
- .clone_for_update()
.syntax(),
);
return true;
@@ -1447,34 +1479,35 @@ fn coerce_pointee_expand(
ast::GenericArg::TypeArg(ty) => ty.ty(),
_ => None,
}),
- |ty| substitute_type_in_bound(ty, param_name, replacement),
+ |ty| substitute_type_in_bound(editor, ty, param_name, replacement),
)
}),
- ast::Type::PtrType(ty) => {
- ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement))
- }
- ast::Type::RefType(ty) => {
- ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement))
- }
- ast::Type::SliceType(ty) => {
- ty.ty().is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement))
- }
- ast::Type::TupleType(ty) => {
- any_long(ty.fields(), |ty| substitute_type_in_bound(ty, param_name, replacement))
- }
+ ast::Type::PtrType(ty) => ty
+ .ty()
+ .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)),
+ ast::Type::RefType(ty) => ty
+ .ty()
+ .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)),
+ ast::Type::SliceType(ty) => ty
+ .ty()
+ .is_some_and(|ty| substitute_type_in_bound(editor, ty, param_name, replacement)),
+ ast::Type::TupleType(ty) => any_long(ty.fields(), |ty| {
+ substitute_type_in_bound(editor, ty, param_name, replacement)
+ }),
ast::Type::InferType(_) | ast::Type::MacroType(_) | ast::Type::NeverType(_) => false,
};
fn go_bounds(
+ editor: &SyntaxEditor,
bounds: Option<ast::TypeBoundList>,
param_name: &str,
replacement: &str,
) -> bool {
bounds.is_some_and(|bounds| {
any_long(bounds.bounds(), |bound| {
- bound
- .ty()
- .is_some_and(|ty| substitute_type_in_bound(ty, param_name, replacement))
+ bound.ty().is_some_and(|ty| {
+ substitute_type_in_bound(editor, ty, param_name, replacement)
+ })
})
})
}
diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs
index 9962677a9d..eb7175c686 100644
--- a/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/crates/hir-expand/src/builtin/fn_macro.rs
@@ -357,7 +357,7 @@ fn cfg_select_expand(
tt: &tt::TopSubtree,
span: Span,
) -> ExpandResult<tt::TopSubtree> {
- let loc = db.lookup_intern_macro_call(id);
+ let loc = id.loc(db);
let cfg_options = loc.krate.cfg_options(db);
let mut iter = tt.iter();
@@ -446,7 +446,7 @@ fn cfg_expand(
tt: &tt::TopSubtree,
span: Span,
) -> ExpandResult<tt::TopSubtree> {
- let loc = db.lookup_intern_macro_call(id);
+ let loc = id.loc(db);
let expr = CfgExpr::parse(tt);
let enabled = loc.krate.cfg_options(db).check(&expr) != Some(false);
let expanded = if enabled { quote!(span=>true) } else { quote!(span=>false) };
@@ -518,7 +518,7 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool {
let Some(expn) = span.ctx.outer_expn(db) else {
break false;
};
- let expn = db.lookup_intern_macro_call(expn.into());
+ let expn = crate::MacroCallId::from(expn).loc(db);
// FIXME: Record allow_internal_unstable in the macro def (not been done yet because it
// would consume quite a bit extra memory for all call locs...)
// if let Some(features) = expn.def.allow_internal_unstable {
@@ -764,7 +764,7 @@ fn relative_file(
allow_recursion: bool,
err_span: Span,
) -> Result<EditionedFileId, ExpandError> {
- let lookup = db.lookup_intern_macro_call(call_id);
+ let lookup = call_id.loc(db);
let call_site = lookup.kind.file_id().original_file_respecting_includes(db).file_id(db);
let path = AnchoredPath { anchor: call_site, path: path_str };
let res: FileId = db
@@ -900,7 +900,7 @@ fn include_str_expand(
}
fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> Option<String> {
- let krate = db.lookup_intern_macro_call(arg_id).krate;
+ let krate = arg_id.loc(db).krate;
krate.env(db).get(key.as_str())
}
diff --git a/crates/hir-expand/src/builtin/quote.rs b/crates/hir-expand/src/builtin/quote.rs
index 51c4e22516..d84756377f 100644
--- a/crates/hir-expand/src/builtin/quote.rs
+++ b/crates/hir-expand/src/builtin/quote.rs
@@ -199,9 +199,9 @@ impl<T: ToTokenTree + Clone> ToTokenTree for &T {
}
impl_to_to_tokentrees! {
- span: u32 => self { crate::tt::Literal{text_and_suffix: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } };
- span: usize => self { crate::tt::Literal{text_and_suffix: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } };
- span: i32 => self { crate::tt::Literal{text_and_suffix: Symbol::integer(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } };
+ span: u32 => self { crate::tt::Literal{text_and_suffix: sym::Integer::get(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } };
+ span: usize => self { crate::tt::Literal{text_and_suffix: sym::Integer::get(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } };
+ span: i32 => self { crate::tt::Literal{text_and_suffix: sym::Integer::get(self as _), span, kind: tt::LitKind::Integer, suffix_len: 0 } };
span: bool => self { crate::tt::Ident{sym: if self { sym::true_ } else { sym::false_ }, span, is_raw: tt::IdentIsRaw::No } };
_span: crate::tt::Leaf => self { self };
_span: crate::tt::Literal => self { self };
diff --git a/crates/hir-expand/src/cfg_process.rs b/crates/hir-expand/src/cfg_process.rs
index 6258fac0e9..81edc9f2cf 100644
--- a/crates/hir-expand/src/cfg_process.rs
+++ b/crates/hir-expand/src/cfg_process.rs
@@ -16,7 +16,7 @@ use crate::{
attrs::{AstPathExt, AttrId, expand_cfg_attr, is_item_tree_filtered_attr},
db::ExpandDatabase,
fixup::{self, SyntaxFixupUndoInfo},
- span_map::SpanMapRef,
+ span_map::SpanMap,
tt::{self, DelimSpan, Span},
};
@@ -51,7 +51,7 @@ fn macro_input_callback(
censor_item_tree_attr_ids: &[AttrId],
krate: Crate,
default_span: Span,
- span_map: SpanMapRef<'_>,
+ span_map: SpanMap<'_>,
) -> impl FnMut(&mut PreorderWithTokens, &WalkEvent<SyntaxElement>) -> (bool, Vec<tt::Leaf>) {
let cfg_options = OnceCell::new();
let cfg_options = move || *cfg_options.get_or_init(|| krate.cfg_options(db));
@@ -295,7 +295,7 @@ fn macro_input_callback(
pub(crate) fn attr_macro_input_to_token_tree(
db: &dyn ExpandDatabase,
node: &SyntaxNode,
- span_map: SpanMapRef<'_>,
+ span_map: SpanMap<'_>,
span: Span,
is_derive: bool,
censor_item_tree_attr_ids: &[AttrId],
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index 8dddddfabb..beae6e843e 100644
--- a/crates/hir-expand/src/db.rs
+++ b/crates/hir-expand/src/db.rs
@@ -3,6 +3,7 @@
use base_db::{Crate, SourceDatabase};
use mbe::MatchedArmIndex;
use span::{AstIdMap, Edition, Span, SyntaxContext};
+use std::borrow::Cow;
use syntax::{AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, ast};
use syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree};
use triomphe::Arc;
@@ -17,11 +18,11 @@ use crate::{
fixup::{self, SyntaxFixupUndoInfo},
hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt},
proc_macro::{CrateProcMacros, CustomProcMacroExpander, ProcMacros},
- span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef},
+ span_map::{ExpansionSpanMap, RealSpanMap, SpanMap},
tt,
};
/// This is just to ensure the types of smart_macro_arg and macro_arg are the same
-type MacroArgResult = (Arc<tt::TopSubtree>, SyntaxFixupUndoInfo, Span);
+type MacroArgResult = (tt::TopSubtree, SyntaxFixupUndoInfo, Span);
/// Total limit on the number of tokens produced by any macro invocation.
///
/// If an invocation produces more tokens than this limit, it will not be stored in the database and
@@ -30,10 +31,10 @@ type MacroArgResult = (Arc<tt::TopSubtree>, SyntaxFixupUndoInfo, Span);
/// Actual max for `analysis-stats .` at some point: 30672.
const TOKEN_LIMIT: usize = 2_097_152;
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub enum TokenExpander {
+#[derive(Debug, Clone, Copy, Eq, PartialEq)]
+pub enum TokenExpander<'db> {
/// Old-style `macro_rules` or the new macros 2.0
- DeclarativeMacro(Arc<DeclarativeMacroExpander>),
+ DeclarativeMacro(&'db DeclarativeMacroExpander),
/// Stuff like `line!` and `file!`.
BuiltIn(BuiltinFnLikeExpander),
/// Built-in eagerly expanded fn-like macros (`include!`, `concat!`, etc.)
@@ -67,65 +68,59 @@ pub trait ExpandDatabase: SourceDatabase {
fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode;
/// Implementation for the macro case.
- #[salsa::lru(512)]
+ #[salsa::transparent]
fn parse_macro_expansion(
&self,
macro_file: MacroCallId,
- ) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)>;
+ ) -> &ExpandResult<(Parse<SyntaxNode>, ExpansionSpanMap)>;
#[salsa::transparent]
#[salsa::invoke(SpanMap::new)]
- fn span_map(&self, file_id: HirFileId) -> SpanMap;
+ fn span_map(&self, file_id: HirFileId) -> SpanMap<'_>;
#[salsa::transparent]
#[salsa::invoke(crate::span_map::expansion_span_map)]
- fn expansion_span_map(&self, file_id: MacroCallId) -> Arc<ExpansionSpanMap>;
+ fn expansion_span_map(&self, file_id: MacroCallId) -> &ExpansionSpanMap;
#[salsa::invoke(crate::span_map::real_span_map)]
- fn real_span_map(&self, file_id: EditionedFileId) -> Arc<RealSpanMap>;
-
- /// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
- /// reason why we use salsa at all.
- ///
- /// We encode macro definitions into ids of macro calls, this what allows us
- /// to be incremental.
- #[salsa::transparent]
- fn intern_macro_call(&self, macro_call: MacroCallLoc) -> MacroCallId;
#[salsa::transparent]
- fn lookup_intern_macro_call(&self, macro_call: MacroCallId) -> MacroCallLoc;
+ fn real_span_map(&self, file_id: EditionedFileId) -> &RealSpanMap;
/// Lowers syntactic macro call to a token tree representation. That's a firewall
/// query, only typing in the macro call itself changes the returned
/// subtree.
#[deprecated = "calling this is incorrect, call `macro_arg_considering_derives` instead"]
#[salsa::invoke(macro_arg)]
- fn macro_arg(&self, id: MacroCallId) -> MacroArgResult;
+ #[salsa::transparent]
+ fn macro_arg(&self, id: MacroCallId) -> &MacroArgResult;
#[salsa::transparent]
- fn macro_arg_considering_derives(
- &self,
+ fn macro_arg_considering_derives<'db>(
+ &'db self,
id: MacroCallId,
kind: &MacroCallKind,
- ) -> MacroArgResult;
+ ) -> &'db MacroArgResult;
/// Fetches the expander for this macro.
#[salsa::transparent]
#[salsa::invoke(TokenExpander::macro_expander)]
- fn macro_expander(&self, id: MacroDefId) -> TokenExpander;
+ fn macro_expander(&self, id: MacroDefId) -> TokenExpander<'_>;
/// Fetches (and compiles) the expander of this decl macro.
#[salsa::invoke(DeclarativeMacroExpander::expander)]
+ #[salsa::transparent]
fn decl_macro_expander(
&self,
def_crate: Crate,
id: AstId<ast::Macro>,
- ) -> Arc<DeclarativeMacroExpander>;
+ ) -> &DeclarativeMacroExpander;
/// 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! See #4315 for details
#[salsa::invoke(expand_proc_macro)]
- fn expand_proc_macro(&self, call: MacroCallId) -> ExpandResult<Arc<tt::TopSubtree>>;
+ #[salsa::transparent]
+ fn expand_proc_macro(&self, call: MacroCallId) -> &ExpandResult<tt::TopSubtree>;
/// Retrieves the span to be used for a proc-macro expansions spans.
/// This is a firewall query as it requires parsing the file, which we don't want proc-macros to
/// directly depend on as that would cause to frequent invalidations, mainly because of the
@@ -134,12 +129,12 @@ pub trait ExpandDatabase: SourceDatabase {
#[salsa::invoke_interned(proc_macro_span)]
fn proc_macro_span(&self, fun: AstId<ast::Fn>) -> Span;
- /// Firewall query that returns the errors from the `parse_macro_expansion` query.
#[salsa::invoke(parse_macro_expansion_error)]
+ #[salsa::transparent]
fn parse_macro_expansion_error(
&self,
macro_call: MacroCallId,
- ) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>>;
+ ) -> Option<ExpandResult<Arc<[SyntaxError]>>>;
#[salsa::transparent]
fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContext;
@@ -154,7 +149,7 @@ fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) ->
match file {
HirFileId::FileId(_) => SyntaxContext::root(edition),
HirFileId::MacroFile(m) => {
- let kind = db.lookup_intern_macro_call(m).kind;
+ let kind = m.loc(db).kind;
db.macro_arg_considering_derives(m, &kind).2.ctx
}
}
@@ -177,11 +172,11 @@ pub fn expand_speculative(
speculative_args: &SyntaxNode,
token_to_map: SyntaxToken,
) -> Option<(SyntaxNode, Vec<(SyntaxToken, u8)>)> {
- let loc = db.lookup_intern_macro_call(actual_macro_call);
- let (_, _, span) = db.macro_arg_considering_derives(actual_macro_call, &loc.kind);
+ let loc = actual_macro_call.loc(db);
+ let (_, _, span) = *db.macro_arg_considering_derives(actual_macro_call, &loc.kind);
let span_map = RealSpanMap::absolute(span.anchor.file_id);
- let span_map = SpanMapRef::RealSpanMap(&span_map);
+ let span_map = SpanMap::RealSpanMap(&span_map);
// Build the subtree and token mapping for the speculative args
let (mut tt, undo_info) = match &loc.kind {
@@ -358,48 +353,42 @@ fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
// FIXME: We should verify that the parsed node is one of the many macro node variants we expect
// instead of having it be untyped
+#[salsa_macros::tracked(returns(ref), lru = 512)]
fn parse_macro_expansion(
db: &dyn ExpandDatabase,
macro_file: MacroCallId,
-) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)> {
+) -> ExpandResult<(Parse<SyntaxNode>, ExpansionSpanMap)> {
let _p = tracing::info_span!("parse_macro_expansion").entered();
- let loc = db.lookup_intern_macro_call(macro_file);
+ let loc = macro_file.loc(db);
let expand_to = loc.expand_to();
let mbe::ValueResult { value: (tt, matched_arm), err } = macro_expand(db, macro_file, loc);
- let (parse, mut rev_token_map) = token_tree_to_syntax_node(
- db,
- match &tt {
- CowArc::Arc(it) => it,
- CowArc::Owned(it) => it,
- },
- expand_to,
- );
+ let (parse, mut rev_token_map) = token_tree_to_syntax_node(db, &tt, expand_to);
rev_token_map.matched_arm = matched_arm;
- ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
+ ExpandResult { value: (parse, rev_token_map), err }
}
fn parse_macro_expansion_error(
db: &dyn ExpandDatabase,
macro_call_id: MacroCallId,
-) -> Option<Arc<ExpandResult<Arc<[SyntaxError]>>>> {
+) -> Option<ExpandResult<Arc<[SyntaxError]>>> {
let e: ExpandResult<Arc<[SyntaxError]>> =
- db.parse_macro_expansion(macro_call_id).map(|it| Arc::from(it.0.errors()));
- if e.value.is_empty() && e.err.is_none() { None } else { Some(Arc::new(e)) }
+ db.parse_macro_expansion(macro_call_id).as_ref().map(|it| Arc::from(it.0.errors()));
+ if e.value.is_empty() && e.err.is_none() { None } else { Some(e) }
}
pub(crate) fn parse_with_map(
db: &dyn ExpandDatabase,
file_id: HirFileId,
-) -> (Parse<SyntaxNode>, SpanMap) {
+) -> (Parse<SyntaxNode>, SpanMap<'_>) {
match file_id {
HirFileId::FileId(file_id) => {
(file_id.parse(db).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(file_id)))
}
HirFileId::MacroFile(macro_file) => {
- let (parse, map) = db.parse_macro_expansion(macro_file).value;
- (parse, SpanMap::ExpansionSpanMap(map))
+ let (parse, map) = &db.parse_macro_expansion(macro_file).value;
+ (parse.clone(), SpanMap::ExpansionSpanMap(map))
}
}
}
@@ -409,11 +398,11 @@ pub(crate) fn parse_with_map(
///
/// This is not connected to the database so it does not cached the result. However, the inner [macro_arg] query is
#[allow(deprecated)] // we are macro_arg_considering_derives
-fn macro_arg_considering_derives(
- db: &dyn ExpandDatabase,
+fn macro_arg_considering_derives<'db>(
+ db: &'db dyn ExpandDatabase,
id: MacroCallId,
kind: &MacroCallKind,
-) -> MacroArgResult {
+) -> &'db MacroArgResult {
match kind {
// Get the macro arg for the derive macro
MacroCallKind::Derive { derive_macro_id, .. } => db.macro_arg(*derive_macro_id),
@@ -422,8 +411,9 @@ fn macro_arg_considering_derives(
}
}
+#[salsa_macros::tracked(returns(ref))]
fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
- let loc = db.lookup_intern_macro_call(id);
+ let loc = id.loc(db);
if let MacroCallLoc {
def: MacroDefId { kind: MacroDefKind::BuiltInEager(..), .. },
@@ -447,10 +437,10 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
let dummy_tt = |kind| {
(
- Arc::new(tt::TopSubtree::from_token_trees(
+ tt::TopSubtree::from_token_trees(
tt::Delimiter { open: span, close: span, kind },
tt::TokenTreesView::empty(),
- )),
+ ),
SyntaxFixupUndoInfo::default(),
span,
)
@@ -487,7 +477,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
let mut tt = syntax_bridge::syntax_node_to_token_tree(
tt.syntax(),
- map.as_ref(),
+ map,
span,
if loc.def.is_proc_macro() {
DocCommentDesugarMode::ProcMacro
@@ -499,7 +489,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
// proc macros expect their inputs without parentheses, MBEs expect it with them included
tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
}
- return (Arc::new(tt), SyntaxFixupUndoInfo::NONE, span);
+ return (tt, SyntaxFixupUndoInfo::NONE, span);
}
// MacroCallKind::Derive should not be here. As we are getting the argument for the derive macro
MacroCallKind::Derive { .. } => {
@@ -522,7 +512,7 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
let (mut tt, undo_info) = attr_macro_input_to_token_tree(
db,
item_node.syntax(),
- map.as_ref(),
+ map,
span,
is_derive,
censor_item_tree_attr_ids,
@@ -534,11 +524,11 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
tt.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
}
- (Arc::new(tt), undo_info, span)
+ (tt, undo_info, span)
}
-impl TokenExpander {
- fn macro_expander(db: &dyn ExpandDatabase, id: MacroDefId) -> TokenExpander {
+impl<'db> TokenExpander<'db> {
+ fn macro_expander(db: &'db dyn ExpandDatabase, id: MacroDefId) -> TokenExpander<'db> {
match id.kind {
MacroDefKind::Declarative(ast_id, _) => {
TokenExpander::DeclarativeMacro(db.decl_macro_expander(id.krate, ast_id))
@@ -552,27 +542,26 @@ impl TokenExpander {
}
}
-enum CowArc<T> {
- Arc(Arc<T>),
- Owned(T),
-}
-
fn macro_expand(
db: &dyn ExpandDatabase,
macro_call_id: MacroCallId,
loc: MacroCallLoc,
-) -> ExpandResult<(CowArc<tt::TopSubtree>, MatchedArmIndex)> {
+) -> ExpandResult<(Cow<'_, tt::TopSubtree>, MatchedArmIndex)> {
let _p = tracing::info_span!("macro_expand").entered();
let (ExpandResult { value: (tt, matched_arm), err }, span) = match loc.def.kind {
MacroDefKind::ProcMacro(..) => {
- return db.expand_proc_macro(macro_call_id).map(CowArc::Arc).zip_val(None);
+ return db
+ .expand_proc_macro(macro_call_id)
+ .as_ref()
+ .map(|it| (Cow::Borrowed(it), None));
}
_ => {
let (macro_arg, undo_info, span) =
db.macro_arg_considering_derives(macro_call_id, &loc.kind);
+ let span = *span;
- let arg = &*macro_arg;
+ let arg = macro_arg;
let res = match loc.def.kind {
MacroDefKind::Declarative(id, _) => db
.decl_macro_expander(loc.def.krate, id)
@@ -592,7 +581,7 @@ fn macro_expand(
// As such we just return the input subtree here.
let eager = match &loc.kind {
MacroCallKind::FnLike { eager: None, .. } => {
- return ExpandResult::ok(CowArc::Arc(macro_arg.clone())).zip_val(None);
+ return ExpandResult::ok(Cow::Borrowed(macro_arg)).zip_val(None);
}
MacroCallKind::FnLike { eager: Some(eager), .. } => Some(&**eager),
_ => None,
@@ -608,7 +597,7 @@ fn macro_expand(
}
MacroDefKind::BuiltInAttr(_, it) => {
let mut res = it.expand(db, macro_call_id, arg, span);
- fixup::reverse_fixups(&mut res.value, &undo_info);
+ fixup::reverse_fixups(&mut res.value, undo_info);
res.zip_val(None)
}
MacroDefKind::ProcMacro(_, _, _) => unreachable!(),
@@ -622,12 +611,12 @@ fn macro_expand(
// Set a hard limit for the expanded tt
if let Err(value) = check_tt_count(&tt) {
return value
- .map(|()| CowArc::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span))))
+ .map(|()| Cow::Owned(tt::TopSubtree::empty(tt::DelimSpan::from_single(span))))
.zip_val(matched_arm);
}
}
- ExpandResult { value: (CowArc::Owned(tt), matched_arm), err }
+ ExpandResult { value: (Cow::Owned(tt), matched_arm), err }
}
fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span {
@@ -641,11 +630,9 @@ fn proc_macro_span(db: &dyn ExpandDatabase, ast: AstId<ast::Fn>) -> Span {
span_map.span_for_range(range)
}
-fn expand_proc_macro(
- db: &dyn ExpandDatabase,
- id: MacroCallId,
-) -> ExpandResult<Arc<tt::TopSubtree>> {
- let loc = db.lookup_intern_macro_call(id);
+#[salsa_macros::tracked(returns(ref))]
+fn expand_proc_macro(db: &dyn ExpandDatabase, id: MacroCallId) -> ExpandResult<tt::TopSubtree> {
+ let loc = id.loc(db);
let (macro_arg, undo_info, span) = db.macro_arg_considering_derives(id, &loc.kind);
let (ast, expander) = match loc.def.kind {
@@ -664,7 +651,7 @@ fn expand_proc_macro(
db,
loc.def.krate,
loc.krate,
- &macro_arg,
+ macro_arg,
attr_arg,
span_with_def_site_ctxt(db, span, id.into(), loc.def.edition),
span_with_call_site_ctxt(db, span, id.into(), loc.def.edition),
@@ -674,12 +661,12 @@ fn expand_proc_macro(
// Set a hard limit for the expanded tt
if let Err(value) = check_tt_count(&tt) {
- return value.map(|()| Arc::new(tt::TopSubtree::empty(tt::DelimSpan::from_single(span))));
+ return value.map(|()| tt::TopSubtree::empty(tt::DelimSpan::from_single(*span)));
}
- fixup::reverse_fixups(&mut tt, &undo_info);
+ fixup::reverse_fixups(&mut tt, undo_info);
- ExpandResult { value: Arc::new(tt), err }
+ ExpandResult { value: tt, err }
}
pub(crate) fn token_tree_to_syntax_node(
@@ -714,11 +701,3 @@ fn check_tt_count(tt: &tt::TopSubtree) -> Result<(), ExpandResult<()>> {
})
}
}
-
-fn intern_macro_call(db: &dyn ExpandDatabase, macro_call: MacroCallLoc) -> MacroCallId {
- MacroCallId::new(db, macro_call)
-}
-
-fn lookup_intern_macro_call(db: &dyn ExpandDatabase, macro_call: MacroCallId) -> MacroCallLoc {
- macro_call.loc(db)
-}
diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs
index 4b2c6e7351..99db0dbcb9 100644
--- a/crates/hir-expand/src/declarative.rs
+++ b/crates/hir-expand/src/declarative.rs
@@ -10,7 +10,6 @@ use syntax::{
ast::{self, HasAttrs},
};
use syntax_bridge::DocCommentDesugarMode;
-use triomphe::Arc;
use crate::{
AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, Lookup, MacroCallId,
@@ -37,7 +36,7 @@ impl DeclarativeMacroExpander {
call_id: MacroCallId,
span: Span,
) -> ExpandResult<(tt::TopSubtree, Option<u32>)> {
- let loc = db.lookup_intern_macro_call(call_id);
+ let loc = call_id.loc(db);
match self.mac.err() {
Some(_) => ExpandResult::new(
(tt::TopSubtree::empty(tt::DelimSpan { open: span, close: span }), None),
@@ -78,12 +77,16 @@ impl DeclarativeMacroExpander {
.map_err(Into::into),
}
}
+}
+#[salsa::tracked]
+impl DeclarativeMacroExpander {
+ #[salsa::tracked(returns(ref))]
pub(crate) fn expander(
db: &dyn ExpandDatabase,
def_crate: Crate,
id: AstId<ast::Macro>,
- ) -> Arc<DeclarativeMacroExpander> {
+ ) -> DeclarativeMacroExpander {
let (root, map) = crate::db::parse_with_map(db, id.file_id);
let root = root.syntax_node();
@@ -117,8 +120,7 @@ impl DeclarativeMacroExpander {
def_crate.data(db).edition
} else {
// UNWRAP-SAFETY: Only the root context has no outer expansion
- let krate =
- db.lookup_intern_macro_call(ctx.outer_expn(db).unwrap().into()).def.krate;
+ let krate = crate::MacroCallId::from(ctx.outer_expn(db).unwrap()).loc(db).def.krate;
krate.data(db).edition
}
};
@@ -128,7 +130,7 @@ impl DeclarativeMacroExpander {
Some(arg) => {
let tt = syntax_bridge::syntax_node_to_token_tree(
arg.syntax(),
- map.as_ref(),
+ map,
map.span_for_range(
macro_rules.macro_rules_token().unwrap().text_range(),
),
@@ -152,14 +154,14 @@ impl DeclarativeMacroExpander {
let args = macro_def.args().map(|args| {
syntax_bridge::syntax_node_to_token_tree(
args.syntax(),
- map.as_ref(),
+ map,
span,
DocCommentDesugarMode::Mbe,
)
});
let body = syntax_bridge::syntax_node_to_token_tree(
body.syntax(),
- map.as_ref(),
+ map,
span,
DocCommentDesugarMode::Mbe,
);
@@ -177,6 +179,6 @@ impl DeclarativeMacroExpander {
HirFileId::MacroFile(macro_file) => macro_file.lookup(db).ctxt,
HirFileId::FileId(file) => SyntaxContext::root(file.edition(db)),
});
- Arc::new(DeclarativeMacroExpander { mac, transparency, edition })
+ DeclarativeMacroExpander { mac, transparency, edition }
}
}
diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs
index 0b6124ebf3..a19f58709b 100644
--- a/crates/hir-expand/src/eager.rs
+++ b/crates/hir-expand/src/eager.rs
@@ -20,9 +20,10 @@
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
use base_db::Crate;
use span::SyntaxContext;
-use syntax::{AstPtr, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent, ted};
+use syntax::{
+ AstPtr, Parse, SyntaxElement, SyntaxNode, TextSize, WalkEvent, syntax_editor::SyntaxEditor,
+};
use syntax_bridge::DocCommentDesugarMode;
-use triomphe::Arc;
use crate::{
AstId, EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile,
@@ -59,7 +60,7 @@ pub fn expand_eager_macro_input(
kind: MacroCallKind::FnLike { ast_id, expand_to: ExpandTo::Expr, eager: None },
ctxt: call_site,
};
- let arg_id = db.intern_macro_call(loc);
+ let arg_id = MacroCallId::new(db, loc);
#[allow(deprecated)] // builtin eager macros are never derives
let (_, _, span) = db.macro_arg(arg_id);
let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } =
@@ -70,7 +71,7 @@ pub fn expand_eager_macro_input(
let ExpandResult { value: expanded_eager_input, err } = {
eager_macro_recur(
db,
- &arg_exp_map,
+ arg_exp_map,
&mut arg_map,
TextSize::new(0),
InFile::new(arg_id.into(), arg_exp.syntax_node()),
@@ -80,7 +81,7 @@ pub fn expand_eager_macro_input(
eager_callback,
)
};
- let err = parse_err.or(err);
+ let err = parse_err.clone().or(err);
if cfg!(debug_assertions) {
arg_map.finish();
}
@@ -92,7 +93,7 @@ pub fn expand_eager_macro_input(
let mut subtree = syntax_bridge::syntax_node_to_token_tree(
&expanded_eager_input,
arg_map,
- span,
+ *span,
DocCommentDesugarMode::Mbe,
);
@@ -104,28 +105,28 @@ pub fn expand_eager_macro_input(
kind: MacroCallKind::FnLike {
ast_id,
expand_to,
- eager: Some(Arc::new(EagerCallInfo {
- arg: Arc::new(subtree),
+ eager: Some(Box::new(EagerCallInfo {
+ arg: subtree,
arg_id,
error: err.clone(),
- span,
+ span: *span,
})),
},
ctxt: call_site,
};
- ExpandResult { value: Some(db.intern_macro_call(loc)), err }
+ ExpandResult { value: Some(MacroCallId::new(db, loc)), err }
}
-fn lazy_expand(
- db: &dyn ExpandDatabase,
+fn lazy_expand<'db>(
+ db: &'db dyn ExpandDatabase,
def: &MacroDefId,
macro_call: &ast::MacroCall,
ast_id: AstId<ast::MacroCall>,
krate: Crate,
call_site: SyntaxContext,
eager_callback: EagerCallBackFn<'_>,
-) -> ExpandResult<(InFile<Parse<SyntaxNode>>, Arc<ExpansionSpanMap>)> {
+) -> ExpandResult<(InFile<Parse<SyntaxNode>>, &'db ExpansionSpanMap)> {
let expand_to = ExpandTo::from_call_site(macro_call);
let id = def.make_call(
db,
@@ -135,7 +136,9 @@ fn lazy_expand(
);
eager_callback(ast_id.map(|ast_id| (AstPtr::new(macro_call), ast_id)), id);
- db.parse_macro_expansion(id).map(|parse| (InFile::new(id.into(), parse.0), parse.1))
+ db.parse_macro_expansion(id)
+ .as_ref()
+ .map(|parse| (InFile::new(id.into(), parse.0.clone()), &parse.1))
}
fn eager_macro_recur(
@@ -149,7 +152,8 @@ fn eager_macro_recur(
macro_resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>,
eager_callback: EagerCallBackFn<'_>,
) -> ExpandResult<Option<(SyntaxNode, TextSize)>> {
- let original = curr.value.clone_for_update();
+ let (editor, _) = SyntaxEditor::new(curr.value.clone());
+ let original = curr.value.clone();
let mut replacements = Vec::new();
@@ -232,7 +236,7 @@ fn eager_macro_recur(
syntax_node.clone_for_update(),
offset + syntax_node.text_range().len(),
)),
- err: err.or(err2),
+ err: err.clone().or_else(|| err2.clone()),
}
}
None => ExpandResult { value: None, err },
@@ -256,7 +260,7 @@ fn eager_macro_recur(
// replace macro inside
let ExpandResult { value, err: error } = eager_macro_recur(
db,
- &tm,
+ tm,
expanded_map,
offset,
// FIXME: We discard parse errors here
@@ -288,6 +292,7 @@ fn eager_macro_recur(
}
}
- replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
+ replacements.into_iter().rev().for_each(|(old, new)| editor.replace(old.syntax(), new));
+ let original = editor.finish().new_root().clone();
ExpandResult { value: Some((original, offset)), err: error }
}
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index 71da560b15..a4c206156d 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -128,6 +128,16 @@ impl ErasedAstId {
}
}
+impl<FileKind, N: AstNode> InFileWrapper<FileKind, AstPtr<N>> {
+ #[inline]
+ pub fn upcast<M: AstNode>(self) -> InFileWrapper<FileKind, AstPtr<M>>
+ where
+ N: Into<M>,
+ {
+ self.map(|it| it.upcast())
+ }
+}
+
impl<FileKind, T> InFileWrapper<FileKind, T> {
pub fn new(file_id: FileKind, value: T) -> Self {
Self { file_id, value }
@@ -256,8 +266,10 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> {
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
Some(parent) => Some(node.with_value(parent)),
- None => db
- .lookup_intern_macro_call(node.file_id.macro_file()?)
+ None => node
+ .file_id
+ .macro_file()?
+ .loc(db)
.to_node_item(db)
.syntax()
.cloned()
@@ -273,8 +285,10 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> {
) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
Some(parent) => Some(node.with_value(parent)),
- None => db
- .lookup_intern_macro_call(node.file_id.macro_file()?)
+ None => node
+ .file_id
+ .macro_file()?
+ .loc(db)
.to_node_item(db)
.syntax()
.cloned()
@@ -328,7 +342,7 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> {
let FileRange { file_id: editioned_file_id, range } = map_node_range_up_rooted(
db,
- &db.expansion_span_map(file_id),
+ db.expansion_span_map(file_id),
self.value.borrow().text_range(),
)?;
@@ -371,7 +385,7 @@ impl InFile<SyntaxToken> {
HirFileId::MacroFile(mac_file) => {
let (range, ctxt) = span_for_offset(
db,
- &db.expansion_span_map(mac_file),
+ db.expansion_span_map(mac_file),
self.value.text_range().start(),
);
@@ -382,7 +396,7 @@ impl InFile<SyntaxToken> {
}
// Fall back to whole macro call.
- let loc = db.lookup_intern_macro_call(mac_file);
+ let loc = mac_file.loc(db);
loc.kind.original_call_range(db, loc.krate)
}
}
@@ -397,7 +411,7 @@ impl InFile<SyntaxToken> {
HirFileId::MacroFile(mac_file) => {
let (range, ctxt) = span_for_offset(
db,
- &db.expansion_span_map(mac_file),
+ db.expansion_span_map(mac_file),
self.value.text_range().start(),
);
@@ -411,7 +425,7 @@ impl InFile<SyntaxToken> {
impl InMacroFile<TextSize> {
pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> (FileRange, SyntaxContext) {
- span_for_offset(db, &db.expansion_span_map(self.file_id), self.value)
+ span_for_offset(db, db.expansion_span_map(self.file_id), self.value)
}
}
@@ -425,10 +439,10 @@ impl InFile<TextRange> {
(FileRange { file_id, range: self.value }, SyntaxContext::root(file_id.edition(db)))
}
HirFileId::MacroFile(mac_file) => {
- match map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) {
+ match map_node_range_up(db, db.expansion_span_map(mac_file), self.value) {
Some(it) => it,
None => {
- let loc = db.lookup_intern_macro_call(mac_file);
+ let loc = mac_file.loc(db);
(
loc.kind.original_call_range(db, loc.krate),
SyntaxContext::root(loc.def.edition),
@@ -443,10 +457,10 @@ impl InFile<TextRange> {
match self.file_id {
HirFileId::FileId(file_id) => FileRange { file_id, range: self.value },
HirFileId::MacroFile(mac_file) => {
- match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) {
+ match map_node_range_up_rooted(db, db.expansion_span_map(mac_file), self.value) {
Some(it) => it,
_ => {
- let loc = db.lookup_intern_macro_call(mac_file);
+ let loc = mac_file.loc(db);
loc.kind.original_call_range(db, loc.krate)
}
}
@@ -461,10 +475,10 @@ impl InFile<TextRange> {
match self.file_id {
HirFileId::FileId(file_id) => FileRange { file_id, range: self.value },
HirFileId::MacroFile(mac_file) => {
- match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) {
+ match map_node_range_up_rooted(db, db.expansion_span_map(mac_file), self.value) {
Some(it) => it,
_ => {
- let loc = db.lookup_intern_macro_call(mac_file);
+ let loc = mac_file.loc(db);
loc.kind.original_call_range_with_input(db)
}
}
@@ -482,7 +496,7 @@ impl InFile<TextRange> {
SyntaxContext::root(file_id.edition(db)),
)),
HirFileId::MacroFile(mac_file) => {
- map_node_range_up(db, &db.expansion_span_map(mac_file), self.value)
+ map_node_range_up(db, db.expansion_span_map(mac_file), self.value)
}
}
}
@@ -494,7 +508,7 @@ impl InFile<TextRange> {
match self.file_id {
HirFileId::FileId(file_id) => Some(FileRange { file_id, range: self.value }),
HirFileId::MacroFile(mac_file) => {
- map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value)
+ map_node_range_up_rooted(db, db.expansion_span_map(mac_file), self.value)
}
}
}
@@ -516,7 +530,7 @@ impl<N: AstNode> InFile<N> {
let FileRange { file_id: editioned_file_id, range } = map_node_range_up_rooted(
db,
- &db.expansion_span_map(file_id),
+ db.expansion_span_map(file_id),
self.value.syntax().text_range(),
)?;
diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs
index 424655ed65..939104b709 100644
--- a/crates/hir-expand/src/fixup.rs
+++ b/crates/hir-expand/src/fixup.rs
@@ -14,11 +14,11 @@ use syntax::{
match_ast,
};
use syntax_bridge::DocCommentDesugarMode;
-use triomphe::Arc;
+use thin_vec::ThinVec;
use tt::{Spacing, TransformTtAction, transform_tt};
use crate::{
- span_map::SpanMapRef,
+ span_map::SpanMap,
tt::{self, Ident, Leaf, Punct, TopSubtree},
};
@@ -35,8 +35,7 @@ pub(crate) struct SyntaxFixups {
/// This is the information needed to reverse the fixups.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct SyntaxFixupUndoInfo {
- // FIXME: ThinArc<[Subtree]>
- original: Option<Arc<Box<[TopSubtree]>>>,
+ original: Option<ThinVec<TopSubtree>>,
}
impl SyntaxFixupUndoInfo {
@@ -51,7 +50,7 @@ const FIXUP_DUMMY_RANGE: TextRange = TextRange::empty(TextSize::new(0));
const FIXUP_DUMMY_RANGE_END: TextSize = TextSize::new(!0);
pub(crate) fn fixup_syntax(
- span_map: SpanMapRef<'_>,
+ span_map: SpanMap<'_>,
node: &SyntaxNode,
call_site: Span,
mode: DocCommentDesugarMode,
@@ -59,7 +58,7 @@ pub(crate) fn fixup_syntax(
let mut append = FxHashMap::<SyntaxElement, _>::default();
let mut remove = FxHashSet::<SyntaxElement>::default();
let mut preorder = node.preorder();
- let mut original = Vec::new();
+ let mut original = ThinVec::new();
let dummy_range = FIXUP_DUMMY_RANGE;
let fake_span = |range| {
let span = span_map.span_for_range(range);
@@ -317,13 +316,12 @@ pub(crate) fn fixup_syntax(
}
}
}
+ original.shrink_to_fit();
let needs_fixups = !append.is_empty() || !original.is_empty();
SyntaxFixups {
append,
remove,
- undo_info: SyntaxFixupUndoInfo {
- original: needs_fixups.then(|| Arc::new(original.into_boxed_slice())),
- },
+ undo_info: SyntaxFixupUndoInfo { original: needs_fixups.then_some(original) },
}
}
@@ -340,7 +338,7 @@ fn has_error_to_handle(node: &SyntaxNode) -> bool {
}
pub(crate) fn reverse_fixups(tt: &mut TopSubtree, undo_info: &SyntaxFixupUndoInfo) {
- let Some(undo_info) = undo_info.original.as_deref() else { return };
+ let Some(undo_info) = &undo_info.original else { return };
let undo_info = &**undo_info;
let top_subtree = tt.top_subtree();
let open_span = top_subtree.delimiter.open;
@@ -402,7 +400,6 @@ mod tests {
use span::{Edition, EditionedFileId, FileId};
use syntax::TextRange;
use syntax_bridge::DocCommentDesugarMode;
- use triomphe::Arc;
use crate::{
fixup::reverse_fixups,
@@ -440,19 +437,19 @@ mod tests {
#[track_caller]
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, mut expect: Expect) {
let parsed = syntax::SourceFile::parse(ra_fixture, span::Edition::CURRENT);
- let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(EditionedFileId::new(
+ let span_map = SpanMap::RealSpanMap(&RealSpanMap::absolute(EditionedFileId::new(
FileId::from_raw(0),
Edition::CURRENT,
- ))));
+ )));
let fixups = super::fixup_syntax(
- span_map.as_ref(),
+ span_map,
&parsed.syntax_node(),
span_map.span_for_range(TextRange::empty(0.into())),
DocCommentDesugarMode::Mbe,
);
let mut tt = syntax_bridge::syntax_node_to_token_tree_modified(
&parsed.syntax_node(),
- span_map.as_ref(),
+ span_map,
fixups.append,
fixups.remove,
span_map.span_for_range(TextRange::empty(0.into())),
@@ -494,7 +491,7 @@ mod tests {
// modulo token IDs and `Punct`s' spacing.
let original_as_tt = syntax_bridge::syntax_node_to_token_tree(
&parsed.syntax_node(),
- span_map.as_ref(),
+ span_map,
span_map.span_for_range(TextRange::empty(0.into())),
DocCommentDesugarMode::Mbe,
);
diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs
index ce7650d077..1cf8ce2a57 100644
--- a/crates/hir-expand/src/hygiene.rs
+++ b/crates/hir-expand/src/hygiene.rs
@@ -81,7 +81,7 @@ pub(super) fn apply_mark(
return apply_mark_internal(db, ctxt, call_id, transparency, edition);
}
- let call_site_ctxt = db.lookup_intern_macro_call(call_id.into()).ctxt;
+ let call_site_ctxt = crate::MacroCallId::from(call_id).loc(db).ctxt;
let mut call_site_ctxt = if transparency == Transparency::SemiOpaque {
call_site_ctxt.normalize_to_macros_2_0(db)
} else {
diff --git a/crates/hir-expand/src/inert_attr_macro.rs b/crates/hir-expand/src/inert_attr_macro.rs
index 53b624d9a6..4185b7b018 100644
--- a/crates/hir-expand/src/inert_attr_macro.rs
+++ b/crates/hir-expand/src/inert_attr_macro.rs
@@ -702,6 +702,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
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),
+ rustc_attr!(TEST, rustc_dyn_incompatible_trait, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_dump_vtable, Normal, template!(Word), WarnFollowing),
rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/), DuplicatesOk),
gated!(
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 8d42a24e2f..0850d6156d 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -69,12 +69,12 @@ pub use tt;
#[macro_export]
macro_rules! impl_intern_lookup {
- ($db:ident, $id:ident, $loc:ident, $intern:ident, $lookup:ident) => {
+ ($db:ident, $id:ident, $loc:ident) => {
impl $crate::Intern for $loc {
type Database = dyn $db;
type ID = $id;
fn intern(self, db: &Self::Database) -> Self::ID {
- db.$intern(self)
+ $id::new(db, self)
}
}
@@ -82,7 +82,7 @@ macro_rules! impl_intern_lookup {
type Database = dyn $db;
type Data = $loc;
fn lookup(&self, db: &Self::Database) -> Self::Data {
- db.$lookup(*self)
+ self.loc(db)
}
}
};
@@ -101,13 +101,7 @@ pub trait Lookup {
fn lookup(&self, db: &Self::Database) -> Self::Data;
}
-impl_intern_lookup!(
- ExpandDatabase,
- MacroCallId,
- MacroCallLoc,
- intern_macro_call,
- lookup_intern_macro_call
-);
+impl_intern_lookup!(ExpandDatabase, MacroCallId, MacroCallLoc);
pub type ExpandResult<T> = ValueResult<T, ExpandError>;
@@ -279,7 +273,7 @@ impl MacroDefKind {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct EagerCallInfo {
/// The expanded argument of the eager macro.
- arg: Arc<tt::TopSubtree>,
+ arg: tt::TopSubtree,
/// Call id of the eager macro's input file (this is the macro file for its fully expanded input).
arg_id: MacroCallId,
error: Option<ExpandError>,
@@ -296,7 +290,7 @@ pub enum MacroCallKind {
/// for the eager input macro file.
// FIXME: This is being interned, subtrees can vary quickly differing just slightly causing
// leakage problems here
- eager: Option<Arc<EagerCallInfo>>,
+ eager: Option<Box<EagerCallInfo>>,
},
Derive {
ast_id: AstId<ast::Adt>,
@@ -311,7 +305,7 @@ pub enum MacroCallKind {
Attr {
ast_id: AstId<ast::Item>,
// FIXME: This shouldn't be here, we can derive this from `invoc_attr_index`.
- attr_args: Option<Arc<tt::TopSubtree>>,
+ attr_args: Option<Box<tt::TopSubtree>>,
/// This contains the list of all *active* attributes (derives and attr macros) preceding this
/// attribute, including this attribute. You can retrieve the [`AttrId`] of the current attribute
/// by calling [`invoc_attr()`] on this.
@@ -386,7 +380,7 @@ impl HirFileId {
pub fn edition(self, db: &dyn ExpandDatabase) -> Edition {
match self {
HirFileId::FileId(file_id) => file_id.edition(db),
- HirFileId::MacroFile(m) => db.lookup_intern_macro_call(m).def.edition,
+ HirFileId::MacroFile(m) => m.loc(db).def.edition,
}
}
pub fn original_file(self, db: &dyn ExpandDatabase) -> EditionedFileId {
@@ -395,7 +389,7 @@ impl HirFileId {
match file_id {
HirFileId::FileId(id) => break id,
HirFileId::MacroFile(macro_call_id) => {
- file_id = db.lookup_intern_macro_call(macro_call_id).kind.file_id()
+ file_id = macro_call_id.loc(db).kind.file_id()
}
}
}
@@ -406,7 +400,7 @@ impl HirFileId {
match self {
HirFileId::FileId(id) => break id,
HirFileId::MacroFile(file) => {
- let loc = db.lookup_intern_macro_call(file);
+ let loc = file.loc(db);
if loc.def.is_include()
&& let MacroCallKind::FnLike { eager: Some(eager), .. } = &loc.kind
&& let Ok(it) = include_input_to_file_id(db, file, &eager.arg)
@@ -420,21 +414,21 @@ impl HirFileId {
}
pub fn original_call_node(self, db: &dyn ExpandDatabase) -> Option<InRealFile<SyntaxNode>> {
- let mut call = db.lookup_intern_macro_call(self.macro_file()?).to_node(db);
+ let mut call = self.macro_file()?.loc(db).to_node(db);
loop {
match call.file_id {
HirFileId::FileId(file_id) => {
break Some(InRealFile { file_id, value: call.value });
}
HirFileId::MacroFile(macro_call_id) => {
- call = db.lookup_intern_macro_call(macro_call_id).to_node(db);
+ call = macro_call_id.loc(db).to_node(db);
}
}
}
}
pub fn call_node(self, db: &dyn ExpandDatabase) -> Option<InFile<SyntaxNode>> {
- Some(db.lookup_intern_macro_call(self.macro_file()?).to_node(db))
+ Some(self.macro_file()?.loc(db).to_node(db))
}
pub fn as_builtin_derive_attr_node(
@@ -442,7 +436,7 @@ impl HirFileId {
db: &dyn ExpandDatabase,
) -> Option<InFile<ast::Attr>> {
let macro_file = self.macro_file()?;
- let loc = db.lookup_intern_macro_call(macro_file);
+ let loc = macro_file.loc(db);
let attr = match loc.def.kind {
MacroDefKind::BuiltInDerive(..) => loc.to_node(db),
_ => return None,
@@ -471,13 +465,13 @@ pub enum MacroKind {
impl MacroCallId {
pub fn call_node(self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode> {
- db.lookup_intern_macro_call(self).to_node(db)
+ self.loc(db).to_node(db)
}
pub fn expansion_level(self, db: &dyn ExpandDatabase) -> u32 {
let mut level = 0;
let mut macro_file = self;
loop {
- let loc = db.lookup_intern_macro_call(macro_file);
+ let loc = macro_file.loc(db);
level += 1;
macro_file = match loc.kind.file_id() {
@@ -487,16 +481,16 @@ impl MacroCallId {
}
}
pub fn parent(self, db: &dyn ExpandDatabase) -> HirFileId {
- db.lookup_intern_macro_call(self).kind.file_id()
+ self.loc(db).kind.file_id()
}
/// Return expansion information if it is a macro-expansion file
- pub fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo {
+ pub fn expansion_info(self, db: &dyn ExpandDatabase) -> ExpansionInfo<'_> {
ExpansionInfo::new(db, self)
}
pub fn kind(self, db: &dyn ExpandDatabase) -> MacroKind {
- match db.lookup_intern_macro_call(self).def.kind {
+ match self.loc(db).def.kind {
MacroDefKind::Declarative(..) => MacroKind::Declarative,
MacroDefKind::BuiltIn(..) | MacroDefKind::BuiltInEager(..) => {
MacroKind::DeclarativeBuiltIn
@@ -510,24 +504,24 @@ impl MacroCallId {
}
pub fn is_include_macro(self, db: &dyn ExpandDatabase) -> bool {
- db.lookup_intern_macro_call(self).def.is_include()
+ self.loc(db).def.is_include()
}
pub fn is_include_like_macro(self, db: &dyn ExpandDatabase) -> bool {
- db.lookup_intern_macro_call(self).def.is_include_like()
+ self.loc(db).def.is_include_like()
}
pub fn is_env_or_option_env(self, db: &dyn ExpandDatabase) -> bool {
- db.lookup_intern_macro_call(self).def.is_env_or_option_env()
+ self.loc(db).def.is_env_or_option_env()
}
pub fn is_eager(self, db: &dyn ExpandDatabase) -> bool {
- let loc = db.lookup_intern_macro_call(self);
+ let loc = self.loc(db);
matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
}
pub fn eager_arg(self, db: &dyn ExpandDatabase) -> Option<MacroCallId> {
- let loc = db.lookup_intern_macro_call(self);
+ let loc = self.loc(db);
match &loc.kind {
MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id),
_ => None,
@@ -535,7 +529,7 @@ impl MacroCallId {
}
pub fn is_derive_attr_pseudo_expansion(self, db: &dyn ExpandDatabase) -> bool {
- let loc = db.lookup_intern_macro_call(self);
+ let loc = self.loc(db);
loc.def.is_attribute_derive()
}
}
@@ -548,7 +542,7 @@ impl MacroDefId {
kind: MacroCallKind,
ctxt: SyntaxContext,
) -> MacroCallId {
- db.intern_macro_call(MacroCallLoc { def: self, krate, kind, ctxt })
+ MacroCallId::new(db, MacroCallLoc { def: self, krate, kind, ctxt })
}
pub fn definition_range(&self, db: &dyn ExpandDatabase) -> InFile<TextRange> {
@@ -725,7 +719,7 @@ impl MacroCallKind {
let file_id = loop {
match kind.file_id() {
HirFileId::MacroFile(file) => {
- kind = db.lookup_intern_macro_call(file).kind;
+ kind = file.loc(db).kind;
}
HirFileId::FileId(file_id) => break file_id,
}
@@ -750,7 +744,7 @@ impl MacroCallKind {
let file_id = loop {
match kind.file_id() {
HirFileId::MacroFile(file) => {
- kind = db.lookup_intern_macro_call(file).kind;
+ kind = file.loc(db).kind;
}
HirFileId::FileId(file_id) => break file_id,
}
@@ -797,16 +791,16 @@ impl MacroCallKind {
// FIXME: can be expensive to create, we should check the use sites and maybe replace them with
// simpler function calls if the map is only used once
#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct ExpansionInfo {
+pub struct ExpansionInfo<'db> {
expanded: InMacroFile<SyntaxNode>,
/// The argument TokenTree or item for attributes
arg: InFile<Option<SyntaxNode>>,
- exp_map: Arc<ExpansionSpanMap>,
- arg_map: SpanMap,
+ exp_map: &'db ExpansionSpanMap,
+ arg_map: SpanMap<'db>,
loc: MacroCallLoc,
}
-impl ExpansionInfo {
+impl<'db> ExpansionInfo<'db> {
pub fn expanded(&self) -> InMacroFile<SyntaxNode> {
self.expanded.clone()
}
@@ -872,7 +866,7 @@ impl ExpansionInfo {
offset: TextSize,
) -> (FileRange, SyntaxContext) {
debug_assert!(self.expanded.value.text_range().contains(offset));
- span_for_offset(db, &self.exp_map, offset)
+ span_for_offset(db, self.exp_map, offset)
}
/// Maps up the text range out of the expansion hierarchy back into the original file its from.
@@ -882,7 +876,7 @@ impl ExpansionInfo {
range: TextRange,
) -> Option<(FileRange, SyntaxContext)> {
debug_assert!(self.expanded.value.text_range().contains_range(range));
- map_node_range_up(db, &self.exp_map, range)
+ map_node_range_up(db, self.exp_map, range)
}
/// Maps up the text range out of the expansion into its macro call.
@@ -918,14 +912,14 @@ impl ExpansionInfo {
}
}
- pub fn new(db: &dyn ExpandDatabase, macro_file: MacroCallId) -> ExpansionInfo {
+ pub fn new(db: &'db dyn ExpandDatabase, macro_file: MacroCallId) -> ExpansionInfo<'db> {
let _p = tracing::info_span!("ExpansionInfo::new").entered();
- let loc = db.lookup_intern_macro_call(macro_file);
+ let loc = macro_file.loc(db);
let arg_tt = loc.kind.arg(db);
let arg_map = db.span_map(arg_tt.file_id);
- let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
+ let (parse, exp_map) = &db.parse_macro_expansion(macro_file).value;
let expanded = InMacroFile { file_id: macro_file, value: parse.syntax_node() };
ExpansionInfo { expanded, loc, arg: arg_tt, exp_map, arg_map }
@@ -1054,6 +1048,11 @@ impl ExpandTo {
intern::impl_internable!(ModPath);
+/// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
+/// reason why we use salsa at all.
+///
+/// We encode macro definitions into ids of macro calls, this what allows us
+/// to be incremental.
#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)]
#[doc(alias = "MacroFileId")]
pub struct MacroCallId {
diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs
index 78228cf82e..9142ad8b92 100644
--- a/crates/hir-expand/src/mod_path.rs
+++ b/crates/hir-expand/src/mod_path.rs
@@ -332,7 +332,7 @@ fn convert_path(
{
let syn_ctx = span_for_range(segment.syntax().text_range());
if let Some(macro_call_id) = syn_ctx.outer_expn(db)
- && db.lookup_intern_macro_call(macro_call_id.into()).def.local_inner
+ && crate::MacroCallId::from(macro_call_id).loc(db).def.local_inner
{
mod_path.kind = match resolve_crate_root(db, syn_ctx) {
Some(crate_root) => PathKind::DollarCrate(crate_root),
@@ -406,7 +406,7 @@ pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContext) -> O
result_mark = Some(mark);
}
- result_mark.map(|call| db.lookup_intern_macro_call(call.into()).def.krate)
+ result_mark.map(|call| crate::MacroCallId::from(call).loc(db).def.krate)
}
pub use crate::name as __name;
@@ -427,6 +427,7 @@ macro_rules! __known_path {
(core::range::RangeFrom) => {};
(core::range::RangeInclusive) => {};
(core::range::RangeToInclusive) => {};
+ (core::async_iter::AsyncIterator) => {};
(core::future::Future) => {};
(core::future::IntoFuture) => {};
(core::fmt::Debug) => {};
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 0408a6943d..3ddc305f95 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -33,12 +33,14 @@ impl fmt::Debug for Name {
}
impl Ord for Name {
+ #[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.symbol.as_str().cmp(other.symbol.as_str())
}
}
impl PartialOrd for Name {
+ #[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
@@ -46,74 +48,62 @@ impl PartialOrd for Name {
// No need to strip `r#`, all comparisons are done against well-known symbols.
impl PartialEq<Symbol> for Name {
+ #[inline]
fn eq(&self, sym: &Symbol) -> bool {
self.symbol == *sym
}
}
impl PartialEq<&Symbol> for Name {
+ #[inline]
fn eq(&self, &sym: &&Symbol) -> bool {
self.symbol == *sym
}
}
impl PartialEq<Name> for Symbol {
+ #[inline]
fn eq(&self, name: &Name) -> bool {
*self == name.symbol
}
}
impl PartialEq<Name> for &Symbol {
+ #[inline]
fn eq(&self, name: &Name) -> bool {
**self == name.symbol
}
}
impl Name {
+ #[inline]
fn new_text(text: &str) -> Name {
Name { symbol: Symbol::intern(text), ctx: () }
}
+ #[inline]
pub fn new(text: &str, mut ctx: SyntaxContext) -> Name {
// For comparisons etc. we remove the edition, because sometimes we search for some `Name`
// and we don't know which edition it came from.
// Can't do that for all `SyntaxContextId`s because it breaks Salsa.
ctx.remove_root_edition();
_ = ctx;
- match text.strip_prefix("r#") {
- Some(text) => Self::new_text(text),
- None => Self::new_text(text),
- }
+ let text = text.strip_prefix("r#").unwrap_or(text);
+ Self::new_text(text)
}
+ #[inline]
pub fn new_root(text: &str) -> Name {
// The edition doesn't matter for hygiene.
Self::new(text, SyntaxContext::root(Edition::Edition2015))
}
+ #[inline]
pub fn new_tuple_field(idx: usize) -> Name {
- let symbol = match idx {
- 0 => sym::INTEGER_0,
- 1 => sym::INTEGER_1,
- 2 => sym::INTEGER_2,
- 3 => sym::INTEGER_3,
- 4 => sym::INTEGER_4,
- 5 => sym::INTEGER_5,
- 6 => sym::INTEGER_6,
- 7 => sym::INTEGER_7,
- 8 => sym::INTEGER_8,
- 9 => sym::INTEGER_9,
- 10 => sym::INTEGER_10,
- 11 => sym::INTEGER_11,
- 12 => sym::INTEGER_12,
- 13 => sym::INTEGER_13,
- 14 => sym::INTEGER_14,
- 15 => sym::INTEGER_15,
- _ => Symbol::intern(&idx.to_string()),
- };
- Name { symbol, ctx: () }
+ Name::new_symbol_root(sym::Integer::get(idx))
}
+ #[inline]
pub fn new_lifetime(lt: &str) -> Name {
match lt.strip_prefix("'r#") {
Some(lt) => Self::new_text(&format_smolstr!("'{lt}")),
@@ -121,6 +111,7 @@ impl Name {
}
}
+ #[inline]
pub fn new_symbol(symbol: Symbol, ctx: SyntaxContext) -> Self {
debug_assert!(!symbol.as_str().starts_with("r#"));
_ = ctx;
@@ -128,6 +119,7 @@ impl Name {
}
// FIXME: This needs to go once we have hygiene
+ #[inline]
pub fn new_symbol_root(sym: Symbol) -> Self {
Self::new_symbol(sym, SyntaxContext::root(Edition::Edition2015))
}
@@ -141,6 +133,7 @@ impl Name {
/// Ideally, we want a `gensym` semantics for missing names -- each missing
/// name is equal only to itself. It's not clear how to implement this in
/// salsa though, so we punt on that bit for a moment.
+ #[inline]
pub const fn missing() -> Name {
Name { symbol: sym::MISSING_NAME, ctx: () }
}
@@ -150,23 +143,27 @@ impl Name {
///
/// Use this method instead of comparing with `Self::missing()` as missing names
/// (ideally should) have a `gensym` semantics.
+ #[inline]
pub fn is_missing(&self) -> bool {
- self == &Name::missing()
+ self.symbol == sym::MISSING_NAME
}
/// Generates a new name that attempts to be unique. Should only be used when body lowering and
/// creating desugared locals and labels. The caller is responsible for picking an index
/// that is stable across re-executions
+ #[inline]
pub fn generate_new_name(idx: usize) -> Name {
- Name::new_text(&format!("<ra@gennew>{idx}"))
+ Name::new_symbol_root(sym::RaGeneratedName::get(idx))
}
/// Returns the tuple index this name represents if it is a tuple field.
+ #[inline]
pub fn as_tuple_index(&self) -> Option<usize> {
- self.symbol.as_str().parse().ok()
+ sym::Integer::as_uint(&self.symbol)
}
/// Whether this name needs to be escaped in the given edition via `r#`.
+ #[inline]
pub fn needs_escape(&self, edition: Edition) -> bool {
is_raw_identifier(self.symbol.as_str(), edition)
}
@@ -175,10 +172,12 @@ impl Name {
///
/// Do not use this for user-facing text, use `display` instead to handle editions properly.
// FIXME: This should take a database argument to hide the interning
+ #[inline]
pub fn as_str(&self) -> &str {
self.symbol.as_str()
}
+ #[inline]
pub fn display<'a>(
&'a self,
db: &dyn crate::db::ExpandDatabase,
@@ -190,14 +189,17 @@ impl Name {
// FIXME: Remove this in favor of `display`, see fixme on `as_str`
#[doc(hidden)]
+ #[inline]
pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ {
Display { name: self, edition }
}
+ #[inline]
pub fn symbol(&self) -> &Symbol {
&self.symbol
}
+ #[inline]
pub fn is_generated(&self) -> bool {
self.as_str().starts_with("<ra@gennew>")
}
diff --git a/crates/hir-expand/src/prettify_macro_expansion_.rs b/crates/hir-expand/src/prettify_macro_expansion_.rs
index 6431d46d39..79e6f0f5b7 100644
--- a/crates/hir-expand/src/prettify_macro_expansion_.rs
+++ b/crates/hir-expand/src/prettify_macro_expansion_.rs
@@ -29,7 +29,7 @@ pub fn prettify_macro_expansion(
let macro_call_id = ctx
.outer_expn(db)
.expect("`$crate` cannot come from `SyntaxContextId::ROOT`");
- let macro_call = db.lookup_intern_macro_call(macro_call_id.into());
+ let macro_call = crate::MacroCallId::from(macro_call_id).loc(db);
let macro_def_crate = macro_call.def.krate;
// First, if this is the same crate as the macro, nothing will work but `crate`.
// If not, if the target trait has the macro's crate as a dependency, using the dependency name
diff --git a/crates/hir-expand/src/span_map.rs b/crates/hir-expand/src/span_map.rs
index aa8603341b..6a94df8b5a 100644
--- a/crates/hir-expand/src/span_map.rs
+++ b/crates/hir-expand/src/span_map.rs
@@ -2,7 +2,6 @@
use span::Span;
use syntax::{AstNode, TextRange, ast};
-use triomphe::Arc;
pub use span::RealSpanMap;
@@ -11,35 +10,21 @@ use crate::{HirFileId, MacroCallId, db::ExpandDatabase};
pub type ExpansionSpanMap = span::SpanMap;
/// Spanmap for a macro file or a real file
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum SpanMap {
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum SpanMap<'db> {
/// Spanmap for a macro file
- ExpansionSpanMap(Arc<ExpansionSpanMap>),
+ ExpansionSpanMap(&'db ExpansionSpanMap),
/// Spanmap for a real file
- RealSpanMap(Arc<RealSpanMap>),
+ RealSpanMap(&'db RealSpanMap),
}
-#[derive(Copy, Clone)]
-pub enum SpanMapRef<'a> {
- /// Spanmap for a macro file
- ExpansionSpanMap(&'a ExpansionSpanMap),
- /// Spanmap for a real file
- RealSpanMap(&'a RealSpanMap),
-}
-
-impl syntax_bridge::SpanMapper for SpanMap {
- fn span_for(&self, range: TextRange) -> Span {
- self.span_for_range(range)
- }
-}
-
-impl syntax_bridge::SpanMapper for SpanMapRef<'_> {
+impl syntax_bridge::SpanMapper for SpanMap<'_> {
fn span_for(&self, range: TextRange) -> Span {
self.span_for_range(range)
}
}
-impl SpanMap {
+impl<'db> SpanMap<'db> {
pub fn span_for_range(&self, range: TextRange) -> Span {
match self {
// FIXME: Is it correct for us to only take the span at the start? This feels somewhat
@@ -51,37 +36,22 @@ impl SpanMap {
}
}
- pub fn as_ref(&self) -> SpanMapRef<'_> {
- match self {
- Self::ExpansionSpanMap(span_map) => SpanMapRef::ExpansionSpanMap(span_map),
- Self::RealSpanMap(span_map) => SpanMapRef::RealSpanMap(span_map),
- }
- }
-
#[inline]
- pub(crate) fn new(db: &dyn ExpandDatabase, file_id: HirFileId) -> SpanMap {
+ pub(crate) fn new(db: &'db dyn ExpandDatabase, file_id: HirFileId) -> SpanMap<'db> {
match file_id {
HirFileId::FileId(file_id) => SpanMap::RealSpanMap(db.real_span_map(file_id)),
HirFileId::MacroFile(m) => {
- SpanMap::ExpansionSpanMap(db.parse_macro_expansion(m).value.1)
+ SpanMap::ExpansionSpanMap(&db.parse_macro_expansion(m).value.1)
}
}
}
}
-impl SpanMapRef<'_> {
- pub fn span_for_range(self, range: TextRange) -> Span {
- match self {
- Self::ExpansionSpanMap(span_map) => span_map.span_at(range.start()),
- Self::RealSpanMap(span_map) => span_map.span_for_range(range),
- }
- }
-}
-
+#[salsa_macros::tracked(returns(ref))]
pub(crate) fn real_span_map(
db: &dyn ExpandDatabase,
editioned_file_id: base_db::EditionedFileId,
-) -> Arc<RealSpanMap> {
+) -> RealSpanMap {
use syntax::ast::HasModuleItem;
let mut pairs = vec![(syntax::TextSize::new(0), span::ROOT_ERASED_FILE_AST_ID)];
let ast_id_map = db.ast_id_map(editioned_file_id.into());
@@ -134,16 +104,16 @@ pub(crate) fn real_span_map(
_ => (),
});
- Arc::new(RealSpanMap::from_file(
+ RealSpanMap::from_file(
editioned_file_id.span_file_id(db),
pairs.into_boxed_slice(),
tree.syntax().text_range().end(),
- ))
+ )
}
pub(crate) fn expansion_span_map(
db: &dyn ExpandDatabase,
file_id: MacroCallId,
-) -> Arc<ExpansionSpanMap> {
- db.parse_macro_expansion(file_id).value.1
+) -> &ExpansionSpanMap {
+ &db.parse_macro_expansion(file_id).value.1
}
diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml
index 18426f3095..e8eda74e40 100644
--- a/crates/hir-ty/Cargo.toml
+++ b/crates/hir-ty/Cargo.toml
@@ -33,6 +33,7 @@ query-group.workspace = true
salsa.workspace = true
salsa-macros.workspace = true
petgraph.workspace = true
+bitflags.workspace = true
ra-ap-rustc_abi.workspace = true
ra-ap-rustc_index.workspace = true
diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs
index abab3bfb25..a8ed4126ab 100644
--- a/crates/hir-ty/src/autoderef.rs
+++ b/crates/hir-ty/src/autoderef.rs
@@ -10,7 +10,7 @@ use rustc_type_ir::inherent::{IntoKind, Ty as _};
use tracing::debug;
use crate::{
- ParamEnvAndCrate,
+ ParamEnvAndCrate, Span,
db::HirDatabase,
infer::InferenceContext,
next_solver::{
@@ -39,8 +39,8 @@ pub fn autoderef<'db>(
) -> impl Iterator<Item = Ty<'db>> + use<'db> {
let interner = DbInterner::new_with(db, env.krate);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
- let (ty, _) = infcx.instantiate_canonical(&ty);
- let autoderef = Autoderef::new(&infcx, env.param_env, ty);
+ let (ty, _) = infcx.instantiate_canonical(Span::Dummy, &ty);
+ let autoderef = Autoderef::new(&infcx, env.param_env, ty, Span::Dummy);
let mut v = Vec::new();
for (ty, _steps) in autoderef {
// `ty` may contain unresolved inference variables. Since there's no chance they would be
@@ -155,6 +155,7 @@ pub(crate) struct GeneralAutoderef<'db, Ctx, Steps = Vec<(Ty<'db>, AutoderefKind
// Configurations:
include_raw_pointers: bool,
use_receiver_trait: bool,
+ span: Span,
}
pub(crate) type Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> =
@@ -200,7 +201,7 @@ where
// autoderef expect this type to have been structurally normalized.
if let TyKind::Alias(..) = ty.kind() {
let (normalized_ty, obligations) =
- structurally_normalize_ty(self.infcx(), self.param_env(), ty)?;
+ structurally_normalize_ty(self.infcx(), self.param_env(), ty, self.span)?;
self.state.obligations.extend(obligations);
(AutoderefKind::Builtin, normalized_ty)
} else {
@@ -232,8 +233,9 @@ impl<'a, 'db> Autoderef<'a, 'db> {
infcx: &'a InferCtxt<'db>,
param_env: ParamEnv<'db>,
base_ty: Ty<'db>,
+ span: Span,
) -> Self {
- Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty)
+ Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty, span)
}
}
@@ -242,8 +244,9 @@ impl<'a, 'b, 'db> InferenceContextAutoderef<'a, 'b, 'db> {
pub(crate) fn new_from_inference_context(
ctx: &'a mut InferenceContext<'b, 'db>,
base_ty: Ty<'db>,
+ span: Span,
) -> Self {
- Self::new_impl(InferenceContextAutoderefCtx(ctx), base_ty)
+ Self::new_impl(InferenceContextAutoderefCtx(ctx), base_ty, span)
}
#[inline]
@@ -258,8 +261,9 @@ impl<'a, 'db> Autoderef<'a, 'db, usize> {
infcx: &'a InferCtxt<'db>,
param_env: ParamEnv<'db>,
base_ty: Ty<'db>,
+ span: Span,
) -> Self {
- Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty)
+ Self::new_impl(DefaultAutoderefCtx { infcx, param_env }, base_ty, span)
}
}
@@ -269,7 +273,7 @@ where
Steps: TrackAutoderefSteps<'db>,
{
#[inline]
- fn new_impl(ctx: Ctx, base_ty: Ty<'db>) -> Self {
+ fn new_impl(ctx: Ctx, base_ty: Ty<'db>, span: Span) -> Self {
GeneralAutoderef {
state: AutoderefSnapshot {
steps: Steps::default(),
@@ -282,6 +286,7 @@ where
traits: None,
include_raw_pointers: false,
use_receiver_trait: false,
+ span,
}
}
@@ -338,7 +343,7 @@ where
let trait_ref = TraitRef::new(interner, trait_.into(), [ty]);
let obligation =
- Obligation::new(interner, ObligationCause::new(), self.param_env(), trait_ref);
+ Obligation::new(interner, ObligationCause::new(self.span), self.param_env(), trait_ref);
// We detect whether the self type implements `Deref` before trying to
// structurally normalize. We use `predicate_may_hold_opaque_types_jank`
// to support not-yet-defined opaque types. It will succeed for `impl Deref`
@@ -352,6 +357,7 @@ where
self.infcx(),
self.param_env(),
Ty::new_projection(interner, trait_target.into(), [ty]),
+ self.span,
)?;
debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations);
self.state.obligations.extend(obligations);
@@ -403,9 +409,11 @@ fn structurally_normalize_ty<'db>(
infcx: &InferCtxt<'db>,
param_env: ParamEnv<'db>,
ty: Ty<'db>,
+ span: Span,
) -> Option<(Ty<'db>, PredicateObligations<'db>)> {
let mut ocx = ObligationCtxt::new(infcx);
- let Ok(normalized_ty) = ocx.structurally_normalize_ty(&ObligationCause::misc(), param_env, ty)
+ let Ok(normalized_ty) =
+ ocx.structurally_normalize_ty(&ObligationCause::new(span), param_env, ty)
else {
// We shouldn't have errors here in the old solver, except for
// evaluate/fulfill mismatches, but that's not a reason for an ICE.
diff --git a/crates/hir-ty/src/builtin_derive.rs b/crates/hir-ty/src/builtin_derive.rs
index 6a9b1671e7..fe60fbc510 100644
--- a/crates/hir-ty/src/builtin_derive.rs
+++ b/crates/hir-ty/src/builtin_derive.rs
@@ -12,7 +12,7 @@ use hir_def::{
use itertools::Itertools;
use la_arena::ArenaMap;
use rustc_type_ir::{
- AliasTyKind, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast,
+ AliasTyKind, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast,
inherent::{GenericArgs as _, IntoKind},
};
@@ -21,7 +21,8 @@ use crate::{
db::HirDatabase,
next_solver::{
AliasTy, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv,
- StoredEarlyBinder, StoredTy, TraitRef, Ty, TyKind, fold::fold_tys, generics::Generics,
+ StoredEarlyBinder, StoredTy, TraitRef, Ty, TyKind, Unnormalized, fold::fold_tys,
+ generics::Generics,
},
};
@@ -53,7 +54,10 @@ fn trait_args(trait_: BuiltinDeriveImplTrait, self_ty: Ty<'_>) -> GenericArgs<'_
}
}
-pub(crate) fn generics_of<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplId) -> Generics {
+pub(crate) fn generics_of<'db>(
+ interner: DbInterner<'db>,
+ id: BuiltinDeriveImplId,
+) -> Generics<'db> {
let db = interner.db;
let loc = id.loc(db);
match loc.trait_ {
@@ -65,15 +69,14 @@ pub(crate) fn generics_of<'db>(interner: DbInterner<'db>, id: BuiltinDeriveImplI
| BuiltinDeriveImplTrait::Ord
| BuiltinDeriveImplTrait::PartialOrd
| BuiltinDeriveImplTrait::Eq
- | BuiltinDeriveImplTrait::PartialEq => interner.generics_of(loc.adt.into()),
+ | BuiltinDeriveImplTrait::PartialEq => Generics::from_generic_def(db, loc.adt.into()),
BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => {
- let mut generics = interner.generics_of(loc.adt.into());
let trait_id = loc
.trait_
.get_id(interner.lang_items())
.expect("we don't pass the impl to the solver if we can't resolve the trait");
- generics.push_param(coerce_pointee_new_type_param(trait_id).into());
- generics
+ let additional_param = coerce_pointee_new_type_param(trait_id).into();
+ Generics::from_generic_def_plus_one(db, loc.adt.into(), additional_param)
}
}
}
@@ -150,7 +153,7 @@ pub fn impl_trait<'db>(
}
#[salsa::tracked(returns(ref))]
-pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates {
+pub fn predicates(db: &dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates {
let loc = impl_.loc(db);
let generic_params = GenericParams::of(db, loc.adt.into());
let interner = DbInterner::new_with(db, loc.module(db).krate(db));
@@ -195,6 +198,7 @@ pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) ->
};
let duplicated_bounds =
adt_predicates.explicit_predicates().iter_identity().filter_map(|pred| {
+ let pred = pred.skip_norm_wip();
let mentions_pointee =
pred.visit_with(&mut MentionsPointee { pointee_param_idx }).is_break();
if !mentions_pointee {
@@ -216,6 +220,7 @@ pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) ->
adt_predicates
.explicit_predicates()
.iter_identity()
+ .map(Unnormalized::skip_norm_wip)
.chain(duplicated_bounds)
.chain(unsize_bound),
)
@@ -317,6 +322,7 @@ fn simple_trait_predicates<'db>(
adt_predicates
.explicit_predicates()
.iter_identity()
+ .map(Unnormalized::skip_norm_wip)
.chain(extra_predicates)
.chain(assoc_type_bounds),
)
@@ -359,7 +365,7 @@ fn extend_assoc_type_bounds<'db>(
let mut visitor = ProjectionFinder { interner, assoc_type_bounds, trait_id, trait_ };
for (_, field) in fields.iter() {
- field.get().instantiate_identity().visit_with(&mut visitor);
+ field.get().instantiate_identity().skip_norm_wip().visit_with(&mut visitor);
}
}
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 80e7e05d76..e1d6cec594 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -5,41 +5,34 @@ mod tests;
use base_db::Crate;
use hir_def::{
- ConstId, EnumVariantId, ExpressionStoreOwnerId, GeneralConstId, GenericDefId, HasModule,
- StaticId,
+ ConstId, EnumVariantId, ExpressionStoreOwnerId, GenericDefId, HasModule, StaticId,
attrs::AttrFlags,
- expr_store::{Body, ExpressionStore},
+ expr_store::{Body, ExpressionStore, HygieneId, path::Path},
hir::{Expr, ExprId, Literal},
+ resolver::{Resolver, ValueNs},
};
use hir_expand::Lookup;
use rustc_abi::Size;
use rustc_apfloat::Float;
-use rustc_type_ir::inherent::IntoKind;
+use rustc_ast_ir::Mutability;
+use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _};
use stdx::never;
-use triomphe::Arc;
use crate::{
- LifetimeElisionKind, ParamEnvAndCrate, TyLoweringContext,
- db::HirDatabase,
+ ParamEnvAndCrate, Span,
+ db::{AnonConstId, AnonConstLoc, GeneralConstId, HirDatabase},
display::DisplayTarget,
- infer::InferenceContext,
+ generics::Generics,
mir::{MirEvalError, MirLowerError, pad16},
next_solver::{
- Allocation, Const, ConstKind, Consts, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
- ScalarInt, StoredAllocation, StoredGenericArgs, Ty, TyKind, ValTreeKind, default_types,
+ Allocation, Const, ConstKind, Consts, DbInterner, DefaultAny, GenericArgs, ParamConst,
+ ScalarInt, StoredAllocation, StoredEarlyBinder, StoredGenericArgs, Ty, TyKind,
+ UnevaluatedConst, ValTreeKind, default_types,
},
traits::StoredParamEnvAndCrate,
};
-use super::mir::{interpret_mir, lower_body_to_mir};
-
-pub fn unknown_const<'db>(_ty: Ty<'db>) -> Const<'db> {
- Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed))
-}
-
-pub fn unknown_const_as_generic<'db>(ty: Ty<'db>) -> GenericArg<'db> {
- unknown_const(ty).into()
-}
+use super::mir::interpret_mir;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConstEvalError {
@@ -82,15 +75,13 @@ impl From<MirEvalError> for ConstEvalError {
}
/// Interns a constant scalar with the given type
-pub fn intern_const_ref<'a>(
- db: &'a dyn HirDatabase,
+fn intern_const_ref<'db>(
+ interner: DbInterner<'db>,
value: &Literal,
- ty: Ty<'a>,
- krate: Crate,
-) -> Const<'a> {
- let interner = DbInterner::new_no_crate(db);
- let Ok(data_layout) = db.target_data_layout(krate) else {
- return Const::error(interner);
+ ty: Ty<'db>,
+) -> Result<Const<'db>, CreateConstError<'db>> {
+ let Ok(data_layout) = interner.db.target_data_layout(interner.expect_crate()) else {
+ return Ok(Const::error(interner));
};
let valtree = match (ty.kind(), value) {
(TyKind::Uint(uint), Literal::Uint(value, _)) => {
@@ -138,14 +129,80 @@ pub fn intern_const_ref<'a>(
}
(_, Literal::CString(_)) => {
// FIXME:
- return Const::error(interner);
+ return Ok(Const::error(interner));
}
_ => {
never!("mismatching type for literal");
- return Const::error(interner);
+ let actual = literal_ty(
+ interner,
+ value,
+ |types| types.types.i32,
+ |types| types.types.u32,
+ |types| types.types.f64,
+ );
+ return Err(CreateConstError::TypeMismatch { actual });
}
};
- Const::new_valtree(interner, ty, valtree)
+ Ok(Const::new_valtree(interner, ty, valtree))
+}
+
+pub(crate) fn literal_ty<'db>(
+ interner: DbInterner<'db>,
+ value: &Literal,
+ default_int: impl FnOnce(&DefaultAny<'db>) -> Ty<'db>,
+ default_uint: impl FnOnce(&DefaultAny<'db>) -> Ty<'db>,
+ default_float: impl FnOnce(&DefaultAny<'db>) -> Ty<'db>,
+) -> Ty<'db> {
+ let types = interner.default_types();
+ match value {
+ Literal::Bool(..) => types.types.bool,
+ Literal::String(..) => types.types.static_str_ref,
+ Literal::ByteString(bs) => {
+ let byte_type = types.types.u8;
+ let array_type = Ty::new_array(interner, byte_type, bs.len() as u128);
+ Ty::new_ref(interner, types.regions.statik, array_type, Mutability::Not)
+ }
+ Literal::CString(..) => Ty::new_ref(
+ interner,
+ types.regions.statik,
+ interner.lang_items().CStr.map_or(types.types.error, |strukt| {
+ Ty::new_adt(interner, strukt.into(), types.empty.generic_args)
+ }),
+ Mutability::Not,
+ ),
+ Literal::Char(..) => types.types.char,
+ Literal::Int(_v, ty) => match ty {
+ Some(int_ty) => match int_ty {
+ hir_def::builtin_type::BuiltinInt::Isize => types.types.isize,
+ hir_def::builtin_type::BuiltinInt::I8 => types.types.i8,
+ hir_def::builtin_type::BuiltinInt::I16 => types.types.i16,
+ hir_def::builtin_type::BuiltinInt::I32 => types.types.i32,
+ hir_def::builtin_type::BuiltinInt::I64 => types.types.i64,
+ hir_def::builtin_type::BuiltinInt::I128 => types.types.i128,
+ },
+ None => default_int(types),
+ },
+ Literal::Uint(_v, ty) => match ty {
+ Some(int_ty) => match int_ty {
+ hir_def::builtin_type::BuiltinUint::Usize => types.types.usize,
+ hir_def::builtin_type::BuiltinUint::U8 => types.types.u8,
+ hir_def::builtin_type::BuiltinUint::U16 => types.types.u16,
+ hir_def::builtin_type::BuiltinUint::U32 => types.types.u32,
+ hir_def::builtin_type::BuiltinUint::U64 => types.types.u64,
+ hir_def::builtin_type::BuiltinUint::U128 => types.types.u128,
+ },
+ None => default_uint(types),
+ },
+ Literal::Float(_v, ty) => match ty {
+ Some(float_ty) => match float_ty {
+ hir_def::builtin_type::BuiltinFloat::F16 => types.types.f16,
+ hir_def::builtin_type::BuiltinFloat::F32 => types.types.f32,
+ hir_def::builtin_type::BuiltinFloat::F64 => types.types.f64,
+ hir_def::builtin_type::BuiltinFloat::F128 => types.types.f128,
+ },
+ None => default_float(types),
+ },
+ }
}
/// Interns a possibly-unknown target usize
@@ -185,7 +242,11 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u
let ec = db.const_eval_static(id).ok()?;
Some(allocation_as_usize(ec))
}
- GeneralConstId::AnonConstId(_) => None,
+ GeneralConstId::AnonConstId(id) => {
+ let subst = unevaluated_const.args;
+ let ec = db.anon_const_eval(id, subst, None).ok()?;
+ Some(allocation_as_usize(ec))
+ }
},
ConstKind::Value(val) => {
if val.ty == default_types(db).types.usize {
@@ -203,8 +264,8 @@ pub fn allocation_as_isize(ec: Allocation<'_>) -> i128 {
i128::from_le_bytes(pad16(&ec.memory, true))
}
-pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<i128> {
- match (*c).kind() {
+pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<i128> {
+ match c.kind() {
ConstKind::Param(_) => None,
ConstKind::Infer(_) => None,
ConstKind::Bound(_, _) => None,
@@ -219,7 +280,11 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<
let ec = db.const_eval_static(id).ok()?;
Some(allocation_as_isize(ec))
}
- GeneralConstId::AnonConstId(_) => None,
+ GeneralConstId::AnonConstId(id) => {
+ let subst = unevaluated_const.args;
+ let ec = db.anon_const_eval(id, subst, None).ok()?;
+ Some(allocation_as_isize(ec))
+ }
},
ConstKind::Value(val) => {
if val.ty == default_types(db).types.isize {
@@ -233,6 +298,99 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<
}
}
+#[derive(Debug)]
+pub(crate) enum CreateConstError<'db> {
+ UsedForbiddenParam,
+ ResolveToNonConst,
+ DoesNotResolve,
+ UnderscoreExpr,
+ TypeMismatch {
+ #[expect(unused, reason = "will need this for diagnostics")]
+ actual: Ty<'db>,
+ },
+}
+
+pub(crate) fn path_to_const<'a, 'db>(
+ db: &'db dyn HirDatabase,
+ resolver: &Resolver<'db>,
+ generics: &dyn Fn() -> &'a Generics<'db>,
+ forbid_params_after: Option<u32>,
+ path: &Path,
+) -> Result<Const<'db>, CreateConstError<'db>> {
+ let interner = DbInterner::new_no_crate(db);
+ let resolution = resolver
+ .resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT)
+ .ok_or(CreateConstError::DoesNotResolve)?;
+ let konst = match resolution {
+ ValueNs::ConstId(id) => GeneralConstId::ConstId(id),
+ ValueNs::StaticId(id) => GeneralConstId::StaticId(id),
+ ValueNs::GenericParam(param) => {
+ let index = generics().type_or_const_param_idx(param.into());
+ if forbid_params_after.is_some_and(|forbid_after| index >= forbid_after) {
+ return Err(CreateConstError::UsedForbiddenParam);
+ }
+ return Ok(Const::new_param(interner, ParamConst { id: param, index }));
+ }
+ // These are not valid as consts.
+ // FIXME: Report an error?
+ ValueNs::ImplSelf(_)
+ | ValueNs::LocalBinding(_)
+ | ValueNs::FunctionId(_)
+ | ValueNs::StructId(_)
+ | ValueNs::EnumVariantId(_) => return Err(CreateConstError::ResolveToNonConst),
+ };
+ let args = GenericArgs::empty(interner);
+ Ok(Const::new_unevaluated(interner, UnevaluatedConst { def: konst.into(), args }))
+}
+
+pub(crate) fn create_anon_const<'a, 'db>(
+ interner: DbInterner<'db>,
+ owner: ExpressionStoreOwnerId,
+ store: &ExpressionStore,
+ expr: ExprId,
+ resolver: &Resolver<'db>,
+ expected_ty: Ty<'db>,
+ generics: &dyn Fn() -> &'a Generics<'db>,
+ create_var: Option<&mut dyn FnMut(Span) -> Const<'db>>,
+ forbid_params_after: Option<u32>,
+) -> Result<Const<'db>, CreateConstError<'db>> {
+ match &store[expr] {
+ Expr::Literal(literal) => intern_const_ref(interner, literal, expected_ty),
+ Expr::Underscore => match create_var {
+ Some(create_var) => Ok(create_var(expr.into())),
+ None => Err(CreateConstError::UnderscoreExpr),
+ },
+ Expr::Path(path)
+ if let konst =
+ path_to_const(interner.db, resolver, generics, forbid_params_after, path)
+ && !matches!(konst, Err(CreateConstError::DoesNotResolve)) =>
+ {
+ konst
+ }
+ _ => {
+ let allow_using_generic_params = forbid_params_after.is_none();
+ let konst = AnonConstId::new(
+ interner.db,
+ AnonConstLoc {
+ owner,
+ expr,
+ ty: StoredEarlyBinder::bind(expected_ty.store()),
+ allow_using_generic_params,
+ },
+ );
+ let args = if allow_using_generic_params {
+ GenericArgs::identity_for_item(interner, owner.generic_def(interner.db).into())
+ } else {
+ GenericArgs::empty(interner)
+ };
+ Ok(Const::new_unevaluated(
+ interner,
+ UnevaluatedConst { def: GeneralConstId::AnonConstId(konst).into(), args },
+ ))
+ }
+ }
+}
+
pub(crate) fn const_eval_discriminant_variant(
db: &dyn HirDatabase,
variant_id: EnumVariantId,
@@ -258,7 +416,7 @@ pub(crate) fn const_eval_discriminant_variant(
let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed());
let mir_body = db.monomorphized_mir_body(
- def,
+ def.into(),
GenericArgs::empty(interner).store(),
ParamEnvAndCrate { param_env: db.trait_environment(def.into()), krate: def.krate(db) }
.store(),
@@ -268,49 +426,6 @@ pub(crate) fn const_eval_discriminant_variant(
Ok(c)
}
-// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should
-// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here
-// and make this function private. See the fixme comment on `InferenceContext::resolve_all`.
-pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'db>) -> Const<'db> {
- let infer = ctx.fixme_resolve_all_clone();
- fn has_closure(store: &ExpressionStore, expr: ExprId) -> bool {
- if matches!(store[expr], Expr::Closure { .. }) {
- return true;
- }
- let mut r = false;
- store.walk_child_exprs(expr, |idx| r |= has_closure(store, idx));
- r
- }
- if has_closure(ctx.store, expr) {
- // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic.
- return Const::error(ctx.interner());
- }
- if let Expr::Path(p) = &ctx.store[expr] {
- let mut ctx = TyLoweringContext::new(
- ctx.db,
- &ctx.resolver,
- ctx.store,
- ctx.generic_def,
- LifetimeElisionKind::Infer,
- );
- if let Some(c) = ctx.path_to_const(p) {
- return c;
- }
- }
- if let Some(body_owner) = ctx.owner.as_def_with_body()
- && let Ok(mir_body) =
- lower_body_to_mir(ctx.db, body_owner, Body::of(ctx.db, body_owner), &infer, expr)
- && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None)
- {
- return Const::new_from_allocation(
- ctx.interner(),
- &result,
- ParamEnvAndCrate { param_env: ctx.table.param_env, krate: ctx.resolver.krate() },
- );
- }
- Const::error(ctx.interner())
-}
-
pub(crate) fn const_eval_discriminant_cycle_result(
_: &dyn HirDatabase,
_: salsa::Id,
@@ -331,8 +446,8 @@ pub(crate) fn const_eval<'db>(
};
#[salsa::tracked(returns(ref), cycle_result = const_eval_cycle_result)]
- pub(crate) fn const_eval_query<'db>(
- db: &'db dyn HirDatabase,
+ pub(crate) fn const_eval_query(
+ db: &dyn HirDatabase,
def: ConstId,
subst: StoredGenericArgs,
trait_env: Option<StoredParamEnvAndCrate>,
@@ -362,6 +477,48 @@ pub(crate) fn const_eval<'db>(
}
}
+pub(crate) fn anon_const_eval<'db>(
+ db: &'db dyn HirDatabase,
+ def: AnonConstId,
+ subst: GenericArgs<'db>,
+ trait_env: Option<ParamEnvAndCrate<'db>>,
+) -> Result<Allocation<'db>, ConstEvalError> {
+ return match anon_const_eval_query(db, def, subst.store(), trait_env.map(|env| env.store())) {
+ Ok(konst) => Ok(konst.as_ref()),
+ Err(err) => Err(err.clone()),
+ };
+
+ #[salsa::tracked(returns(ref), cycle_result = anon_const_eval_cycle_result)]
+ pub(crate) fn anon_const_eval_query(
+ db: &dyn HirDatabase,
+ def: AnonConstId,
+ subst: StoredGenericArgs,
+ trait_env: Option<StoredParamEnvAndCrate>,
+ ) -> Result<StoredAllocation, ConstEvalError> {
+ let body = db.monomorphized_mir_body(
+ def.into(),
+ subst,
+ ParamEnvAndCrate {
+ param_env: db.trait_environment(def.loc(db).owner),
+ krate: def.krate(db),
+ }
+ .store(),
+ )?;
+ let c = interpret_mir(db, body, false, trait_env.as_ref().map(|env| env.as_ref()))?.0?;
+ Ok(c.store())
+ }
+
+ pub(crate) fn anon_const_eval_cycle_result(
+ _: &dyn HirDatabase,
+ _: salsa::Id,
+ _: AnonConstId,
+ _: StoredGenericArgs,
+ _: Option<StoredParamEnvAndCrate>,
+ ) -> Result<StoredAllocation, ConstEvalError> {
+ Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
+ }
+}
+
pub(crate) fn const_eval_static<'db>(
db: &'db dyn HirDatabase,
def: StaticId,
@@ -372,8 +529,8 @@ pub(crate) fn const_eval_static<'db>(
};
#[salsa::tracked(returns(ref), cycle_result = const_eval_static_cycle_result)]
- pub(crate) fn const_eval_static_query<'db>(
- db: &'db dyn HirDatabase,
+ pub(crate) fn const_eval_static_query(
+ db: &dyn HirDatabase,
def: StaticId,
) -> Result<StoredAllocation, ConstEvalError> {
let interner = DbInterner::new_no_crate(db);
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 723fa0fc68..f158661069 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -2474,8 +2474,6 @@ fn extern_weak_statics() {
}
#[test]
-// FIXME
-#[should_panic]
fn from_ne_bytes() {
check_number(
r#"
@@ -2567,9 +2565,8 @@ fn const_transfer_memory() {
fn anonymous_const_block() {
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn size_of<T>() -> usize;
- }
+ #[rustc_intrinsic]
+ pub fn size_of<T>() -> usize;
const fn f<T>() -> usize {
let r = const { size_of::<T>() };
diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs
index 10282d2116..85e917fe1a 100644
--- a/crates/hir-ty/src/consteval/tests/intrinsics.rs
+++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -204,12 +204,11 @@ fn const_eval_select() {
check_number(
r#"
//- minicore: fn
- 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>;
- }
+ #[rustc_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
@@ -229,9 +228,8 @@ fn const_eval_select() {
fn wrapping_add() {
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn wrapping_add<T>(a: T, b: T) -> T;
- }
+ #[rustc_intrinsic]
+ pub fn wrapping_add<T>(a: T, b: T) -> T;
const GOAL: u8 = wrapping_add(10, 250);
"#,
@@ -244,10 +242,10 @@ fn ptr_offset_from() {
check_number(
r#"
//- minicore: index, slice, coerce_unsized
- extern "rust-intrinsic" {
- pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
- pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;
- }
+ #[rustc_intrinsic]
+ pub fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize;
+ #[rustc_intrinsic]
+ pub fn ptr_offset_from_unsigned<T>(ptr: *const T, base: *const T) -> usize;
const GOAL: isize = {
let x = [1, 2, 3, 4, 5i32];
@@ -265,9 +263,8 @@ fn ptr_offset_from() {
fn saturating() {
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn saturating_add<T>(a: T, b: T) -> T;
- }
+ #[rustc_intrinsic]
+ pub fn saturating_add<T>(a: T, b: T) -> T;
const GOAL: u8 = saturating_add(10, 250);
"#,
@@ -275,9 +272,8 @@ fn saturating() {
);
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn saturating_sub<T>(a: T, b: T) -> T;
- }
+ #[rustc_intrinsic]
+ pub fn saturating_sub<T>(a: T, b: T) -> T;
const GOAL: bool = saturating_sub(5u8, 7) == 0 && saturating_sub(8u8, 4) == 4;
"#,
@@ -285,9 +281,8 @@ fn saturating() {
);
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn saturating_add<T>(a: T, b: T) -> T;
- }
+ #[rustc_intrinsic]
+ pub fn saturating_add<T>(a: T, b: T) -> T;
const GOAL: i8 = saturating_add(5, 8);
"#,
@@ -330,9 +325,8 @@ fn allocator() {
fn overflowing_add() {
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
- }
+ #[rustc_intrinsic]
+ pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
const GOAL: u8 = add_with_overflow(1, 2).0;
"#,
@@ -340,9 +334,8 @@ fn overflowing_add() {
);
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
- }
+ #[rustc_intrinsic]
+ pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
const GOAL: u8 = add_with_overflow(1, 2).1 as u8;
"#,
@@ -357,9 +350,8 @@ fn needs_drop() {
//- minicore: drop, manually_drop, copy, sized, phantom_data
use core::mem::ManuallyDrop;
use core::marker::PhantomData;
- extern "rust-intrinsic" {
- pub fn needs_drop<T: ?Sized>() -> bool;
- }
+ #[rustc_intrinsic]
+ pub fn needs_drop<T: ?Sized>() -> bool;
struct X;
struct NeedsDrop;
impl Drop for NeedsDrop {
@@ -405,9 +397,8 @@ fn discriminant_value() {
r#"
//- minicore: discriminant, option
use core::marker::DiscriminantKind;
- extern "rust-intrinsic" {
- pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;
- }
+ #[rustc_intrinsic]
+ pub fn discriminant_value<T>(v: &T) -> <T as DiscriminantKind>::Discriminant;
const GOAL: bool = {
discriminant_value(&Some(2i32)) == discriminant_value(&Some(5i32))
&& discriminant_value(&Some(2i32)) != discriminant_value(&None::<i32>)
@@ -442,11 +433,12 @@ fn floating_point() {
// FIXME(#17451): Add `f16` and `f128` tests once intrinsics are added.
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;
- }
+ #[rustc_intrinsic]
+ pub fn sqrtf32(x: f32) -> f32;
+ #[rustc_intrinsic]
+ pub fn powf32(a: f32, x: f32) -> f32;
+ #[rustc_intrinsic]
+ 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);
"#,
@@ -458,11 +450,12 @@ fn floating_point() {
#[allow(unknown_lints, clippy::unnecessary_min_or_max)]
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;
- }
+ #[rustc_intrinsic]
+ pub fn powif64(a: f64, x: i32) -> f64;
+ #[rustc_intrinsic]
+ pub fn sinf64(x: f64) -> f64;
+ #[rustc_intrinsic]
+ pub fn minnumf64(x: f64, y: f64) -> f64;
const GOAL: f64 = powif64(1.2, 5) + sinf64(3.4) + minnumf64(-7.8, 1.3);
"#,
@@ -478,21 +471,32 @@ 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;
- pub fn atomic_fence_seqcst();
- pub fn atomic_singlethreadfence_acqrel();
- }
+ #[rustc_intrinsic]
+ pub fn atomic_load_seqcst<T: Copy>(src: *const T) -> T;
+ #[rustc_intrinsic]
+ pub fn atomic_xchg_acquire<T: Copy>(dst: *mut T, src: T) -> T;
+ #[rustc_intrinsic]
+ pub fn atomic_cxchg_release_seqcst<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool);
+ #[rustc_intrinsic]
+ pub fn atomic_cxchgweak_acquire_acquire<T: Copy>(dst: *mut T, old: T, src: T) -> (T, bool);
+ #[rustc_intrinsic]
+ pub fn atomic_store_release<T: Copy>(dst: *mut T, val: T);
+ #[rustc_intrinsic]
+ pub fn atomic_xadd_acqrel<T: Copy>(dst: *mut T, src: T) -> T;
+ #[rustc_intrinsic]
+ pub fn atomic_xsub_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
+ #[rustc_intrinsic]
+ pub fn atomic_and_acquire<T: Copy>(dst: *mut T, src: T) -> T;
+ #[rustc_intrinsic]
+ pub fn atomic_nand_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
+ #[rustc_intrinsic]
+ pub fn atomic_or_release<T: Copy>(dst: *mut T, src: T) -> T;
+ #[rustc_intrinsic]
+ pub fn atomic_xor_seqcst<T: Copy>(dst: *mut T, src: T) -> T;
+ #[rustc_intrinsic]
+ pub fn atomic_fence_seqcst();
+ #[rustc_intrinsic]
+ pub fn atomic_singlethreadfence_acqrel();
fn should_not_reach() {
_ // fails the test if executed
@@ -528,10 +532,10 @@ fn offset() {
check_number(
r#"
//- minicore: coerce_unsized, index, slice
- extern "rust-intrinsic" {
- pub fn offset<Ptr, Delta>(dst: Ptr, offset: Delta) -> Ptr;
- pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
- }
+ #[rustc_intrinsic]
+ pub fn offset<Ptr, Delta>(dst: Ptr, offset: Delta) -> Ptr;
+ #[rustc_intrinsic]
+ pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
const GOAL: i32 = unsafe {
let ar: &[(i32, i32, i32)] = &[
@@ -557,9 +561,8 @@ 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;
- }
+ #[rustc_intrinsic]
+ pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
const GOAL: u8 = unsafe {
let ar: &[(u8, u8, u8)] = &[
@@ -583,9 +586,8 @@ fn arith_offset() {
fn copy_nonoverlapping() {
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
- }
+ #[rustc_intrinsic]
+ pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
const GOAL: u8 = unsafe {
let mut x = 2;
@@ -602,9 +604,8 @@ fn copy_nonoverlapping() {
fn write_bytes() {
check_number(
r#"
- extern "rust-intrinsic" {
- fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
- }
+ #[rustc_intrinsic]
+ fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
const GOAL: i32 = unsafe {
let mut x = 2;
@@ -620,9 +621,8 @@ fn write_bytes() {
fn write_via_move() {
check_number(
r#"
- extern "rust-intrinsic" {
- fn write_via_move<T>(ptr: *mut T, value: T);
- }
+ #[rustc_intrinsic]
+ fn write_via_move<T>(ptr: *mut T, value: T);
const GOAL: i32 = unsafe {
let mut x = 2;
@@ -639,9 +639,8 @@ 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);
- }
+ #[rustc_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];
@@ -659,9 +658,8 @@ fn copy() {
fn ctpop() {
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn ctpop<T: Copy>(x: T) -> T;
- }
+ #[rustc_intrinsic]
+ pub fn ctpop<T: Copy>(x: T) -> T;
const GOAL: i64 = ctpop(-29);
"#,
@@ -673,9 +671,8 @@ fn ctpop() {
fn ctlz() {
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn ctlz<T: Copy>(x: T) -> T;
- }
+ #[rustc_intrinsic]
+ pub fn ctlz<T: Copy>(x: T) -> T;
const GOAL: u8 = ctlz(0b0001_1100_u8);
"#,
@@ -687,9 +684,8 @@ fn ctlz() {
fn cttz() {
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn cttz<T: Copy>(x: T) -> T;
- }
+ #[rustc_intrinsic]
+ pub fn cttz<T: Copy>(x: T) -> T;
const GOAL: i64 = cttz(-24);
"#,
@@ -701,9 +697,8 @@ fn cttz() {
fn rotate() {
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn rotate_left<T: Copy>(x: T, y: T) -> T;
- }
+ #[rustc_intrinsic]
+ pub fn rotate_left<T: Copy>(x: T, y: T) -> T;
const GOAL: i64 = rotate_left(0xaa00000000006e1i64, 12);
"#,
@@ -711,9 +706,8 @@ fn rotate() {
);
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn rotate_right<T: Copy>(x: T, y: T) -> T;
- }
+ #[rustc_intrinsic]
+ pub fn rotate_right<T: Copy>(x: T, y: T) -> T;
const GOAL: i64 = rotate_right(0x6e10aa, 12);
"#,
@@ -721,9 +715,8 @@ fn rotate() {
);
check_number(
r#"
- extern "rust-intrinsic" {
- pub fn rotate_left<T: Copy>(x: T, y: T) -> T;
- }
+ #[rustc_intrinsic]
+ pub fn rotate_left<T: Copy>(x: T, y: T) -> T;
const GOAL: i8 = rotate_left(129, 2);
"#,
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 3bf2d9a6a6..c24a5b943d 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -1,28 +1,38 @@
//! The home of `HirDatabase`, which is the Salsa database containing all the
//! type inference-related queries.
+use arrayvec::ArrayVec;
use base_db::{Crate, target::TargetLoadError};
use either::Either;
use hir_def::{
- AdtId, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
- ExpressionStoreOwnerId, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId,
- StaticId, TraitId, TypeAliasId, VariantId, builtin_derive::BuiltinDeriveImplMethod,
- db::DefDatabase, expr_store::ExpressionStore, hir::ExprId, layout::TargetDataLayout,
+ AdtId, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, EnumVariantId,
+ ExpressionStoreOwnerId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
+ LocalFieldId, ModuleId, StaticId, TraitId, TypeAliasId, VariantId,
+ builtin_derive::BuiltinDeriveImplMethod,
+ db::DefDatabase,
+ expr_store::ExpressionStore,
+ hir::{ClosureKind, ExprId, generics::LocalTypeOrConstParamId},
+ layout::TargetDataLayout,
+ resolver::{HasResolver, Resolver},
+ signatures::{ConstSignature, StaticSignature},
};
use la_arena::ArenaMap;
-use salsa::plumbing::AsId;
+use span::Edition;
+use stdx::impl_from;
use triomphe::Arc;
use crate::{
- ImplTraitId, TyDefId, ValueTyDefId,
+ GenericDefaultsRef, GenericPredicates, ImplTraitId, InferBodyId, TyDefId, TyLoweringResult,
+ ValueTyDefId,
consteval::ConstEvalError,
dyn_compatibility::DynCompatibilityViolation,
layout::{Layout, LayoutError},
- lower::{Diagnostics, GenericDefaults},
+ lower::{GenericDefaults, TypeAliasBounds},
mir::{BorrowckResult, MirBody, MirLowerError},
next_solver::{
- Allocation, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredEarlyBinder,
- StoredGenericArgs, StoredTy, TraitRef, Ty, VariancesOf,
+ Allocation, Clause, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredClauses,
+ StoredEarlyBinder, StoredGenericArgs, StoredPolyFnSig, StoredTraitRef, StoredTy, TraitRef,
+ Ty, VariancesOf,
},
traits::{ParamEnvAndCrate, StoredParamEnvAndCrate},
};
@@ -33,33 +43,44 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
// FXME: Collapse `mir_body_for_closure` into `mir_body`
// and `monomorphized_mir_body_for_closure` into `monomorphized_mir_body`
- #[salsa::invoke(crate::mir::mir_body_query)]
- #[salsa::cycle(cycle_result = crate::mir::mir_body_cycle_result)]
- fn mir_body(&self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError>;
+ #[salsa::transparent]
+ fn mir_body(&self, def: InferBodyId) -> Result<&MirBody, MirLowerError> {
+ crate::mir::mir_body_query(self, def).as_ref().map_err(|err| err.clone())
+ }
- #[salsa::invoke(crate::mir::mir_body_for_closure_query)]
- fn mir_body_for_closure(&self, def: InternedClosureId) -> Result<Arc<MirBody>, MirLowerError>;
+ #[salsa::transparent]
+ fn mir_body_for_closure(&self, def: InternedClosureId) -> Result<&MirBody, MirLowerError> {
+ crate::mir::mir_body_for_closure_query(self, def).as_ref().map_err(|err| err.clone())
+ }
- #[salsa::invoke(crate::mir::monomorphized_mir_body_query)]
- #[salsa::cycle(cycle_result = crate::mir::monomorphized_mir_body_cycle_result)]
+ #[salsa::transparent]
fn monomorphized_mir_body(
&self,
- def: DefWithBodyId,
+ def: InferBodyId,
subst: StoredGenericArgs,
env: StoredParamEnvAndCrate,
- ) -> Result<Arc<MirBody>, MirLowerError>;
+ ) -> Result<&MirBody, MirLowerError> {
+ crate::mir::monomorphized_mir_body_query(self, def, subst, env)
+ .as_ref()
+ .map_err(|err| err.clone())
+ }
- #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)]
+ #[salsa::transparent]
fn monomorphized_mir_body_for_closure(
&self,
def: InternedClosureId,
subst: StoredGenericArgs,
env: StoredParamEnvAndCrate,
- ) -> Result<Arc<MirBody>, MirLowerError>;
+ ) -> Result<&MirBody, MirLowerError> {
+ crate::mir::monomorphized_mir_body_for_closure_query(self, def, subst, env)
+ .as_ref()
+ .map_err(|err| err.clone())
+ }
- #[salsa::invoke(crate::mir::borrowck_query)]
- #[salsa::lru(2024)]
- fn borrowck(&self, def: DefWithBodyId) -> Result<Arc<[BorrowckResult]>, MirLowerError>;
+ #[salsa::transparent]
+ fn borrowck(&self, def: InferBodyId) -> Result<&[BorrowckResult], MirLowerError> {
+ crate::mir::borrowck_query(self, def).as_ref().map(|it| &**it).map_err(|err| err.clone())
+ }
#[salsa::invoke(crate::consteval::const_eval)]
#[salsa::transparent]
@@ -70,6 +91,15 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
trait_env: Option<ParamEnvAndCrate<'db>>,
) -> Result<Allocation<'db>, ConstEvalError>;
+ #[salsa::invoke(crate::consteval::anon_const_eval)]
+ #[salsa::transparent]
+ fn anon_const_eval<'db>(
+ &'db self,
+ def: AnonConstId,
+ subst: GenericArgs<'db>,
+ trait_env: Option<ParamEnvAndCrate<'db>>,
+ ) -> Result<Allocation<'db>, ConstEvalError>;
+
#[salsa::invoke(crate::consteval::const_eval_static)]
#[salsa::transparent]
fn const_eval_static<'db>(&'db self, def: StaticId) -> Result<Allocation<'db>, ConstEvalError>;
@@ -106,8 +136,10 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
env: StoredParamEnvAndCrate,
) -> Result<Arc<Layout>, LayoutError>;
- #[salsa::invoke(crate::layout::target_data_layout_query)]
- fn target_data_layout(&self, krate: Crate) -> Result<Arc<TargetDataLayout>, TargetLoadError>;
+ #[salsa::transparent]
+ fn target_data_layout(&self, krate: Crate) -> Result<&TargetDataLayout, TargetLoadError> {
+ crate::layout::target_data_layout_query(self, krate).as_ref().map_err(|err| err.clone())
+ }
#[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)]
fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option<DynCompatibilityViolation>;
@@ -118,10 +150,10 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
#[salsa::invoke(crate::lower::type_for_type_alias_with_diagnostics)]
#[salsa::transparent]
- fn type_for_type_alias_with_diagnostics<'db>(
- &'db self,
+ fn type_for_type_alias_with_diagnostics(
+ &self,
def: TypeAliasId,
- ) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics);
+ ) -> &TyLoweringResult<StoredEarlyBinder<StoredTy>>;
/// Returns the type of the value of the given constant, or `None` if the `ValueTyDefId` is
/// a `StructId` or `EnumVariantId` with a record constructor.
@@ -129,43 +161,71 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
#[salsa::transparent]
fn value_ty<'db>(&'db self, def: ValueTyDefId) -> Option<EarlyBinder<'db, Ty<'db>>>;
+ #[salsa::invoke(crate::lower::type_for_const)]
+ #[salsa::transparent]
+ fn type_for_const<'db>(&'db self, def: ConstId) -> EarlyBinder<'db, Ty<'db>>;
+
+ #[salsa::invoke(crate::lower::type_for_const_with_diagnostics)]
+ #[salsa::transparent]
+ fn type_for_const_with_diagnostics(
+ &self,
+ def: ConstId,
+ ) -> &TyLoweringResult<StoredEarlyBinder<StoredTy>>;
+
+ #[salsa::invoke(crate::lower::type_for_static)]
+ #[salsa::transparent]
+ fn type_for_static<'db>(&'db self, def: StaticId) -> EarlyBinder<'db, Ty<'db>>;
+
+ #[salsa::invoke(crate::lower::type_for_static_with_diagnostics)]
+ #[salsa::transparent]
+ fn type_for_static_with_diagnostics(
+ &self,
+ def: StaticId,
+ ) -> &TyLoweringResult<StoredEarlyBinder<StoredTy>>;
+
#[salsa::invoke(crate::lower::impl_self_ty_with_diagnostics)]
#[salsa::transparent]
- fn impl_self_ty_with_diagnostics<'db>(
- &'db self,
+ fn impl_self_ty_with_diagnostics(
+ &self,
def: ImplId,
- ) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics);
+ ) -> &TyLoweringResult<StoredEarlyBinder<StoredTy>>;
#[salsa::invoke(crate::lower::impl_self_ty_query)]
#[salsa::transparent]
fn impl_self_ty<'db>(&'db self, def: ImplId) -> EarlyBinder<'db, Ty<'db>>;
- #[salsa::invoke(crate::lower::const_param_ty_with_diagnostics)]
+ #[salsa::invoke(crate::lower::const_param_types_with_diagnostics)]
+ #[salsa::transparent]
+ fn const_param_types_with_diagnostics(
+ &self,
+ def: GenericDefId,
+ ) -> &TyLoweringResult<ArenaMap<LocalTypeOrConstParamId, StoredTy>>;
+
+ #[salsa::invoke(crate::lower::const_param_types)]
#[salsa::transparent]
- fn const_param_ty_with_diagnostics<'db>(&'db self, def: ConstParamId)
- -> (Ty<'db>, Diagnostics);
+ fn const_param_types(&self, def: GenericDefId) -> &ArenaMap<LocalTypeOrConstParamId, StoredTy>;
- #[salsa::invoke(crate::lower::const_param_ty_query)]
+ #[salsa::invoke(crate::lower::const_param_ty)]
#[salsa::transparent]
- fn const_param_ty_ns<'db>(&'db self, def: ConstParamId) -> Ty<'db>;
+ fn const_param_ty<'db>(&'db self, def: ConstParamId) -> Ty<'db>;
#[salsa::invoke(crate::lower::impl_trait_with_diagnostics)]
#[salsa::transparent]
- fn impl_trait_with_diagnostics<'db>(
- &'db self,
+ fn impl_trait_with_diagnostics(
+ &self,
def: ImplId,
- ) -> Option<(EarlyBinder<'db, TraitRef<'db>>, Diagnostics)>;
+ ) -> &Option<TyLoweringResult<StoredEarlyBinder<StoredTraitRef>>>;
#[salsa::invoke(crate::lower::impl_trait_query)]
#[salsa::transparent]
fn impl_trait<'db>(&'db self, def: ImplId) -> Option<EarlyBinder<'db, TraitRef<'db>>>;
- #[salsa::invoke(crate::lower::field_types_with_diagnostics_query)]
+ #[salsa::invoke(crate::lower::field_types_with_diagnostics)]
#[salsa::transparent]
fn field_types_with_diagnostics(
&self,
var: VariantId,
- ) -> &(ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>>, Diagnostics);
+ ) -> &TyLoweringResult<ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>>>;
#[salsa::invoke(crate::lower::field_types_query)]
#[salsa::transparent]
@@ -178,27 +238,51 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
def: CallableDefId,
) -> EarlyBinder<'db, PolyFnSig<'db>>;
+ #[salsa::invoke(crate::lower::callable_item_signature_with_diagnostics)]
+ #[salsa::transparent]
+ fn callable_item_signature_with_diagnostics(
+ &self,
+ def: CallableDefId,
+ ) -> &TyLoweringResult<StoredEarlyBinder<StoredPolyFnSig>>;
+
#[salsa::invoke(crate::lower::trait_environment)]
#[salsa::transparent]
fn trait_environment<'db>(&'db self, def: ExpressionStoreOwnerId) -> ParamEnv<'db>;
- #[salsa::invoke(crate::lower::generic_defaults_with_diagnostics_query)]
- #[salsa::cycle(cycle_result = crate::lower::generic_defaults_with_diagnostics_cycle_result)]
+ #[salsa::invoke(crate::lower::generic_defaults_with_diagnostics)]
+ #[salsa::transparent]
fn generic_defaults_with_diagnostics(
&self,
def: GenericDefId,
- ) -> (GenericDefaults, Diagnostics);
+ ) -> &TyLoweringResult<GenericDefaults>;
/// This returns an empty list if no parameter has default.
///
/// The binders of the returned defaults are only up to (not including) this parameter.
- #[salsa::invoke(crate::lower::generic_defaults_query)]
+ #[salsa::invoke(crate::lower::generic_defaults)]
+ #[salsa::transparent]
+ fn generic_defaults(&self, def: GenericDefId) -> GenericDefaultsRef<'_>;
+
+ #[salsa::invoke(crate::lower::type_alias_bounds_with_diagnostics)]
#[salsa::transparent]
- fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults;
+ fn type_alias_bounds_with_diagnostics(
+ &self,
+ type_alias: TypeAliasId,
+ ) -> &TyLoweringResult<TypeAliasBounds<StoredEarlyBinder<StoredClauses>>>;
+
+ #[salsa::invoke(crate::lower::type_alias_bounds)]
+ #[salsa::transparent]
+ fn type_alias_bounds<'db>(
+ &'db self,
+ type_alias: TypeAliasId,
+ ) -> EarlyBinder<'db, &'db [Clause<'db>]>;
- // Interned IDs for solver integration
- #[salsa::interned]
- fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
+ #[salsa::invoke(crate::lower::type_alias_self_bounds)]
+ #[salsa::transparent]
+ fn type_alias_self_bounds<'db>(
+ &'db self,
+ type_alias: TypeAliasId,
+ ) -> EarlyBinder<'db, &'db [Clause<'db>]>;
#[salsa::invoke(crate::variance::variances_of)]
#[salsa::transparent]
@@ -229,8 +313,12 @@ pub struct InternedOpaqueTyId {
pub loc: ImplTraitId,
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct InternedClosure(pub ExpressionStoreOwnerId, pub ExprId);
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct InternedClosure {
+ pub owner: InferBodyId,
+ pub expr: ExprId,
+ pub kind: ClosureKind,
+}
#[salsa_macros::interned(constructor = new_impl, no_lifetime, debug, revisions = usize::MAX)]
#[derive(PartialOrd, Ord)]
@@ -242,8 +330,8 @@ impl InternedClosureId {
#[inline]
pub fn new(db: &dyn HirDatabase, loc: InternedClosure) -> Self {
if cfg!(debug_assertions) {
- let store = ExpressionStore::of(db, loc.0);
- let expr = &store[loc.1];
+ let store = ExpressionStore::of(db, loc.owner.expression_store_owner(db));
+ let expr = &store[loc.expr];
assert!(
matches!(
expr,
@@ -270,14 +358,14 @@ impl InternedCoroutineId {
#[inline]
pub fn new(db: &dyn HirDatabase, loc: InternedClosure) -> Self {
if cfg!(debug_assertions) {
- let store = ExpressionStore::of(db, loc.0);
- let expr = &store[loc.1];
+ let store = ExpressionStore::of(db, loc.owner.expression_store_owner(db));
+ let expr = &store[loc.expr];
assert!(
matches!(
expr,
hir_def::hir::Expr::Closure {
- closure_kind: hir_def::hir::ClosureKind::Coroutine(_)
- | hir_def::hir::ClosureKind::AsyncBlock { .. },
+ closure_kind: hir_def::hir::ClosureKind::OldCoroutine(_)
+ | hir_def::hir::ClosureKind::Coroutine { .. },
..
}
),
@@ -299,13 +387,13 @@ impl InternedCoroutineClosureId {
#[inline]
pub fn new(db: &dyn HirDatabase, loc: InternedClosure) -> Self {
if cfg!(debug_assertions) {
- let store = ExpressionStore::of(db, loc.0);
- let expr = &store[loc.1];
+ let store = ExpressionStore::of(db, loc.owner.expression_store_owner(db));
+ let expr = &store[loc.expr];
assert!(
matches!(
expr,
hir_def::hir::Expr::Closure {
- closure_kind: hir_def::hir::ClosureKind::AsyncClosure,
+ closure_kind: hir_def::hir::ClosureKind::CoroutineClosure(_),
..
}
),
@@ -316,3 +404,112 @@ impl InternedCoroutineClosureId {
Self::new_impl(db, loc)
}
}
+
+/// An anonymous const expression that appears in a type position (e.g., array lengths,
+/// const generic arguments like `{ N + 1 }`, or const param defaults). Unlike named constants,
+/// these don't have their own `Body` — their expressions live in the parent's signature `ExpressionStore`.
+#[derive(Debug, Hash, PartialEq, Eq, Clone)]
+pub struct AnonConstLoc {
+ /// The owner store containing this expression.
+ pub owner: ExpressionStoreOwnerId,
+ /// The ExprId within the owner's ExpressionStore that is the root
+ /// of this anonymous const expression.
+ pub expr: ExprId,
+ pub ty: StoredEarlyBinder<StoredTy>,
+ /// Whether to allow using generic params from the owner.
+ /// true for array repeats, false for everything else.
+ pub(crate) allow_using_generic_params: bool,
+}
+
+#[salsa_macros::interned(debug, no_lifetime, revisions = usize::MAX)]
+#[derive(PartialOrd, Ord)]
+pub struct AnonConstId {
+ #[returns(ref)]
+ pub loc: AnonConstLoc,
+}
+
+impl HasModule for AnonConstId {
+ fn module(&self, db: &dyn DefDatabase) -> ModuleId {
+ self.loc(db).owner.module(db)
+ }
+}
+
+impl HasResolver for AnonConstId {
+ fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
+ self.loc(db).owner.resolver(db)
+ }
+}
+
+impl AnonConstId {
+ pub fn all_from_signature(
+ db: &dyn HirDatabase,
+ def: GenericDefId,
+ ) -> ArrayVec<&[AnonConstId], 5> {
+ let mut result = ArrayVec::new();
+
+ // Queries common to all generic defs:
+ result.push(db.generic_defaults_with_diagnostics(def).defined_anon_consts());
+ result.push(GenericPredicates::query_with_diagnostics(db, def).defined_anon_consts());
+ result.push(db.const_param_types_with_diagnostics(def).defined_anon_consts());
+
+ match def {
+ GenericDefId::ImplId(id) => {
+ result.push(db.impl_self_ty_with_diagnostics(id).defined_anon_consts());
+ if let Some(trait_ref) = db.impl_trait_with_diagnostics(id) {
+ result.push(trait_ref.defined_anon_consts());
+ }
+ }
+ GenericDefId::TypeAliasId(id) => {
+ result.push(db.type_for_type_alias_with_diagnostics(id).defined_anon_consts());
+ result.push(db.type_alias_bounds_with_diagnostics(id).defined_anon_consts());
+ }
+ GenericDefId::FunctionId(id) => result
+ .push(db.callable_item_signature_with_diagnostics(id.into()).defined_anon_consts()),
+ GenericDefId::ConstId(def) => {
+ result.push(db.type_for_const_with_diagnostics(def).defined_anon_consts())
+ }
+ GenericDefId::StaticId(def) => {
+ result.push(db.type_for_static_with_diagnostics(def).defined_anon_consts())
+ }
+ GenericDefId::TraitId(_) | GenericDefId::AdtId(_) => {}
+ }
+
+ result
+ }
+}
+
+/// A constant, which might appears as a const item, an anonymous const block in expressions
+/// or patterns, or as a constant in types with const generics.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
+pub enum GeneralConstId {
+ ConstId(ConstId),
+ StaticId(StaticId),
+ AnonConstId(AnonConstId),
+}
+
+impl_from!(ConstId, StaticId, AnonConstId for GeneralConstId);
+
+impl GeneralConstId {
+ pub fn generic_def(self, db: &dyn HirDatabase) -> Option<GenericDefId> {
+ match self {
+ GeneralConstId::ConstId(it) => Some(it.into()),
+ GeneralConstId::StaticId(it) => Some(it.into()),
+ GeneralConstId::AnonConstId(it) => Some(it.loc(db).owner.generic_def(db)),
+ }
+ }
+
+ pub fn name(self, db: &dyn DefDatabase) -> String {
+ match self {
+ GeneralConstId::StaticId(it) => {
+ StaticSignature::of(db, it).name.display(db, Edition::CURRENT).to_string()
+ }
+ GeneralConstId::ConstId(const_id) => {
+ ConstSignature::of(db, const_id).name.as_ref().map_or_else(
+ || "_".to_owned(),
+ |name| name.display(db, Edition::CURRENT).to_string(),
+ )
+ }
+ GeneralConstId::AnonConstId(_) => "{const}".to_owned(),
+ }
+ }
+}
diff --git a/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs
index 89d8c0e91d..76fbb66b06 100644
--- a/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -33,7 +33,7 @@ use hir_expand::{
HirFileId,
name::{AsName, Name},
};
-use intern::sym;
+use rustc_abi::ExternAbi;
use stdx::{always, never};
use syntax::{
AstNode, AstPtr, ToSmolStr,
@@ -211,7 +211,7 @@ impl<'a> DeclValidator<'a> {
// Don't run the lint on extern "[not Rust]" fn items with the
// #[no_mangle] attribute.
let no_mangle = AttrFlags::query(self.db, func.into()).contains(AttrFlags::NO_MANGLE);
- if no_mangle && data.abi.as_ref().is_some_and(|abi| *abi != sym::Rust) {
+ if no_mangle && data.abi != ExternAbi::Rust {
cov_mark::hit!(extern_func_no_mangle_ignored);
} else {
self.create_incorrect_case_diagnostic_for_item_name(
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 068118c705..760ebd27e0 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -7,7 +7,8 @@ use std::fmt;
use base_db::Crate;
use either::Either;
use hir_def::{
- AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup,
+ AdtId, AssocItemId, CallableDefId, DefWithBodyId, HasModule, ItemContainerId, Lookup,
+ attrs::AttrFlags,
lang_item::LangItems,
resolver::{HasResolver, ValueNs},
};
@@ -15,7 +16,7 @@ use intern::sym;
use itertools::Itertools;
use rustc_hash::FxHashSet;
use rustc_pattern_analysis::constructor::Constructor;
-use rustc_type_ir::inherent::{AdtDef, IntoKind};
+use rustc_type_ir::inherent::IntoKind;
use syntax::{
AstNode,
ast::{self, UnaryOp},
@@ -33,7 +34,7 @@ use crate::{
},
display::{DisplayTarget, HirDisplay},
next_solver::{
- DbInterner, ParamEnv, Ty, TyKind, TypingMode,
+ CallableIdWrapper, DbInterner, ParamEnv, Ty, TyKind, TypingMode,
infer::{DbInternerInferExt, InferCtxt},
},
};
@@ -44,7 +45,7 @@ pub(crate) use hir_def::{
hir::{Expr, ExprId, MatchArm, Pat, PatId, RecordSpread, Statement},
};
-pub enum BodyValidationDiagnostic {
+pub enum BodyValidationDiagnostic<'db> {
RecordMissingFields {
record: Either<ExprId, PatId>,
variant: VariantId,
@@ -67,14 +68,18 @@ pub enum BodyValidationDiagnostic {
RemoveUnnecessaryElse {
if_expr: ExprId,
},
+ UnusedMustUse {
+ expr: ExprId,
+ message: Option<&'db str>,
+ },
}
-impl BodyValidationDiagnostic {
+impl<'db> BodyValidationDiagnostic<'db> {
pub fn collect(
- db: &dyn HirDatabase,
+ db: &'db dyn HirDatabase,
owner: DefWithBodyId,
validate_lints: bool,
- ) -> Vec<BodyValidationDiagnostic> {
+ ) -> Vec<BodyValidationDiagnostic<'db>> {
let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered();
let infer = InferenceResult::of(db, owner);
let body = Body::of(db, owner);
@@ -101,7 +106,7 @@ struct ExprValidator<'db> {
body: &'db Body,
infer: &'db InferenceResult,
env: ParamEnv<'db>,
- diagnostics: Vec<BodyValidationDiagnostic>,
+ diagnostics: Vec<BodyValidationDiagnostic<'db>>,
validate_lints: bool,
infcx: InferCtxt<'db>,
}
@@ -177,7 +182,7 @@ impl<'db> ExprValidator<'db> {
}
// Check that the number of arguments matches the number of parameters.
- if self.infer.expr_type_mismatches().next().is_some() {
+ if self.infer.exprs_have_type_mismatches() {
// FIXME: Due to shortcomings in the current type system implementation, only emit
// this diagnostic if there are no type mismatches in the containing function.
} else if let Expr::MethodCall { receiver, .. } = expr {
@@ -218,9 +223,7 @@ impl<'db> ExprValidator<'db> {
// Note: Skipping the entire diagnostic rather than just not including a faulty match arm is
// preferred to avoid the chance of false positives.
for arm in arms {
- let Some(pat_ty) = self.infer.type_of_pat_with_adjust(arm.pat) else {
- return;
- };
+ let pat_ty = self.infer.type_of_pat_with_adjust(arm.pat);
if pat_ty.references_non_lt_error() {
return;
}
@@ -313,7 +316,7 @@ impl<'db> ExprValidator<'db> {
value_or_partial.is_none_or(|v| !matches!(v, ValueNs::StaticId(_)))
}
Expr::Field { expr, .. } => match self.infer.expr_ty(*expr).kind() {
- TyKind::Adt(adt, ..) if matches!(adt.def_id().0, AdtId::UnionId(_)) => false,
+ TyKind::Adt(adt, ..) if matches!(adt.def_id(), AdtId::UnionId(_)) => false,
_ => self.is_known_valid_scrutinee(*expr),
},
Expr::Index { base, .. } => self.is_known_valid_scrutinee(*base),
@@ -330,52 +333,71 @@ impl<'db> ExprValidator<'db> {
let pattern_arena = Arena::new();
let cx = MatchCheckCtx::new(self.owner.module(self.db()), &self.infcx, self.env);
for stmt in &**statements {
- let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else {
- continue;
+ let diag = match *stmt {
+ Statement::Expr { expr: stmt_expr, has_semi: true } if self.validate_lints => {
+ self.check_unused_must_use(stmt_expr)
+ }
+ Statement::Let { pat, initializer, else_branch: None, .. } => {
+ self.check_non_exhaustive_let(&cx, &pattern_arena, pat, initializer)
+ }
+ _ => None,
};
- if self.infer.type_mismatch_for_pat(pat).is_some() {
- continue;
- }
- let Some(initializer) = initializer else { continue };
- let Some(ty) = self.infer.type_of_expr_with_adjust(initializer) else { continue };
- if ty.references_non_lt_error() {
- continue;
+ if let Some(diag) = diag {
+ self.diagnostics.push(diag);
}
+ }
+ }
- let mut have_errors = false;
- let deconstructed_pat = self.lower_pattern(&cx, pat, &mut have_errors);
+ fn check_non_exhaustive_let<'a>(
+ &self,
+ cx: &MatchCheckCtx<'a, 'db>,
+ pattern_arena: &'a Arena<DeconstructedPat<'a, 'db>>,
+ pat: PatId,
+ initializer: Option<ExprId>,
+ ) -> Option<BodyValidationDiagnostic<'db>> {
+ if self.infer.pat_has_type_mismatch(pat) {
+ return None;
+ }
+ let initializer = initializer?;
+ let ty = self.infer.type_of_expr_with_adjust(initializer)?;
+ if ty.references_non_lt_error() {
+ return None;
+ }
- // optimization, wildcard trivially hold
- if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) {
- continue;
- }
+ let mut have_errors = false;
+ let deconstructed_pat = self.lower_pattern(cx, pat, &mut have_errors);
- let match_arm = rustc_pattern_analysis::MatchArm {
- pat: pattern_arena.alloc(deconstructed_pat),
- has_guard: false,
- arm_data: (),
- };
- let report = match cx.compute_match_usefulness(&[match_arm], ty, None) {
- Ok(v) => v,
- Err(e) => {
- debug!(?e, "match usefulness error");
- continue;
- }
- };
- let witnesses = report.non_exhaustiveness_witnesses;
- if !witnesses.is_empty() {
- self.diagnostics.push(BodyValidationDiagnostic::NonExhaustiveLet {
- pat,
- uncovered_patterns: missing_match_arms(
- &cx,
- ty,
- witnesses,
- false,
- self.owner.krate(self.db()),
- ),
- });
+ // optimization, wildcard trivially hold
+ if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) {
+ return None;
+ }
+
+ let match_arm = rustc_pattern_analysis::MatchArm {
+ pat: pattern_arena.alloc(deconstructed_pat),
+ has_guard: false,
+ arm_data: (),
+ };
+ let report = match cx.compute_match_usefulness(&[match_arm], ty, None) {
+ Ok(v) => v,
+ Err(e) => {
+ debug!(?e, "match usefulness error");
+ return None;
}
+ };
+ let witnesses = report.non_exhaustiveness_witnesses;
+ if witnesses.is_empty() {
+ return None;
}
+ Some(BodyValidationDiagnostic::NonExhaustiveLet {
+ pat,
+ uncovered_patterns: missing_match_arms(
+ cx,
+ ty,
+ witnesses,
+ false,
+ self.owner.krate(self.db()),
+ ),
+ })
}
fn lower_pattern<'a>(
@@ -393,6 +415,34 @@ impl<'db> ExprValidator<'db> {
pattern
}
+ fn check_unused_must_use(&self, expr: ExprId) -> Option<BodyValidationDiagnostic<'db>> {
+ let fn_def = match &self.body[expr] {
+ Expr::Call { callee, .. } => {
+ let callee_ty = self.infer.expr_ty(*callee);
+ if let TyKind::FnDef(CallableIdWrapper(CallableDefId::FunctionId(func)), _) =
+ callee_ty.kind()
+ {
+ Some(func.into())
+ } else {
+ None
+ }
+ }
+ Expr::MethodCall { .. } => {
+ self.infer.method_resolution(expr).map(|(func, _)| func.into())
+ }
+ _ => return None,
+ };
+ let ty_def = self.infer.type_of_expr_with_adjust(expr).and_then(|ty| match ty.kind() {
+ TyKind::Adt(adt, _) => Some(adt.def_id().into()),
+ _ => None,
+ });
+ let must_use_diag = |owner| {
+ AttrFlags::must_use_message(self.db(), owner?)
+ .map(|message| BodyValidationDiagnostic::UnusedMustUse { expr, message })
+ };
+ must_use_diag(fn_def).or_else(|| must_use_diag(ty_def))
+ }
+
fn check_for_trailing_return(&mut self, body_expr: ExprId, body: &Body) {
if !self.validate_lints {
return;
@@ -630,13 +680,13 @@ pub fn record_pattern_missing_fields(
fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResult) -> bool {
fn walk(pat: PatId, body: &Body, infer: &InferenceResult, has_type_mismatches: &mut bool) {
- match infer.type_mismatch_for_pat(pat) {
- Some(_) => *has_type_mismatches = true,
- None if *has_type_mismatches => (),
- None => {
+ match infer.pat_has_type_mismatch(pat) {
+ true => *has_type_mismatches = true,
+ false if *has_type_mismatches => (),
+ false => {
let pat = &body[pat];
if let Pat::ConstBlock(expr) | Pat::Lit(expr) = *pat {
- *has_type_mismatches |= infer.type_mismatch_for_expr(expr).is_some();
+ *has_type_mismatches |= infer.expr_has_type_mismatch(expr);
if *has_type_mismatches {
return;
}
diff --git a/crates/hir-ty/src/diagnostics/match_check.rs b/crates/hir-ty/src/diagnostics/match_check.rs
index f559c26bf5..8356329d96 100644
--- a/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/crates/hir-ty/src/diagnostics/match_check.rs
@@ -22,7 +22,7 @@ use span::Edition;
use stdx::{always, never, variance::PhantomCovariantLifetime};
use crate::{
- InferenceResult,
+ ByRef, InferenceResult,
db::HirDatabase,
display::{HirDisplay, HirDisplayError, HirFormatter},
infer::BindingMode,
@@ -121,7 +121,7 @@ impl<'a, 'db> PatCtxt<'a, 'db> {
self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold(
unadjusted_pat,
|subpattern, ref_ty| Pat {
- ty: ref_ty.as_ref(),
+ ty: ref_ty.source.as_ref(),
kind: Box::new(PatKind::Deref { subpattern }),
},
)
@@ -158,8 +158,8 @@ impl<'a, 'db> PatCtxt<'a, 'db> {
ty = self.infer.binding_ty(id);
let name = &self.body[id].name;
match (bm, ty.kind()) {
- (BindingMode::Ref(_), TyKind::Ref(_, rty, _)) => ty = rty,
- (BindingMode::Ref(_), _) => {
+ (BindingMode(ByRef::Yes(_), _), TyKind::Ref(_, rty, _)) => ty = rty,
+ (BindingMode(ByRef::Yes(_), _), _) => {
never!(
"`ref {}` has wrong type {:?}",
name.display(self.db, Edition::LATEST),
diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index bc3d9bbec6..46959aaa5a 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -4,15 +4,14 @@ use std::{cell::LazyCell, fmt};
use hir_def::{
EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId, attrs::AttrFlags,
- signatures::VariantFields,
+ signatures::VariantFields, unstable_features::UnstableFeatures,
};
-use intern::sym;
use rustc_pattern_analysis::{
IndexVec, PatCx, PrivateUninhabitedField,
constructor::{Constructor, ConstructorSet, VariantVisibility},
usefulness::{PlaceValidity, UsefulnessReport, compute_match_usefulness},
};
-use rustc_type_ir::inherent::{AdtDef, IntoKind};
+use rustc_type_ir::inherent::IntoKind;
use smallvec::{SmallVec, smallvec};
use stdx::never;
@@ -82,8 +81,7 @@ pub(crate) struct MatchCheckCtx<'a, 'db> {
impl<'a, 'db> MatchCheckCtx<'a, 'db> {
pub(crate) fn new(module: ModuleId, infcx: &'a InferCtxt<'db>, env: ParamEnv<'db>) -> Self {
let db = infcx.interner.db;
- let def_map = module.crate_def_map(db);
- let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
+ let exhaustive_patterns = UnstableFeatures::query(db, module.krate(db)).exhaustive_patterns;
Self { module, db, exhaustive_patterns, env, infcx }
}
@@ -151,7 +149,7 @@ impl<'a, 'db> MatchCheckCtx<'a, 'db> {
let fields_len = variant.fields(self.db).fields().len() as u32;
(0..fields_len).map(|idx| LocalFieldId::from_raw(idx.into())).map(move |fid| {
- let ty = field_tys[fid].get().instantiate(self.infcx.interner, substs);
+ let ty = field_tys[fid].get().instantiate(self.infcx.interner, substs).skip_norm_wip();
let ty = self
.infcx
.at(&ObligationCause::dummy(), self.env)
@@ -199,7 +197,7 @@ impl<'a, 'db> MatchCheckCtx<'a, 'db> {
arity = substs.len();
}
TyKind::Adt(adt_def, _) => {
- let adt = adt_def.def_id().0;
+ let adt = adt_def.def_id();
ctor = match pat.kind.as_ref() {
PatKind::Leaf { .. } if matches!(adt, hir_def::AdtId::UnionId(_)) => {
UnionField
@@ -267,7 +265,7 @@ impl<'a, 'db> MatchCheckCtx<'a, 'db> {
},
TyKind::Adt(adt, substs) => {
let variant =
- Self::variant_id_for_adt(self.db, pat.ctor(), adt.def_id().0).unwrap();
+ Self::variant_id_for_adt(self.db, pat.ctor(), adt.def_id()).unwrap();
let subpatterns = self
.list_variant_fields(*pat.ty(), variant)
.zip(subpatterns)
@@ -327,7 +325,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> {
TyKind::Tuple(tys) => tys.len(),
TyKind::Adt(adt_def, ..) => {
let variant =
- Self::variant_id_for_adt(self.db, ctor, adt_def.def_id().0).unwrap();
+ Self::variant_id_for_adt(self.db, ctor, adt_def.def_id()).unwrap();
variant.fields(self.db).fields().len()
}
_ => {
@@ -361,7 +359,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> {
}
TyKind::Ref(_, rty, _) => single(rty),
TyKind::Adt(adt_def, ..) => {
- let adt = adt_def.def_id().0;
+ let adt = adt_def.def_id();
let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
let visibilities =
@@ -430,7 +428,7 @@ impl<'a, 'db> PatCx for MatchCheckCtx<'a, 'db> {
TyKind::Int(..) | TyKind::Uint(..) => unhandled(),
TyKind::Array(..) | TyKind::Slice(..) => unhandled(),
TyKind::Adt(adt_def, subst) => {
- let adt = adt_def.def_id().0;
+ let adt = adt_def.def_id();
match adt {
hir_def::AdtId::EnumId(enum_id) => {
let enum_data = enum_id.enum_variants(cx.db);
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index ee33f7d158..c37a194d47 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -253,12 +253,13 @@ impl<'db> UnsafeVisitor<'db> {
| Pat::TupleStruct { .. }
| Pat::Ref { .. }
| Pat::Box { .. }
+ | Pat::Deref { .. }
| Pat::Expr(..)
| Pat::ConstBlock(..) => {
self.on_unsafe_op(current.into(), UnsafetyReason::UnionField)
}
// `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read.
- Pat::Missing | Pat::Wild | Pat::Or(_) => {}
+ Pat::Missing | Pat::Rest | Pat::Wild | Pat::Or(_) => {}
}
}
@@ -297,7 +298,7 @@ impl<'db> UnsafeVisitor<'db> {
self.check_call(current, func);
}
if let TyKind::FnPtr(_, hdr) = callee.kind()
- && hdr.safety == Safety::Unsafe
+ && hdr.safety() == Safety::Unsafe
{
self.on_unsafe_op(current.into(), UnsafetyReason::UnsafeFnCall);
}
@@ -424,7 +425,6 @@ impl<'db> UnsafeVisitor<'db> {
Expr::Closure { args, .. } => {
self.walk_pats_top(args.iter().copied(), current);
}
- Expr::Const(e) => self.walk_expr(*e),
_ => {}
}
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index e4a8def442..bc726b652f 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -10,17 +10,20 @@ use std::{
use base_db::{Crate, FxIndexMap};
use either::Either;
use hir_def::{
- ExpressionStoreOwnerId, FindPathConfig, GenericDefId, GenericParamId, HasModule, LocalFieldId,
- Lookup, ModuleDefId, ModuleId, TraitId,
+ ExpressionStoreOwnerId, FindPathConfig, GenericDefId, GenericParamId, HasModule,
+ ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, TypeAliasId,
expr_store::{ExpressionStore, path::Path},
find_path::{self, PrefixKind},
- hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate},
+ hir::{
+ ClosureKind as HirClosureKind, CoroutineKind,
+ generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate},
+ },
item_scope::ItemInNs,
item_tree::FieldsShape,
lang_item::LangItems,
signatures::{
- EnumSignature, FunctionSignature, StructSignature, TraitSignature, TypeAliasSignature,
- UnionSignature, VariantFields,
+ ConstSignature, EnumSignature, FunctionSignature, StaticSignature, StructSignature,
+ TraitSignature, TypeAliasSignature, UnionSignature, VariantFields,
},
type_ref::{
ConstRef, LifetimeRef, LifetimeRefId, TraitBoundModifier, TypeBound, TypeRef, TypeRefId,
@@ -32,6 +35,7 @@ use hir_expand::{mod_path::PathKind, name::Name};
use intern::{Internable, Interned, sym};
use itertools::Itertools;
use la_arena::ArenaMap;
+use rustc_abi::ExternAbi;
use rustc_apfloat::{
Float,
ieee::{Half as f16, Quad as f128},
@@ -40,24 +44,24 @@ use rustc_ast_ir::FloatTy;
use rustc_hash::FxHashSet;
use rustc_type_ir::{
AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, RegionKind, Upcast,
- inherent::{AdtDef, GenericArgs as _, IntoKind, Term as _, Ty as _, Tys as _},
+ inherent::{GenericArgs as _, IntoKind, Term as _, Ty as _, Tys as _},
};
use smallvec::SmallVec;
use span::Edition;
use stdx::never;
use crate::{
- CallableDefId, FnAbi, ImplTraitId, MemoryMap, ParamEnvAndCrate, consteval,
- db::{HirDatabase, InternedClosure},
- generics::generics,
+ CallableDefId, ImplTraitId, MemoryMap, ParamEnvAndCrate, consteval,
+ db::{GeneralConstId, HirDatabase},
+ generics::{ProvenanceSplit, generics},
layout::Layout,
lower::GenericPredicates,
mir::pad16,
next_solver::{
AliasTy, Allocation, Clause, ClauseKind, Const, ConstKind, DbInterner,
ExistentialPredicate, FnSig, GenericArg, GenericArgKind, GenericArgs, ParamEnv, PolyFnSig,
- Region, SolverDefId, StoredEarlyBinder, StoredTy, Term, TermKind, TraitRef, Ty, TyKind,
- TypingMode, ValTree,
+ Region, StoredEarlyBinder, StoredTy, Term, TermId, TermKind, TraitPredicate, TraitRef, Ty,
+ TyKind, TypingMode, Unnormalized, ValTree,
abi::Safety,
infer::{DbInternerInferExt, traits::ObligationCause},
},
@@ -65,6 +69,36 @@ use crate::{
utils::{detect_variant_from_bytes, fn_traits},
};
+fn async_gen_item_ty_from_yield_ty<'db>(
+ lang_items: &LangItems,
+ yield_ty: Ty<'db>,
+) -> Option<Ty<'db>> {
+ let poll_id = lang_items.Poll.map(hir_def::AdtId::EnumId)?;
+ let option_id = lang_items.Option.map(hir_def::AdtId::EnumId)?;
+
+ let TyKind::Adt(poll_def, poll_args) = yield_ty.kind() else {
+ return None;
+ };
+ if poll_def.def_id() != poll_id {
+ return None;
+ }
+ let [poll_inner] = poll_args.as_slice() else {
+ return None;
+ };
+ let poll_inner = poll_inner.ty()?;
+
+ let TyKind::Adt(option_def, option_args) = poll_inner.kind() else {
+ return None;
+ };
+ if option_def.def_id() != option_id {
+ return None;
+ }
+ let [item] = option_args.as_slice() else {
+ return None;
+ };
+ item.ty()
+}
+
pub type Result<T = (), E = HirDisplayError> = std::result::Result<T, E>;
pub trait HirWrite: fmt::Write {
@@ -101,7 +135,15 @@ pub struct HirFormatter<'a, 'db> {
display_lifetimes: DisplayLifetime,
display_kind: DisplayKind,
display_target: DisplayTarget,
- bounds_formatting_ctx: BoundsFormattingCtx<'db>,
+ /// We can have recursive bounds like the following case:
+ /// ```ignore
+ /// where
+ /// T: Foo,
+ /// T::FooAssoc: Baz<<T::FooAssoc as Bar>::BarAssoc> + Bar
+ /// ```
+ /// So, record the projection types met while formatting bounds and
+ /// prevent recursing into their bounds to avoid infinite loops.
+ currently_formatting_bounds: FxHashSet<AliasTy<'db>>,
/// Whether formatting `impl Trait1 + Trait2` or `dyn Trait1 + Trait2` needs parentheses around it,
/// for example when formatting `&(impl Trait1 + Trait2)`.
trait_bounds_need_parens: bool,
@@ -121,34 +163,6 @@ pub enum DisplayLifetime {
Never,
}
-#[derive(Default)]
-enum BoundsFormattingCtx<'db> {
- Entered {
- /// We can have recursive bounds like the following case:
- /// ```ignore
- /// where
- /// T: Foo,
- /// T::FooAssoc: Baz<<T::FooAssoc as Bar>::BarAssoc> + Bar
- /// ```
- /// So, record the projection types met while formatting bounds and
- //. prevent recursing into their bounds to avoid infinite loops.
- projection_tys_met: FxHashSet<AliasTy<'db>>,
- },
- #[default]
- Exited,
-}
-
-impl<'db> BoundsFormattingCtx<'db> {
- fn contains(&self, proj: &AliasTy<'db>) -> bool {
- match self {
- BoundsFormattingCtx::Entered { projection_tys_met } => {
- projection_tys_met.contains(proj)
- }
- BoundsFormattingCtx::Exited => false,
- }
- }
-}
-
impl<'db> HirFormatter<'_, 'db> {
pub fn start_location_link(&mut self, location: ModuleDefId) {
self.fmt.start_location_link(location);
@@ -162,26 +176,42 @@ impl<'db> HirFormatter<'_, 'db> {
self.fmt.end_location_link();
}
- fn format_bounds_with<T, F: FnOnce(&mut Self) -> T>(
+ fn format_bounds_with<F: FnOnce(&mut Self) -> Result>(
&mut self,
target: AliasTy<'db>,
format_bounds: F,
- ) -> T {
- match self.bounds_formatting_ctx {
- BoundsFormattingCtx::Entered { ref mut projection_tys_met } => {
- projection_tys_met.insert(target);
- format_bounds(self)
- }
- BoundsFormattingCtx::Exited => {
- let mut projection_tys_met = FxHashSet::default();
- projection_tys_met.insert(target);
- self.bounds_formatting_ctx = BoundsFormattingCtx::Entered { projection_tys_met };
- let res = format_bounds(self);
- // Since we want to prevent only the infinite recursions in bounds formatting
- // and do not want to skip formatting of other separate bounds, clear context
- // when exiting the formatting of outermost bounds
- self.bounds_formatting_ctx = BoundsFormattingCtx::Exited;
- res
+ ) -> Result {
+ if self.currently_formatting_bounds.insert(target) {
+ let result = format_bounds(self);
+ self.currently_formatting_bounds.remove(&target);
+ result
+ } else {
+ if self.display_kind.is_source_code() {
+ Err(HirDisplayError::DisplaySourceCodeError(DisplaySourceCodeError::Cycle))
+ } else {
+ match target.kind {
+ AliasTyKind::Projection { def_id } => {
+ let def_id = def_id.0;
+ let ItemContainerId::TraitId(trait_) = def_id.loc(self.db).container else {
+ panic!("expected an assoc type");
+ };
+ let trait_name = &TraitSignature::of(self.db, trait_).name;
+ let assoc_type_name = &TypeAliasSignature::of(self.db, def_id).name;
+ write!(
+ self,
+ "<… as {}>::{}",
+ trait_name.display(self.db, self.edition()),
+ assoc_type_name.display(self.db, self.edition()),
+ )?;
+ if target.args.len() > 1 {
+ self.write_str("<…>")?;
+ }
+ Ok(())
+ }
+ AliasTyKind::Inherent { .. }
+ | AliasTyKind::Opaque { .. }
+ | AliasTyKind::Free { .. } => self.write_str("…"),
+ }
}
}
}
@@ -335,7 +365,7 @@ pub trait HirDisplay<'db> {
display_kind: DisplayKind::SourceCode { target_module_id: module_id, allow_opaque },
show_container_bounds: false,
display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
- bounds_formatting_ctx: Default::default(),
+ currently_formatting_bounds: Default::default(),
trait_bounds_need_parens: false,
}) {
Ok(()) => {}
@@ -511,6 +541,7 @@ pub enum DisplaySourceCodeError {
PathNotFound,
Coroutine,
OpaqueType,
+ Cycle,
}
pub enum HirDisplayError {
@@ -571,7 +602,7 @@ impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> {
closure_style: self.closure_style,
show_container_bounds: self.show_container_bounds,
display_lifetimes: self.display_lifetimes,
- bounds_formatting_ctx: Default::default(),
+ currently_formatting_bounds: Default::default(),
trait_bounds_need_parens: false,
})
}
@@ -623,63 +654,58 @@ fn write_projection<'db>(
f: &mut HirFormatter<'_, 'db>,
alias: &AliasTy<'db>,
needs_parens_if_multi: bool,
+ def_id: TypeAliasId,
) -> Result {
- if f.should_truncate() {
- return write!(f, "{TYPE_HINT_TRUNCATION}");
- }
- let trait_ref = alias.trait_ref(f.interner);
- let self_ty = trait_ref.self_ty();
-
- // if we are projection on a type parameter, check if the projection target has bounds
- // itself, if so, we render them directly as `impl Bound` instead of the less useful
- // `<Param as Trait>::Assoc`
- if !f.display_kind.is_source_code()
- && let TyKind::Param(param) = self_ty.kind()
- && !f.bounds_formatting_ctx.contains(alias)
- {
- // FIXME: We shouldn't use `param.id`, it should be removed. We should know the
- // `GenericDefId` from the formatted type (store it inside the `HirFormatter`).
- let bounds = GenericPredicates::query_all(f.db, param.id.parent())
- .iter_identity()
- .filter(|wc| {
- let ty = match wc.kind().skip_binder() {
- ClauseKind::Trait(tr) => tr.self_ty(),
- ClauseKind::TypeOutlives(t) => t.0,
- _ => return false,
- };
- let TyKind::Alias(a) = ty.kind() else {
- return false;
- };
- a == *alias
- })
- .collect::<Vec<_>>();
- if !bounds.is_empty() {
- return f.format_bounds_with(*alias, |f| {
- write_bounds_like_dyn_trait_with_prefix(
+ f.format_bounds_with(*alias, |f| {
+ if f.should_truncate() {
+ return write!(f, "{TYPE_HINT_TRUNCATION}");
+ }
+ let trait_ref = alias.trait_ref(f.interner);
+ let self_ty = trait_ref.self_ty();
+
+ // if we are projection on a type parameter, check if the projection target has bounds
+ // itself, if so, we render them directly as `impl Bound` instead of the less useful
+ // `<Param as Trait>::Assoc`
+ if !f.display_kind.is_source_code()
+ && let TyKind::Param(param) = self_ty.kind()
+ {
+ // FIXME: We shouldn't use `param.id`, it should be removed. We should know the
+ // `GenericDefId` from the formatted type (store it inside the `HirFormatter`).
+ let bounds = GenericPredicates::query_all(f.db, param.id.parent())
+ .iter_identity()
+ .map(Unnormalized::skip_norm_wip)
+ .filter(|wc| {
+ let ty = match wc.kind().skip_binder() {
+ ClauseKind::Trait(tr) => tr.self_ty(),
+ ClauseKind::TypeOutlives(t) => t.0,
+ _ => return false,
+ };
+ let TyKind::Alias(a) = ty.kind() else {
+ return false;
+ };
+ a == *alias
+ })
+ .collect::<Vec<_>>();
+ if !bounds.is_empty() {
+ return write_bounds_like_dyn_trait_with_prefix(
f,
"impl",
Either::Left(Ty::new_alias(f.interner, *alias)),
&bounds,
SizedByDefault::NotSized,
needs_parens_if_multi,
- )
- });
+ );
+ }
}
- }
- write!(f, "<")?;
- self_ty.hir_fmt(f)?;
- write!(f, " as ")?;
- trait_ref.hir_fmt(f)?;
- write!(
- f,
- ">::{}",
- TypeAliasSignature::of(f.db, alias.kind.def_id().expect_type_alias())
- .name
- .display(f.db, f.edition())
- )?;
- let proj_params = &alias.args.as_slice()[trait_ref.args.len()..];
- hir_fmt_generics(f, proj_params, None, None)
+ write!(f, "<")?;
+ self_ty.hir_fmt(f)?;
+ write!(f, " as ")?;
+ trait_ref.hir_fmt(f)?;
+ write!(f, ">::{}", TypeAliasSignature::of(f.db, def_id).name.display(f.db, f.edition()))?;
+ let proj_params = &alias.args.as_slice()[trait_ref.args.len()..];
+ hir_fmt_generics(f, proj_params, None, None)
+ })
}
impl<'db> HirDisplay<'db> for GenericArg<'db> {
@@ -710,7 +736,7 @@ impl<'db> HirDisplay<'db> for Const<'db> {
}
ConstKind::Infer(..) => write!(f, "#c#"),
ConstKind::Param(param) => {
- let generics = generics(f.db, param.id.parent());
+ let generics = GenericParams::of(f.db, param.id.parent());
let param_data = &generics[param.id.local_id()];
f.start_location_link_generic(param.id.into());
write!(f, "{}", param_data.name().unwrap().display(f.db, f.edition()))?;
@@ -720,7 +746,25 @@ impl<'db> HirDisplay<'db> for Const<'db> {
ConstKind::Value(value) => render_const_scalar_from_valtree(f, value.ty, value.value),
ConstKind::Unevaluated(unev) => {
let c = unev.def.0;
- write!(f, "{}", c.name(f.db))?;
+ match c {
+ GeneralConstId::ConstId(id) => match &ConstSignature::of(f.db, id).name {
+ Some(name) => {
+ f.start_location_link(id.into());
+ write!(f, "{}", name.display(f.db, f.edition()))?;
+ f.end_location_link();
+ }
+ None => f.write_str("_")?,
+ },
+ GeneralConstId::StaticId(id) => {
+ let name = &StaticSignature::of(f.db, id).name;
+ f.start_location_link(id.into());
+ write!(f, "{}", name.display(f.db, f.edition()))?;
+ f.end_location_link();
+ }
+ GeneralConstId::AnonConstId(_) => {
+ f.write_str(if f.display_kind.is_source_code() { "_" } else { "{const}" })?
+ }
+ };
hir_fmt_generics(f, unev.args.as_slice(), c.generic_def(f.db), None)?;
Ok(())
}
@@ -738,7 +782,7 @@ fn render_const_scalar<'db>(
) -> Result {
let param_env = ParamEnv::empty();
let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis);
- let ty = infcx.at(&ObligationCause::new(), param_env).deeply_normalize(ty).unwrap_or(ty);
+ let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty);
render_const_scalar_inner(f, b, memory_map, ty, param_env)
}
@@ -857,7 +901,7 @@ fn render_const_scalar_inner<'db>(
f.write_str("&")?;
render_const_scalar(f, bytes, memory_map, t)
}
- TyKind::Adt(adt, _) if b.len() == 2 * size_of::<usize>() => match adt.def_id().0 {
+ TyKind::Adt(adt, _) if b.len() == 2 * size_of::<usize>() => match adt.def_id() {
hir_def::AdtId::StructId(s) => {
let data = StructSignature::of(f.db, s);
write!(f, "&{}", data.name.display(f.db, f.edition()))?;
@@ -911,7 +955,7 @@ fn render_const_scalar_inner<'db>(
f.write_str(")")
}
TyKind::Adt(def, args) => {
- let def = def.def_id().0;
+ let def = def.def_id();
let Ok(layout) = f.db.layout_of_adt(def, args.store(), param_env.store()) else {
return f.write_str("<layout-error>");
};
@@ -941,7 +985,7 @@ fn render_const_scalar_inner<'db>(
return f.write_str("<target-layout-not-available>");
};
let Some((var_id, var_layout)) =
- detect_variant_from_bytes(&layout, f.db, &target_data_layout, b, e)
+ detect_variant_from_bytes(&layout, f.db, target_data_layout, b, e)
else {
return f.write_str("<failed-to-detect-variant>");
};
@@ -1023,7 +1067,7 @@ fn render_const_scalar_from_valtree<'db>(
) -> Result {
let param_env = ParamEnv::empty();
let infcx = f.interner.infer_ctxt().build(TypingMode::PostAnalysis);
- let ty = infcx.at(&ObligationCause::new(), param_env).deeply_normalize(ty).unwrap_or(ty);
+ let ty = infcx.at(&ObligationCause::dummy(), param_env).deeply_normalize(ty).unwrap_or(ty);
render_const_scalar_from_valtree_inner(f, ty, valtree, param_env)
}
@@ -1176,7 +1220,7 @@ fn render_variant_after_name<'db>(
FieldsShape::Record | FieldsShape::Tuple => {
let render_field = |f: &mut HirFormatter<'_, 'db>, id: LocalFieldId| {
let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize();
- let ty = field_types[id].get().instantiate(f.interner, args);
+ let ty = field_types[id].get().instantiate(f.interner, args).skip_norm_wip();
let Ok(layout) = f.db.layout_of_ty(ty.store(), param_env.store()) else {
return f.write_str("<layout-error>");
};
@@ -1287,7 +1331,8 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
}
TyKind::FnDef(def, args) => {
let def = def.0;
- let sig = db.callable_item_signature(def).instantiate(interner, args);
+ let sig =
+ db.callable_item_signature(def).instantiate(interner, args).skip_norm_wip();
if f.display_kind.is_source_code() {
// `FnDef` is anonymous and there's no surface syntax for it. Show it as a
@@ -1297,7 +1342,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
if let Safety::Unsafe = sig.safety() {
write!(f, "unsafe ")?;
}
- if !matches!(sig.abi(), FnAbi::Rust | FnAbi::RustCall) {
+ if !sig.abi().is_rustic_abi() {
f.write_str("extern \"")?;
f.write_str(sig.abi().as_str())?;
f.write_str("\" ")?;
@@ -1331,8 +1376,14 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
if !args.is_empty() {
let generic_def_id = GenericDefId::from_callable(db, def);
let generics = generics(db, generic_def_id);
- let (parent_len, self_param, type_, const_, impl_, lifetime) =
- generics.provenance_split();
+ let ProvenanceSplit {
+ parent_total: parent_len,
+ has_self_param: self_param,
+ non_impl_trait_type_params: type_,
+ const_params: const_,
+ impl_trait_type_params: impl_,
+ lifetimes: lifetime,
+ } = generics.provenance_split();
let parameters = args.as_slice();
debug_assert_eq!(
parameters.len(),
@@ -1387,7 +1438,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
}
}
TyKind::Adt(def, parameters) => {
- let def_id = def.def_id().0;
+ let def_id = def.def_id();
f.start_location_link(def_id.into());
match f.display_kind {
DisplayKind::Diagnostics | DisplayKind::Test => {
@@ -1425,10 +1476,10 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
}
f.end_location_link();
- hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().0.into()), None)?;
+ hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().into()), None)?;
}
- TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => {
- write_projection(f, &alias_ty, trait_bounds_need_parens)?
+ TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Projection { def_id }, .. }) => {
+ write_projection(f, &alias_ty, trait_bounds_need_parens, def_id.0)?
}
TyKind::Foreign(alias) => {
let type_alias = TypeAliasSignature::of(db, alias.0);
@@ -1437,19 +1488,17 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
f.end_location_link();
}
TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Opaque { def_id }, .. }) => {
- let opaque_ty_id = match def_id {
- SolverDefId::InternedOpaqueTyId(id) => id,
- _ => unreachable!(),
- };
+ let opaque_ty_id = def_id.0;
if !f.display_kind.allows_opaque() {
return Err(HirDisplayError::DisplaySourceCodeError(
DisplaySourceCodeError::OpaqueType,
));
}
- let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id);
+ let impl_trait_id = opaque_ty_id.loc(db);
let data = impl_trait_id.predicates(db);
let bounds = data
.iter_instantiated_copied(interner, alias_ty.args.as_slice())
+ .map(Unnormalized::skip_norm_wip)
.collect::<Vec<_>>();
let krate = match impl_trait_id {
ImplTraitId::ReturnTypeImplTrait(func, _) => {
@@ -1519,6 +1568,15 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
}
TyKind::CoroutineClosure(id, args) => {
let id = id.0;
+ let closure_kind = match id.loc(db).kind {
+ HirClosureKind::CoroutineClosure(kind) => kind,
+ kind => panic!("invalid kind for coroutine closure: {kind:?}"),
+ };
+ let closure_label = match closure_kind {
+ CoroutineKind::Async => "async closure",
+ CoroutineKind::Gen => "gen closure",
+ CoroutineKind::AsyncGen => "async gen closure",
+ };
if f.display_kind.is_source_code() {
if !f.display_kind.allows_opaque() {
return Err(HirDisplayError::DisplaySourceCodeError(
@@ -1533,25 +1591,28 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
ClosureStyle::ClosureWithId => {
return write!(
f,
- "{{async closure#{:?}}}",
+ "{{{closure_label}#{:?}}}",
salsa::plumbing::AsId::as_id(&id).index()
);
}
ClosureStyle::ClosureWithSubst => {
write!(
f,
- "{{async closure#{:?}}}",
+ "{{{closure_label}#{:?}}}",
salsa::plumbing::AsId::as_id(&id).index()
)?;
return hir_fmt_generics(f, args.as_slice(), None, None);
}
_ => (),
}
- let kind = args.as_coroutine_closure().kind();
- let kind = match kind {
- rustc_type_ir::ClosureKind::Fn => "AsyncFn",
- rustc_type_ir::ClosureKind::FnMut => "AsyncFnMut",
- rustc_type_ir::ClosureKind::FnOnce => "AsyncFnOnce",
+ let callable_kind = args.as_coroutine_closure().kind();
+ let kind = match (closure_kind, callable_kind) {
+ (CoroutineKind::Async, rustc_type_ir::ClosureKind::Fn) => "AsyncFn",
+ (CoroutineKind::Async, rustc_type_ir::ClosureKind::FnMut) => "AsyncFnMut",
+ (CoroutineKind::Async, rustc_type_ir::ClosureKind::FnOnce) => "AsyncFnOnce",
+ (_, rustc_type_ir::ClosureKind::Fn) => "Fn",
+ (_, rustc_type_ir::ClosureKind::FnMut) => "FnMut",
+ (_, rustc_type_ir::ClosureKind::FnOnce) => "FnOnce",
};
let coroutine_sig = args.as_coroutine_closure().coroutine_closure_sig();
let coroutine_sig = coroutine_sig.skip_binder();
@@ -1559,7 +1620,11 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
let coroutine_output = coroutine_sig.return_ty;
match f.closure_style {
ClosureStyle::ImplFn => write!(f, "impl {kind}(")?,
- ClosureStyle::RANotation => write!(f, "async |")?,
+ ClosureStyle::RANotation => match closure_kind {
+ CoroutineKind::Async => write!(f, "async |")?,
+ CoroutineKind::Gen => write!(f, "gen |")?,
+ CoroutineKind::AsyncGen => write!(f, "async gen |")?,
+ },
_ => unreachable!(),
}
if coroutine_inputs.is_empty() {
@@ -1582,7 +1647,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
TyKind::Param(param) => {
// FIXME: We should not access `param.id`, it should be removed, and we should know the
// parent from the formatted type.
- let generics = generics(db, param.id.parent());
+ let generics = GenericParams::of(db, param.id.parent());
let param_data = &generics[param.id.local_id()];
match param_data {
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
@@ -1601,6 +1666,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
TypeParamProvenance::ArgumentImplTrait => {
let bounds = GenericPredicates::query_all(f.db, param.id.parent())
.iter_identity()
+ .map(Unnormalized::skip_norm_wip)
.filter(|wc| match wc.kind().skip_binder() {
ClauseKind::Trait(tr) => tr.self_ty() == *self,
ClauseKind::Projection(proj) => proj.self_ty() == *self,
@@ -1670,21 +1736,14 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
}
TyKind::Infer(..) => write!(f, "_")?,
TyKind::Coroutine(coroutine_id, subst) => {
- let InternedClosure(owner, expr_id) = coroutine_id.0.loc(db);
+ let kind = coroutine_id.0.loc(db).kind;
let CoroutineArgsParts { resume_ty, yield_ty, return_ty, .. } =
subst.split_coroutine_args();
- let body = ExpressionStore::of(db, owner);
- let expr = &body[expr_id];
- match expr {
- hir_def::hir::Expr::Closure {
- closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. },
- ..
- } => {
- let future_trait = f.lang_items().Future;
- let output = future_trait.and_then(|t| {
- t.trait_items(db)
- .associated_type_by_name(&Name::new_symbol_root(sym::Output))
- });
+ match kind {
+ HirClosureKind::Coroutine { kind: CoroutineKind::Async, .. } => {
+ let lang_items = f.lang_items();
+ let future_trait = lang_items.Future;
+ let output = lang_items.FutureOutput;
write!(f, "impl ")?;
if let Some(t) = future_trait {
f.start_location_link(t.into());
@@ -1705,10 +1764,57 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
return_ty.hir_fmt(f)?;
write!(f, ">")?;
}
- hir_def::hir::Expr::Closure {
- closure_kind: hir_def::hir::ClosureKind::Coroutine(..),
- ..
- } => {
+ HirClosureKind::Coroutine { kind: CoroutineKind::Gen, .. } => {
+ let lang_items = f.lang_items();
+ let iterator_trait = lang_items.Iterator;
+ let item = lang_items.IteratorItem;
+ write!(f, "impl ")?;
+ if let Some(t) = iterator_trait {
+ f.start_location_link(t.into());
+ }
+ write!(f, "Iterator")?;
+ if iterator_trait.is_some() {
+ f.end_location_link();
+ }
+ write!(f, "<")?;
+ if let Some(t) = item {
+ f.start_location_link(t.into());
+ }
+ write!(f, "Item")?;
+ if item.is_some() {
+ f.end_location_link();
+ }
+ write!(f, " = ")?;
+ yield_ty.hir_fmt(f)?;
+ write!(f, ">")?;
+ }
+ HirClosureKind::Coroutine { kind: CoroutineKind::AsyncGen, .. } => {
+ let lang_items = f.lang_items();
+ let async_iterator_trait = lang_items.AsyncIterator;
+ let item = lang_items.AsyncIteratorItem;
+ write!(f, "impl ")?;
+ if let Some(t) = async_iterator_trait {
+ f.start_location_link(t.into());
+ }
+ write!(f, "AsyncIterator")?;
+ if async_iterator_trait.is_some() {
+ f.end_location_link();
+ }
+ write!(f, "<")?;
+ if let Some(t) = item {
+ f.start_location_link(t.into());
+ }
+ write!(f, "Item")?;
+ if item.is_some() {
+ f.end_location_link();
+ }
+ write!(f, " = ")?;
+ let item_ty = async_gen_item_ty_from_yield_ty(f.lang_items(), yield_ty)
+ .unwrap_or(yield_ty);
+ item_ty.hir_fmt(f)?;
+ write!(f, ">")?;
+ }
+ HirClosureKind::OldCoroutine(..) => {
if f.display_kind.is_source_code() {
return Err(HirDisplayError::DisplaySourceCodeError(
DisplaySourceCodeError::Coroutine,
@@ -1724,7 +1830,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
write!(f, " -> ")?;
return_ty.hir_fmt(f)?;
}
- _ => panic!("invalid expr for coroutine: {expr:?}"),
+ _ => panic!("invalid kind for coroutine: {kind:?}"),
}
}
TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?,
@@ -1769,7 +1875,9 @@ fn generic_args_sans_defaults<'ga, 'db>(
let should_show = |arg: GenericArg<'db>, i: usize| match default_parameters.get(i) {
None => true,
Some(default_parameter) => {
- arg != default_parameter.instantiate(f.interner, &parameters[..i])
+ arg != default_parameter
+ .instantiate(f.interner, &parameters[..i])
+ .skip_norm_wip()
}
};
let mut default_from = 0;
@@ -1852,8 +1960,8 @@ fn hir_fmt_tys<'db>(
impl<'db> HirDisplay<'db> for PolyFnSig<'db> {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
- let FnSig { inputs_and_output, c_variadic, safety, abi: _ } = self.skip_binder();
- if let Safety::Unsafe = safety {
+ let FnSig { inputs_and_output, fn_sig_kind } = self.skip_binder();
+ if let Safety::Unsafe = fn_sig_kind.safety() {
write!(f, "unsafe ")?;
}
// FIXME: Enable this when the FIXME on FnAbi regarding PartialEq is fixed.
@@ -1864,7 +1972,7 @@ impl<'db> HirDisplay<'db> for PolyFnSig<'db> {
// }
write!(f, "fn(")?;
f.write_joined(inputs_and_output.inputs(), ", ")?;
- if c_variadic {
+ if fn_sig_kind.c_variadic() {
if inputs_and_output.inputs().is_empty() {
write!(f, "...")?;
} else {
@@ -2067,6 +2175,9 @@ fn write_bounds_like_dyn_trait<'db>(
}
}
ClauseKind::Projection(projection) => {
+ let TermId::TypeAliasId(assoc_ty_id) = projection.def_id().0 else {
+ continue;
+ };
// in types in actual Rust, these will always come
// after the corresponding Implemented predicate
if angle_open {
@@ -2075,7 +2186,6 @@ fn write_bounds_like_dyn_trait<'db>(
write!(f, "<")?;
angle_open = true;
}
- let assoc_ty_id = projection.def_id().expect_type_alias();
let type_alias = TypeAliasSignature::of(f.db, assoc_ty_id);
f.start_location_link(assoc_ty_id.into());
write!(f, "{}", type_alias.name.display(f.db, f.edition()))?;
@@ -2174,11 +2284,28 @@ impl<'db> HirDisplay<'db> for TraitRef<'db> {
}
}
+impl<'db> HirDisplay<'db> for TraitPredicate<'db> {
+ fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
+ self.self_ty().hir_fmt(f)?;
+ f.write_str(": ")?;
+ match self.polarity {
+ rustc_type_ir::PredicatePolarity::Positive => {}
+ rustc_type_ir::PredicatePolarity::Negative => f.write_char('!')?,
+ }
+ let trait_ = self.def_id().0;
+ f.start_location_link(trait_.into());
+ write!(f, "{}", TraitSignature::of(f.db, trait_).name.display(f.db, f.edition()))?;
+ f.end_location_link();
+ let substs = &self.trait_ref.args[1..];
+ hir_fmt_generic_args(f, substs, None, None)
+ }
+}
+
impl<'db> HirDisplay<'db> for Region<'db> {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
match self.kind() {
RegionKind::ReEarlyParam(param) => {
- let generics = generics(f.db, param.id.parent);
+ let generics = GenericParams::of(f.db, param.id.parent);
let param_data = &generics[param.id.local_id];
f.start_location_link_generic(param.id.into());
write!(f, "{}", param_data.name.display(f.db, f.edition()))?;
@@ -2364,9 +2491,9 @@ impl<'db> HirDisplayWithExpressionStore<'db> for TypeRefId {
if fn_.is_unsafe {
write!(f, "unsafe ")?;
}
- if let Some(abi) = &fn_.abi {
+ if fn_.abi != ExternAbi::Rust {
f.write_str("extern \"")?;
- f.write_str(abi.as_str())?;
+ f.write_str(fn_.abi.as_str())?;
f.write_str("\" ")?;
}
write!(f, "fn(")?;
diff --git a/crates/hir-ty/src/drop.rs b/crates/hir-ty/src/drop.rs
index 0d25d7dbd1..61e6720a29 100644
--- a/crates/hir-ty/src/drop.rs
+++ b/crates/hir-ty/src/drop.rs
@@ -22,9 +22,9 @@ use crate::{
#[salsa::tracked]
pub fn destructor(db: &dyn HirDatabase, adt: AdtId) -> Option<ImplId> {
let module = match adt {
- AdtId::EnumId(id) => db.lookup_intern_enum(id).container,
- AdtId::StructId(id) => db.lookup_intern_struct(id).container,
- AdtId::UnionId(id) => db.lookup_intern_union(id).container,
+ AdtId::EnumId(id) => id.loc(db).container,
+ AdtId::StructId(id) => id.loc(db).container,
+ AdtId::UnionId(id) => id.loc(db).container,
};
let interner = DbInterner::new_with(db, module.krate(db));
let drop_trait = interner.lang_items().Drop?;
@@ -70,7 +70,7 @@ fn has_drop_glue_impl<'db>(
let db = infcx.interner.db;
match ty.kind() {
TyKind::Adt(adt_def, subst) => {
- let adt_id = adt_def.def_id().0;
+ let adt_id = adt_def.def_id();
if adt_def.destructor(infcx.interner).is_some() {
return DropGlue::HasDropGlue;
}
@@ -87,7 +87,7 @@ fn has_drop_glue_impl<'db>(
.map(|(_, field_ty)| {
has_drop_glue_impl(
infcx,
- field_ty.get().instantiate(infcx.interner, subst),
+ field_ty.get().instantiate(infcx.interner, subst).skip_norm_wip(),
env,
visited,
)
@@ -107,7 +107,10 @@ fn has_drop_glue_impl<'db>(
.map(|(_, field_ty)| {
has_drop_glue_impl(
infcx,
- field_ty.get().instantiate(infcx.interner, subst),
+ field_ty
+ .get()
+ .instantiate(infcx.interner, subst)
+ .skip_norm_wip(),
env,
visited,
)
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs
index ba63343d49..34858212cb 100644
--- a/crates/hir-ty/src/dyn_compatibility.rs
+++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -6,8 +6,8 @@ use hir_def::{
AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, TypeAliasId,
TypeOrConstParamId, TypeParamId,
hir::generics::{GenericParams, LocalTypeOrConstParamId},
- nameres::crate_def_map,
signatures::{FunctionSignature, TraitFlags, TraitSignature},
+ unstable_features::UnstableFeatures,
};
use rustc_hash::FxHashSet;
use rustc_type_ir::{
@@ -21,11 +21,14 @@ use crate::{
db::{HirDatabase, InternedOpaqueTyId},
lower::{GenericPredicates, associated_ty_item_bounds},
next_solver::{
- AliasTy, Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, Goal, ParamEnv,
- ParamTy, SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, infer::DbInternerInferExt,
+ AliasTy, Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, ParamTy,
+ SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, Unnormalized,
+ infer::{
+ DbInternerInferExt,
+ traits::{Obligation, ObligationCause},
+ },
mk_param,
},
- traits::next_trait_solve_in_ctxt,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -111,8 +114,9 @@ where
// rustc checks for non-lifetime binders here, but we don't support HRTB yet
let trait_data = trait_.trait_items(db);
+ let mut features = None;
for (_, assoc_item) in &trait_data.items {
- dyn_compatibility_violation_for_assoc_item(db, trait_, *assoc_item, cb)?;
+ dyn_compatibility_violation_for_assoc_item(db, &mut features, trait_, *assoc_item, cb)?;
}
ControlFlow::Continue(())
@@ -142,8 +146,8 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b
// FIXME: We should use `explicit_predicates_of` here, which hasn't been implemented to
// rust-analyzer yet
// https://github.com/rust-lang/rust/blob/ddaf12390d3ffb7d5ba74491a48f3cd528e5d777/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L490
- elaborate::elaborate(interner, predicates.iter_identity()).any(|pred| {
- match pred.kind().skip_binder() {
+ elaborate::elaborate(interner, predicates.iter_identity().map(Unnormalized::skip_norm_wip)).any(
+ |pred| match pred.kind().skip_binder() {
ClauseKind::Trait(trait_pred) => {
if sized == trait_pred.def_id().0
&& let rustc_type_ir::TyKind::Param(param_ty) =
@@ -156,17 +160,17 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b
}
}
_ => false,
- }
- })
+ },
+ )
}
// rustc gathers all the spans that references `Self` for error rendering,
// but we don't have good way to render such locations.
// So, just return single boolean value for existence of such `Self` reference
fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool {
- GenericPredicates::query_explicit(db, trait_.into())
- .iter_identity()
- .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No))
+ GenericPredicates::query_explicit(db, trait_.into()).iter_identity().any(|pred| {
+ predicate_references_self(db, trait_, pred.skip_norm_wip(), AllowSelfProjection::No)
+ })
}
// Same as the above, `predicates_reference_self`
@@ -244,11 +248,7 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable<DbI
proj @ AliasTy { kind: AliasTyKind::Projection { .. }, .. },
) => match self.allow_self_projection {
AllowSelfProjection::Yes => {
- let trait_ = proj.trait_def_id(interner);
- let trait_ = match trait_ {
- SolverDefId::TraitId(id) => id,
- _ => unreachable!(),
- };
+ let trait_ = proj.trait_def_id(interner).0;
if self.super_traits.is_none() {
self.super_traits = Some(
elaborate::supertrait_def_ids(interner, self.trait_.into())
@@ -274,8 +274,9 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable<DbI
t.visit_with(&mut visitor).is_break()
}
-fn dyn_compatibility_violation_for_assoc_item<F>(
- db: &dyn HirDatabase,
+fn dyn_compatibility_violation_for_assoc_item<'db, F>(
+ db: &'db dyn HirDatabase,
+ features: &mut Option<&'db UnstableFeatures>,
trait_: TraitId,
item: AssocItemId,
cb: &mut F,
@@ -297,8 +298,10 @@ where
})
}
AssocItemId::TypeAliasId(it) => {
- let def_map = crate_def_map(db, trait_.krate(db));
- if def_map.is_unstable_feature_enabled(&intern::sym::generic_associated_type_extended) {
+ if features
+ .get_or_insert_with(|| UnstableFeatures::query(db, trait_.krate(db)))
+ .generic_associated_type_extended
+ {
ControlFlow::Continue(())
} else {
let generic_params = GenericParams::of(db, item.into());
@@ -397,7 +400,7 @@ fn receiver_is_dispatchable<'db>(
func: FunctionId,
sig: &EarlyBinder<'db, Binder<'db, rustc_type_ir::FnSig<DbInterner<'db>>>>,
) -> bool {
- let sig = sig.instantiate_identity();
+ let sig = sig.instantiate_identity().skip_norm_wip();
let module = trait_.module(db);
let interner = DbInterner::new_with(db, module.krate(db));
@@ -461,6 +464,7 @@ fn receiver_is_dispatchable<'db>(
interner,
generic_predicates
.iter_identity()
+ .map(Unnormalized::skip_norm_wip)
.chain([unsize_predicate.upcast(interner), trait_predicate.upcast(interner)])
.chain(meta_sized_predicate),
),
@@ -470,12 +474,11 @@ fn receiver_is_dispatchable<'db>(
// Receiver: DispatchFromDyn<Receiver[Self => U]>
let predicate =
TraitRef::new(interner, dispatch_from_dyn_did.into(), [receiver_ty, unsized_receiver_ty]);
- let goal = Goal::new(interner, param_env, predicate);
+ let obligation = Obligation::new(interner, ObligationCause::dummy(), param_env, predicate);
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
// the receiver is dispatchable iff the obligation holds
- let res = next_trait_solve_in_ctxt(&infcx, goal);
- res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes))
+ infcx.predicate_must_hold_modulo_regions(&obligation)
}
fn receiver_for_self_ty<'db>(
@@ -488,7 +491,7 @@ fn receiver_for_self_ty<'db>(
if index == 0 { self_ty.into() } else { mk_param(interner, index, kind) }
});
- EarlyBinder::bind(receiver_ty).instantiate(interner, args)
+ EarlyBinder::bind(receiver_ty).instantiate(interner, args).skip_norm_wip()
}
fn contains_illegal_impl_trait_in_trait<'db>(
@@ -509,11 +512,7 @@ fn contains_illegal_impl_trait_in_trait<'db>(
..
}) = ty.kind()
{
- let id = match def_id {
- SolverDefId::InternedOpaqueTyId(id) => id,
- _ => unreachable!(),
- };
- self.0.insert(id);
+ self.0.insert(def_id.0);
}
ty.super_visit_with(self)
}
@@ -526,7 +525,7 @@ fn contains_illegal_impl_trait_in_trait<'db>(
// Since we haven't implemented RPITIT in proper way like rustc yet,
// just check whether `ret` contains RPIT for now
for opaque_ty in visitor.0 {
- let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty);
+ let impl_trait_id = opaque_ty.loc(db);
if matches!(impl_trait_id, ImplTraitId::ReturnTypeImplTrait(..)) {
return Some(MethodViolationCode::ReferencesImplTraitInTrait);
}
diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs
index 822942eec3..c4321e8a61 100644
--- a/crates/hir-ty/src/generics.rs
+++ b/crates/hir-ty/src/generics.rs
@@ -7,51 +7,56 @@
//! - Type or Const parameters
//!
//! where parent follows the same scheme.
-use std::ops;
+use arrayvec::ArrayVec;
use hir_def::{
ConstParamId, GenericDefId, GenericParamId, ItemContainerId, LifetimeParamId, Lookup,
TypeOrConstParamId, TypeParamId,
db::DefDatabase,
expr_store::ExpressionStore,
hir::generics::{
- GenericParamDataRef, GenericParams, LifetimeParamData, LocalLifetimeParamId,
- LocalTypeOrConstParamId, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
+ GenericParamDataRef, GenericParams, LifetimeParamData, TypeOrConstParamData,
+ TypeParamProvenance, WherePredicate,
},
};
-use itertools::chain;
-pub fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics<'_> {
- let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def)));
+pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics<'_> {
+ let mut chain = ArrayVec::new();
+ let mut parent_params_len = 0;
+ if let Some(parent_def) = parent_generic_def(db, def) {
+ let (parent_params, parent_store) = GenericParams::with_store(db, parent_def);
+ chain.push(SingleGenerics {
+ def: parent_def,
+ params: parent_params,
+ store: parent_store,
+ preceding_params_len: 0,
+ });
+ parent_params_len = parent_params.len() as u32;
+ }
let (params, store) = GenericParams::with_store(db, def);
- let has_trait_self_param = params.trait_self_param().is_some();
- Generics { def, params, parent_generics, has_trait_self_param, store }
+ chain.push(SingleGenerics { def, params, store, preceding_params_len: parent_params_len });
+ Generics { chain }
}
-#[derive(Clone, Debug)]
+
+#[derive(Debug)]
pub struct Generics<'db> {
+ chain: ArrayVec<SingleGenerics<'db>, 2>,
+}
+
+#[derive(Debug)]
+pub(crate) struct SingleGenerics<'db> {
def: GenericDefId,
+ preceding_params_len: u32,
params: &'db GenericParams,
store: &'db ExpressionStore,
- parent_generics: Option<Box<Generics<'db>>>,
- has_trait_self_param: bool,
}
-impl<T> ops::Index<T> for Generics<'_>
-where
- GenericParams: ops::Index<T>,
-{
- type Output = <GenericParams as ops::Index<T>>::Output;
- fn index(&self, index: T) -> &Self::Output {
- &self.params[index]
- }
-}
-
-impl<'db> Generics<'db> {
+impl<'db> SingleGenerics<'db> {
pub(crate) fn def(&self) -> GenericDefId {
self.def
}
- pub(crate) fn store(&self) -> &ExpressionStore {
+ pub(crate) fn store(&self) -> &'db ExpressionStore {
self.store
}
@@ -59,171 +64,249 @@ impl<'db> Generics<'db> {
self.params.where_predicates().iter()
}
- pub(crate) fn is_empty(&self) -> bool {
- self.params.is_empty() && self.parent_generics.as_ref().is_none_or(|g| g.params.is_empty())
+ pub(crate) fn has_no_params(&self) -> bool {
+ self.params.is_empty()
+ }
+
+ pub(crate) fn len_lifetimes(&self) -> usize {
+ self.params.len_lifetimes()
}
- pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
- self.iter_parent_id().chain(self.iter_self_id())
+ pub(crate) fn len(&self) -> usize {
+ self.params.len()
}
- pub(crate) fn iter_self_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
- self.iter_self().map(|(id, _)| id)
+ fn iter_lifetimes(&self) -> impl Iterator<Item = (LifetimeParamId, &'db LifetimeParamData)> {
+ let parent = self.def;
+ self.params
+ .iter_lt()
+ .map(move |(local_id, data)| (LifetimeParamId { parent, local_id }, data))
}
- pub(crate) fn iter_parent_id(&self) -> impl Iterator<Item = GenericParamId> + '_ {
- self.iter_parent().map(|(id, _)| id)
+ pub(crate) fn iter_type_or_consts(
+ &self,
+ ) -> impl Iterator<Item = (TypeOrConstParamId, &'db TypeOrConstParamData)> {
+ let parent = self.def;
+ self.params
+ .iter_type_or_consts()
+ .map(move |(local_id, data)| (TypeOrConstParamId { parent, local_id }, data))
}
- pub(crate) fn iter_self_type_or_consts(
+ fn iter_type_or_consts_as_generic(
&self,
- ) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> + '_
- {
- let mut toc = self.params.iter_type_or_consts();
- let trait_self_param = self.has_trait_self_param.then(|| toc.next()).flatten();
- chain!(trait_self_param, toc)
+ ) -> impl Iterator<Item = (GenericParamId, GenericParamDataRef<'db>)> {
+ self.iter_type_or_consts().map(|(id, data)| match data {
+ TypeOrConstParamData::TypeParamData(data) => (
+ GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
+ GenericParamDataRef::TypeParamData(data),
+ ),
+ TypeOrConstParamData::ConstParamData(data) => (
+ GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
+ GenericParamDataRef::ConstParamData(data),
+ ),
+ })
}
- /// Iterate over the parent params followed by self params.
- pub(crate) fn iter(
+ fn trait_self_and_others(
&self,
- ) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
- self.iter_parent().chain(self.iter_self())
+ ) -> (
+ Option<(GenericParamId, GenericParamDataRef<'db>)>,
+ impl Iterator<Item = (GenericParamId, GenericParamDataRef<'db>)>,
+ ) {
+ let mut iter = self.iter_type_or_consts_as_generic();
+ let trait_self = if let GenericDefId::TraitId(_) = self.def { iter.next() } else { None };
+ (trait_self, iter)
}
- pub(crate) fn iter_parents_with_store(
+ pub(crate) fn iter(&self) -> impl Iterator<Item = (GenericParamId, GenericParamDataRef<'db>)> {
+ let lifetimes = self.iter_lifetimes().map(|(id, data)| {
+ (GenericParamId::LifetimeParamId(id), GenericParamDataRef::LifetimeParamData(data))
+ });
+ let (trait_self, type_and_consts) = self.trait_self_and_others();
+ trait_self.into_iter().chain(lifetimes).chain(type_and_consts)
+ }
+
+ pub(crate) fn iter_with_idx(
&self,
- ) -> impl Iterator<Item = ((GenericParamId, GenericParamDataRef<'_>), &ExpressionStore)> + '_
- {
- self.iter_parent()
- .zip(self.parent_generics().into_iter().flat_map(|it| std::iter::repeat(it.store)))
+ ) -> impl Iterator<Item = (u32, GenericParamId, GenericParamDataRef<'db>)> {
+ std::iter::zip(self.preceding_params_len.., self.iter())
+ .map(|(index, (id, data))| (index, id, data))
+ }
+
+ pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> {
+ self.iter().map(|(id, _)| id)
+ }
+}
+
+impl<'db> Generics<'db> {
+ pub(crate) fn iter_owners(&self) -> impl DoubleEndedIterator<Item = &SingleGenerics<'db>> {
+ self.chain.iter()
+ }
+
+ fn owner(&self) -> &SingleGenerics<'db> {
+ self.chain.last().expect("must have an owner params")
+ }
+
+ pub(crate) fn parent(&self) -> Option<&SingleGenerics<'db>> {
+ match &*self.chain {
+ [parent, _owner] => Some(parent),
+ _ => None,
+ }
+ }
+
+ pub(crate) fn has_no_params(&self) -> bool {
+ self.iter_owners().all(|owner| owner.has_no_params())
+ }
+
+ pub(crate) fn def(&self) -> GenericDefId {
+ self.owner().def
+ }
+
+ pub(crate) fn store(&self) -> &'db ExpressionStore {
+ self.owner().store
}
- /// Iterate over the params without parent params.
pub(crate) fn iter_self(
&self,
- ) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
- let mut toc = self.params.iter_type_or_consts().map(from_toc_id(self));
- let trait_self_param = self.has_trait_self_param.then(|| toc.next()).flatten();
- chain!(trait_self_param, self.params.iter_lt().map(from_lt_id(self)), toc)
+ ) -> impl Iterator<Item = (GenericParamId, GenericParamDataRef<'db>)> {
+ self.owner().iter()
}
- /// Iterator over types and const params of parent.
- pub(crate) fn iter_parent(
+ pub(crate) fn iter_self_with_idx(
&self,
- ) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
- self.parent_generics().into_iter().flat_map(|it| {
- let mut toc = it.params.iter_type_or_consts().map(from_toc_id(it));
- let trait_self_param = it.has_trait_self_param.then(|| toc.next()).flatten();
- chain!(trait_self_param, it.params.iter_lt().map(from_lt_id(it)), toc)
- })
+ ) -> impl Iterator<Item = (u32, GenericParamId, GenericParamDataRef<'db>)> {
+ self.owner().iter_with_idx()
+ }
+
+ pub(crate) fn iter_parent_id(&self) -> impl Iterator<Item = GenericParamId> {
+ self.parent().into_iter().flat_map(|parent| parent.iter_id())
+ }
+
+ pub(crate) fn iter_self_type_or_consts(
+ &self,
+ ) -> impl Iterator<Item = (TypeOrConstParamId, &'db TypeOrConstParamData)> {
+ self.owner().iter_type_or_consts()
+ }
+
+ /// Iterate over the parent params followed by self params.
+ #[cfg(test)]
+ pub(crate) fn iter(&self) -> impl Iterator<Item = (GenericParamId, GenericParamDataRef<'_>)> {
+ self.iter_owners().flat_map(|owner| owner.iter())
+ }
+
+ pub(crate) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> {
+ self.iter_owners().flat_map(|owner| owner.iter_id())
}
/// Returns total number of generic parameters in scope, including those from parent.
pub(crate) fn len(&self) -> usize {
- let parent = self.len_parent();
- let child = self.params.len();
- parent + child
+ match &*self.chain {
+ [parent, owner] => parent.len() + owner.len(),
+ [owner] => owner.len(),
+ _ => unreachable!(),
+ }
}
#[inline]
pub(crate) fn len_parent(&self) -> usize {
- self.parent_generics().map_or(0, Generics::len)
- }
-
- /// Returns numbers of generic parameters excluding those from parent.
- pub(crate) fn len_self(&self) -> usize {
- self.params.len()
+ self.parent().map_or(0, SingleGenerics::len)
}
pub(crate) fn len_lifetimes_self(&self) -> usize {
- self.params.len_lifetimes()
+ self.owner().len_lifetimes()
}
- /// (parent total, self param, type params, const params, impl trait list, lifetimes)
- pub(crate) fn provenance_split(&self) -> (usize, bool, usize, usize, usize, usize) {
- let mut self_param = false;
- let mut type_params = 0;
- let mut impl_trait_params = 0;
+ pub(crate) fn provenance_split(&self) -> ProvenanceSplit {
+ let parent_total = self.len_parent();
+
+ let owner = self.owner();
+ let lifetimes = owner.params.len_lifetimes();
+
+ let mut has_self_param = false;
+ let mut non_impl_trait_type_params = 0;
+ let mut impl_trait_type_params = 0;
let mut const_params = 0;
- self.params.iter_type_or_consts().for_each(|(_, data)| match data {
+ owner.params.iter_type_or_consts().for_each(|(_, data)| match data {
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
- TypeParamProvenance::TypeParamList => type_params += 1,
- TypeParamProvenance::TraitSelf => self_param |= true,
- TypeParamProvenance::ArgumentImplTrait => impl_trait_params += 1,
+ TypeParamProvenance::TypeParamList => non_impl_trait_type_params += 1,
+ TypeParamProvenance::TraitSelf => has_self_param |= true,
+ TypeParamProvenance::ArgumentImplTrait => impl_trait_type_params += 1,
},
TypeOrConstParamData::ConstParamData(_) => const_params += 1,
});
- let lifetime_params = self.params.len_lifetimes();
-
- let parent_len = self.parent_generics().map_or(0, Generics::len);
- (parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params)
- }
-
- pub(crate) fn type_or_const_param(
- &self,
- param: TypeOrConstParamId,
- ) -> Option<(usize, TypeOrConstParamData)> {
- let idx = self.find_type_or_const_param(param)?;
- self.iter().nth(idx).and_then(|p| {
- let data = match p.1 {
- GenericParamDataRef::TypeParamData(p) => p.clone().into(),
- GenericParamDataRef::ConstParamData(p) => p.clone().into(),
- _ => return None,
- };
- Some((idx, data))
- })
+ ProvenanceSplit {
+ parent_total,
+ has_self_param,
+ non_impl_trait_type_params,
+ const_params,
+ impl_trait_type_params,
+ lifetimes,
+ }
}
- pub fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> Option<usize> {
- self.find_type_or_const_param(param)
+ fn find_owner(&self, def: GenericDefId) -> &SingleGenerics<'db> {
+ match &*self.chain {
+ [parent, owner] => {
+ if parent.def == def {
+ parent
+ } else {
+ debug_assert_eq!(def, owner.def);
+ owner
+ }
+ }
+ [owner] => {
+ debug_assert_eq!(def, owner.def);
+ owner
+ }
+ _ => unreachable!(),
+ }
}
- fn find_type_or_const_param(&self, param: TypeOrConstParamId) -> Option<usize> {
- if param.parent == self.def {
- let idx = param.local_id.into_raw().into_u32() as usize;
- debug_assert!(
- idx < self.params.len_type_or_consts(),
- "idx: {} len: {}",
- idx,
- self.params.len_type_or_consts()
- );
- if self.params.trait_self_param() == Some(param.local_id) {
- return Some(idx);
- }
- Some(self.parent_generics().map_or(0, |g| g.len()) + self.params.len_lifetimes() + idx)
+ pub(crate) fn type_or_const_param_idx(&self, param: TypeOrConstParamId) -> u32 {
+ let owner = self.find_owner(param.parent);
+ let has_trait_self = matches!(owner.def, GenericDefId::TraitId(_));
+ if has_trait_self && param.local_id == GenericParams::SELF_PARAM_ID_IN_SELF {
+ owner.preceding_params_len
} else {
- debug_assert_eq!(self.parent_generics().map(|it| it.def), Some(param.parent));
- self.parent_generics().and_then(|g| g.find_type_or_const_param(param))
+ owner.preceding_params_len
+ + owner.len_lifetimes() as u32
+ + param.local_id.into_raw().into_u32()
}
}
- pub fn lifetime_idx(&self, lifetime: LifetimeParamId) -> Option<usize> {
- self.find_lifetime(lifetime)
+ pub(crate) fn lifetime_param_idx(&self, param: LifetimeParamId) -> u32 {
+ let owner = self.find_owner(param.parent);
+ let has_trait_self = matches!(owner.def, GenericDefId::TraitId(_));
+ owner.preceding_params_len
+ + u32::from(has_trait_self)
+ + param.local_id.into_raw().into_u32()
}
- fn find_lifetime(&self, lifetime: LifetimeParamId) -> Option<usize> {
- if lifetime.parent == self.def {
- let idx = lifetime.local_id.into_raw().into_u32() as usize;
- debug_assert!(idx <= self.params.len_lifetimes());
- Some(
- self.parent_generics().map_or(0, |g| g.len())
- + self.params.trait_self_param().is_some() as usize
- + idx,
- )
- } else {
- debug_assert_eq!(self.parent_generics().map(|it| it.def), Some(lifetime.parent));
- self.parent_generics().and_then(|g| g.find_lifetime(lifetime))
- }
+ #[deprecated = "don't use this; it's easy to expose an erroneous `Generics` with this"]
+ pub(crate) fn empty(def: GenericDefId) -> Self {
+ let mut chain = ArrayVec::new();
+ chain.push(SingleGenerics {
+ def,
+ preceding_params_len: 0,
+ params: GenericParams::empty(),
+ store: ExpressionStore::empty(),
+ });
+ Generics { chain }
}
+}
- pub(crate) fn parent_generics(&self) -> Option<&Generics<'db>> {
- self.parent_generics.as_deref()
- }
+pub(crate) struct ProvenanceSplit {
+ pub(crate) parent_total: usize,
+ // The rest are about self.
+ pub(crate) has_self_param: bool,
+ pub(crate) non_impl_trait_type_params: usize,
+ pub(crate) const_params: usize,
+ pub(crate) impl_trait_type_params: usize,
+ pub(crate) lifetimes: usize,
}
-pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> {
+fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<GenericDefId> {
let container = match def {
GenericDefId::FunctionId(it) => it.lookup(db).container,
GenericDefId::TypeAliasId(it) => it.lookup(db).container,
@@ -240,35 +323,3 @@ pub(crate) fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Opt
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None,
}
}
-
-fn from_toc_id<'a>(
- it: &'a Generics<'a>,
-) -> impl Fn(
- (LocalTypeOrConstParamId, &'a TypeOrConstParamData),
-) -> (GenericParamId, GenericParamDataRef<'a>) {
- move |(local_id, p): (_, _)| {
- let id = TypeOrConstParamId { parent: it.def, local_id };
- match p {
- TypeOrConstParamData::TypeParamData(p) => (
- GenericParamId::TypeParamId(TypeParamId::from_unchecked(id)),
- GenericParamDataRef::TypeParamData(p),
- ),
- TypeOrConstParamData::ConstParamData(p) => (
- GenericParamId::ConstParamId(ConstParamId::from_unchecked(id)),
- GenericParamDataRef::ConstParamData(p),
- ),
- }
- }
-}
-
-fn from_lt_id<'a>(
- it: &'a Generics<'a>,
-) -> impl Fn((LocalLifetimeParamId, &'a LifetimeParamData)) -> (GenericParamId, GenericParamDataRef<'a>)
-{
- move |(local_id, p): (_, _)| {
- (
- GenericParamId::LifetimeParamId(LifetimeParamId { parent: it.def, local_id }),
- GenericParamDataRef::LifetimeParamData(p),
- )
- }
-}
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 339ce7933a..39ffb91a8c 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -29,31 +29,39 @@ mod path;
mod place_op;
pub(crate) mod unify;
-use std::{cell::OnceCell, convert::identity, fmt, iter, ops::Deref};
+use std::{
+ cell::{OnceCell, RefCell},
+ convert::identity,
+ fmt,
+ hash::Hash,
+ ops::Deref,
+};
use base_db::{Crate, FxIndexMap};
use either::Either;
use hir_def::{
- AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, ExpressionStoreOwnerId, FieldId,
- FunctionId, GenericDefId, GenericParamId, ItemContainerId, LocalFieldId, Lookup, TraitId,
- TupleFieldId, TupleId, TypeAliasId, TypeOrConstParamId, VariantId,
- expr_store::{Body, ExpressionStore, HygieneId, RootExprOrigin, path::Path},
- hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
+ AdtId, AssocItemId, AttrDefId, ConstId, DefWithBodyId, ExpressionStoreOwnerId, FieldId,
+ FunctionId, GenericDefId, GenericParamId, HasModule, LocalFieldId, Lookup, StaticId, TraitId,
+ TupleFieldId, TupleId, VariantId,
+ attrs::AttrFlags,
+ expr_store::{Body, ExpressionStore, HygieneId, path::Path},
+ hir::{BindingId, ExprId, ExprOrPatId, LabelId, PatId},
lang_item::LangItems,
layout::Integer,
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
signatures::{ConstSignature, EnumSignature, FunctionSignature, StaticSignature},
- type_ref::{ConstRef, LifetimeRefId, TypeRef, TypeRefId},
+ type_ref::{LifetimeRefId, TypeRefId},
+ unstable_features::UnstableFeatures,
};
use hir_expand::{mod_path::ModPath, name::Name};
use indexmap::IndexSet;
-use intern::sym;
use la_arena::ArenaMap;
+use macros::{TypeFoldable, TypeVisitable};
use rustc_ast_ir::Mutability;
use rustc_hash::{FxHashMap, FxHashSet};
use rustc_type_ir::{
- AliasTyKind, TypeFoldable,
- inherent::{AdtDef, IntoKind, Ty as _},
+ AliasTyKind, TypeFoldable, TypeVisitableExt,
+ inherent::{GenericArgs as _, IntoKind, Ty as _},
};
use smallvec::SmallVec;
use span::Edition;
@@ -61,10 +69,12 @@ use stdx::never;
use thin_vec::ThinVec;
use crate::{
- ImplTraitId, IncorrectGenericsLenKind, PathLoweringDiagnostic, TargetFeatures,
+ ImplTraitId, IncorrectGenericsLenKind, InferBodyId, PathLoweringDiagnostic, Span,
+ TargetFeatures,
closure_analysis::PlaceBase,
- collect_type_inference_vars,
- db::{HirDatabase, InternedOpaqueTyId},
+ consteval::{create_anon_const, path_to_const},
+ db::{AnonConstId, GeneralConstId, HirDatabase, InternedOpaqueTyId},
+ generics::Generics,
infer::{
callee::DeferredCallResolution,
closure::analysis::{
@@ -72,19 +82,25 @@ use crate::{
expr_use_visitor::{FakeReadCause, Place},
},
coerce::{CoerceMany, DynamicCoerceMany},
- diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext},
+ diagnostics::{
+ Diagnostics, InferenceTyLoweringContext as TyLoweringContext,
+ InferenceTyLoweringVarsCtx,
+ },
expr::ExprIsRead,
+ pat::PatOrigin,
+ unify::resolve_completely::WriteBackCtxt,
},
lower::{
ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic,
},
- method_resolution::{CandidateId, MethodResolutionUnstableFeatures},
+ method_resolution::CandidateId,
next_solver::{
- AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region,
- StoredGenericArgs, StoredTy, StoredTys, Ty, TyKind, Tys,
+ AliasTy, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region,
+ StoredGenericArg, StoredGenericArgs, StoredTy, StoredTys, Term, Ty, TyKind, Tys,
abi::Safety,
infer::{InferCtxt, ObligationInspector, traits::ObligationCause},
},
+ solver_errors::SolverDiagnostic,
utils::TargetFeatureIsSafeInTarget,
};
@@ -111,17 +127,24 @@ pub fn infer_query_with_inspect<'db>(
let _p = tracing::info_span!("infer_query").entered();
let resolver = def.resolver(db);
let body = Body::of(db, def);
- let mut ctx =
- InferenceContext::new(db, ExpressionStoreOwnerId::Body(def), &body.store, resolver);
+ let mut ctx = InferenceContext::new(
+ db,
+ InferBodyId::DefWithBodyId(def),
+ ExpressionStoreOwnerId::Body(def),
+ def.generic_def(db),
+ &body.store,
+ resolver,
+ true,
+ );
if let Some(inspect) = inspect {
ctx.table.infer_ctxt.attach_obligation_inspector(inspect);
}
match def {
- DefWithBodyId::FunctionId(f) => ctx.collect_fn(f, body.self_param, &body.params),
+ DefWithBodyId::FunctionId(f) => ctx.collect_fn(f, body.self_param(), &body.params),
DefWithBodyId::ConstId(c) => ctx.collect_const(c, ConstSignature::of(db, c)),
- DefWithBodyId::StaticId(s) => ctx.collect_static(StaticSignature::of(db, s)),
+ DefWithBodyId::StaticId(s) => ctx.collect_static(s, StaticSignature::of(db, s)),
DefWithBodyId::VariantId(v) => {
ctx.return_ty = match EnumSignature::variant_body_type(db, v.lookup(db).parent) {
hir_def::layout::IntegerType::Pointer(signed) => match signed {
@@ -162,91 +185,38 @@ fn infer_cycle_result(db: &dyn HirDatabase, _: salsa::Id, _: DefWithBodyId) -> I
}
}
-/// Infer types for all const expressions in an item's signature.
-///
-/// This handles const expressions that appear in type positions within a generic
-/// item's signature, such as array lengths (`[T; N]`) and const generic arguments
-/// (`Foo<{ expr }>`). Each root expression is inferred independently within
-/// a shared `InferenceContext`, accumulating results into a single `InferenceResult`.
-fn infer_signature_query(db: &dyn HirDatabase, def: GenericDefId) -> InferenceResult {
- let _p = tracing::info_span!("infer_signature_query").entered();
- let store = ExpressionStore::of(db, def.into());
- let mut roots = store.expr_roots_with_origins().peekable();
- let Some(_) = roots.peek() else {
- return InferenceResult::new(crate::next_solver::default_types(db).types.error);
- };
-
- let resolver = def.resolver(db);
- let owner = ExpressionStoreOwnerId::Signature(def);
-
- let mut ctx = InferenceContext::new(db, owner, store, resolver);
-
- for (root_expr, origin) in roots {
- let expected = match origin {
- // Array lengths are always `usize`.
- RootExprOrigin::ArrayLength => Expectation::has_type(ctx.types.types.usize),
- // Const parameter default: look up the param's declared type.
- RootExprOrigin::ConstParam(local_id) => Expectation::has_type(db.const_param_ty_ns(
- ConstParamId::from_unchecked(TypeOrConstParamId { parent: def, local_id }),
- )),
- // Path const generic args: determining the expected type requires
- // path resolution.
- // FIXME
- RootExprOrigin::GenericArgsPath => Expectation::None,
- RootExprOrigin::BodyRoot => Expectation::None,
- };
- ctx.infer_expr(root_expr, &expected, ExprIsRead::Yes);
- }
-
- infer_finalize(ctx)
-}
-
-fn infer_variant_fields_query(db: &dyn HirDatabase, def: VariantId) -> InferenceResult {
- let _p = tracing::info_span!("infer_variant_fields_query").entered();
- let store = ExpressionStore::of(db, def.into());
- let mut roots = store.expr_roots_with_origins().peekable();
- let Some(_) = roots.peek() else {
- return InferenceResult::new(crate::next_solver::default_types(db).types.error);
- };
-
- let resolver = def.resolver(db);
- let owner = ExpressionStoreOwnerId::VariantFields(def);
-
- let mut ctx = InferenceContext::new(db, owner, store, resolver);
-
- for (root_expr, origin) in roots {
- let expected = match origin {
- // Array lengths are always `usize`.
- RootExprOrigin::ArrayLength => Expectation::has_type(ctx.types.types.usize),
- // unreachable
- RootExprOrigin::ConstParam(_) => Expectation::None,
- // Path const generic args: determining the expected type requires
- // path resolution.
- // FIXME
- RootExprOrigin::GenericArgsPath => Expectation::None,
- RootExprOrigin::BodyRoot => Expectation::None,
- };
- ctx.infer_expr(root_expr, &expected, ExprIsRead::Yes);
- }
+/// Infer types for an anonymous const expression.
+fn infer_anon_const_query(db: &dyn HirDatabase, def: AnonConstId) -> InferenceResult {
+ let _p = tracing::info_span!("infer_anon_const_query").entered();
+ let loc = def.loc(db);
+ let store_owner = loc.owner;
+ let store = ExpressionStore::of(db, store_owner);
+
+ let resolver = store_owner.resolver(db);
+
+ let mut ctx = InferenceContext::new(
+ db,
+ InferBodyId::AnonConstId(def),
+ store_owner,
+ loc.owner.generic_def(db),
+ store,
+ resolver,
+ loc.allow_using_generic_params,
+ );
+
+ ctx.infer_expr(
+ loc.expr,
+ &Expectation::has_type(loc.ty.get().instantiate_identity().skip_norm_wip()),
+ ExprIsRead::Yes,
+ );
infer_finalize(ctx)
}
-fn infer_signature_cycle_result(
+fn infer_anon_const_cycle_result(
db: &dyn HirDatabase,
_: salsa::Id,
- _: GenericDefId,
-) -> InferenceResult {
- InferenceResult {
- has_errors: true,
- ..InferenceResult::new(Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed))
- }
-}
-
-fn infer_variant_fields_cycle_result(
- db: &dyn HirDatabase,
- _: salsa::Id,
- _: VariantId,
+ _: AnonConstId,
) -> InferenceResult {
InferenceResult {
has_errors: true,
@@ -280,27 +250,25 @@ fn infer_finalize(mut ctx: InferenceContext<'_, '_>) -> InferenceResult {
ctx.handle_opaque_type_uses();
+ ctx.merge_anon_consts();
+
ctx.resolve_all()
}
-/// Binding modes inferred for patterns.
-/// <https://doc.rust-lang.org/reference/patterns.html#binding-modes>
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
-pub enum BindingMode {
- #[default]
- Move,
- Ref(Mutability),
-}
-impl BindingMode {
- fn convert(annotation: BindingAnnotation) -> BindingMode {
- match annotation {
- BindingAnnotation::Unannotated | BindingAnnotation::Mutable => BindingMode::Move,
- BindingAnnotation::Ref => BindingMode::Ref(Mutability::Not),
- BindingAnnotation::RefMut => BindingMode::Ref(Mutability::Mut),
- }
- }
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum ByRef {
+ Yes(Mutability),
+ No,
}
+/// The mode of a binding (`mut`, `ref mut`, etc).
+/// Used for both the explicit binding annotations given in the HIR for a binding
+/// and the final binding mode that we infer after type inference/match ergonomics.
+/// `.0` is the by-reference mode (`ref`, `ref mut`, or by value),
+/// `.1` is the mutability of the binding.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct BindingMode(pub ByRef, pub Mutability);
+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum InferenceTyDiagnosticSource {
/// Diagnostics that come from types in the body.
@@ -309,104 +277,188 @@ pub enum InferenceTyDiagnosticSource {
Signature,
}
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, TypeVisitable, TypeFoldable)]
pub enum InferenceDiagnostic {
NoSuchField {
+ #[type_visitable(ignore)]
field: ExprOrPatId,
+ #[type_visitable(ignore)]
private: Option<LocalFieldId>,
+ #[type_visitable(ignore)]
+ variant: VariantId,
+ },
+ MismatchedArrayPatLen {
+ #[type_visitable(ignore)]
+ pat: PatId,
+ #[type_visitable(ignore)]
+ expected: u128,
+ #[type_visitable(ignore)]
+ found: u128,
+ #[type_visitable(ignore)]
+ has_rest: bool,
+ },
+ ExpectedArrayOrSlicePat {
+ #[type_visitable(ignore)]
+ pat: PatId,
+ found: StoredTy,
+ },
+ DuplicateField {
+ #[type_visitable(ignore)]
+ field: ExprOrPatId,
+ #[type_visitable(ignore)]
variant: VariantId,
},
PrivateField {
+ #[type_visitable(ignore)]
expr: ExprId,
+ #[type_visitable(ignore)]
field: FieldId,
},
PrivateAssocItem {
+ #[type_visitable(ignore)]
id: ExprOrPatId,
+ #[type_visitable(ignore)]
item: AssocItemId,
},
UnresolvedField {
+ #[type_visitable(ignore)]
expr: ExprId,
receiver: StoredTy,
+ #[type_visitable(ignore)]
name: Name,
+ #[type_visitable(ignore)]
method_with_same_name_exists: bool,
},
UnresolvedMethodCall {
+ #[type_visitable(ignore)]
expr: ExprId,
receiver: StoredTy,
+ #[type_visitable(ignore)]
name: Name,
/// Contains the type the field resolves to
field_with_same_name: Option<StoredTy>,
+ #[type_visitable(ignore)]
assoc_func_with_same_name: Option<FunctionId>,
},
UnresolvedAssocItem {
+ #[type_visitable(ignore)]
id: ExprOrPatId,
},
UnresolvedIdent {
+ #[type_visitable(ignore)]
id: ExprOrPatId,
},
// FIXME: This should be emitted in body lowering
BreakOutsideOfLoop {
+ #[type_visitable(ignore)]
expr: ExprId,
+ #[type_visitable(ignore)]
is_break: bool,
+ #[type_visitable(ignore)]
bad_value_break: bool,
},
+ NonExhaustiveRecordExpr {
+ #[type_visitable(ignore)]
+ expr: ExprId,
+ },
+ FunctionalRecordUpdateOnNonStruct {
+ #[type_visitable(ignore)]
+ base_expr: ExprId,
+ },
MismatchedArgCount {
+ #[type_visitable(ignore)]
call_expr: ExprId,
+ #[type_visitable(ignore)]
expected: usize,
+ #[type_visitable(ignore)]
found: usize,
},
MismatchedTupleStructPatArgCount {
- pat: ExprOrPatId,
+ #[type_visitable(ignore)]
+ pat: PatId,
+ #[type_visitable(ignore)]
expected: usize,
+ #[type_visitable(ignore)]
found: usize,
},
ExpectedFunction {
+ #[type_visitable(ignore)]
call_expr: ExprId,
found: StoredTy,
},
TypedHole {
+ #[type_visitable(ignore)]
expr: ExprId,
expected: StoredTy,
},
CastToUnsized {
+ #[type_visitable(ignore)]
expr: ExprId,
cast_ty: StoredTy,
},
InvalidCast {
+ #[type_visitable(ignore)]
expr: ExprId,
+ #[type_visitable(ignore)]
error: CastError,
expr_ty: StoredTy,
cast_ty: StoredTy,
},
TyDiagnostic {
+ #[type_visitable(ignore)]
source: InferenceTyDiagnosticSource,
+ #[type_visitable(ignore)]
diag: TyLoweringDiagnostic,
},
PathDiagnostic {
+ #[type_visitable(ignore)]
node: ExprOrPatId,
+ #[type_visitable(ignore)]
diag: PathLoweringDiagnostic,
},
MethodCallIncorrectGenericsLen {
+ #[type_visitable(ignore)]
expr: ExprId,
+ #[type_visitable(ignore)]
provided_count: u32,
+ #[type_visitable(ignore)]
expected_count: u32,
+ #[type_visitable(ignore)]
kind: IncorrectGenericsLenKind,
+ #[type_visitable(ignore)]
def: GenericDefId,
},
MethodCallIncorrectGenericsOrder {
+ #[type_visitable(ignore)]
expr: ExprId,
+ #[type_visitable(ignore)]
param_id: GenericParamId,
+ #[type_visitable(ignore)]
arg_idx: u32,
/// Whether the `GenericArgs` contains a `Self` arg.
+ #[type_visitable(ignore)]
has_self_arg: bool,
},
-}
-
-/// A mismatch between an expected and an inferred type.
-#[derive(Clone, PartialEq, Eq, Debug, Hash)]
-pub struct TypeMismatch {
- pub expected: StoredTy,
- pub actual: StoredTy,
+ InvalidLhsOfAssignment {
+ #[type_visitable(ignore)]
+ lhs: ExprId,
+ },
+ TypeMustBeKnown {
+ #[type_visitable(ignore)]
+ at_point: Span,
+ top_term: Option<StoredGenericArg>,
+ },
+ UnionExprMustHaveExactlyOneField {
+ #[type_visitable(ignore)]
+ expr: ExprId,
+ },
+ TypeMismatch {
+ #[type_visitable(ignore)]
+ node: ExprOrPatId,
+ expected: StoredTy,
+ found: StoredTy,
+ },
+ SolverDiagnostic(SolverDiagnostic),
}
/// Represents coercing a value to a different type of value.
@@ -575,6 +627,27 @@ pub enum PointerCast {
Unsize,
}
+/// Represents an implicit coercion applied to the scrutinee of a match before testing a pattern
+/// against it. Currently, this is used only for implicit dereferences.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct PatAdjustment {
+ pub kind: PatAdjust,
+ /// The type of the scrutinee before the adjustment is applied, or the "adjusted type" of the
+ /// pattern.
+ pub source: StoredTy,
+}
+
+/// Represents implicit coercions of patterns' types, rather than values' types.
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum PatAdjust {
+ /// An implicit dereference before matching, such as when matching the pattern `0` against a
+ /// scrutinee of type `&u8` or `&mut u8`.
+ BuiltinDeref,
+ /// An implicit call to `Deref(Mut)::deref(_mut)` before matching, such as when matching the
+ /// pattern `[..]` against a scrutinee of type `Vec<T>`.
+ OverloadedDeref,
+}
+
/// 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
@@ -605,7 +678,6 @@ pub struct InferenceResult {
pub(crate) type_of_type_placeholder: FxHashMap<TypeRefId, StoredTy>,
pub(crate) type_of_opaque: FxHashMap<InternedOpaqueTyId, StoredTy>,
- pub(crate) type_mismatches: Option<Box<FxHashMap<ExprOrPatId, TypeMismatch>>>,
/// Whether there are any type-mismatching errors in the result.
// FIXME: This isn't as useful as initially thought due to us falling back placeholders to
// `TyKind::Error`.
@@ -613,6 +685,8 @@ pub struct InferenceResult {
pub(crate) has_errors: bool,
/// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead.
diagnostics: ThinVec<InferenceDiagnostic>,
+ // FIXME: Remove this, change it to be in `InferenceContext`:
+ nodes_with_type_mismatches: Option<Box<FxHashSet<ExprOrPatId>>>,
/// Interned `Error` type to return references to.
// FIXME: Remove this.
@@ -620,7 +694,7 @@ pub struct InferenceResult {
pub(crate) expr_adjustments: FxHashMap<ExprId, Box<[Adjustment]>>,
/// Stores the types which were implicitly dereferenced in pattern binding modes.
- pub(crate) pat_adjustments: FxHashMap<PatId, Vec<StoredTy>>,
+ pub(crate) pat_adjustments: FxHashMap<PatId, Vec<PatAdjustment>>,
/// Stores the binding mode (`ref` in `let ref x = 2`) of bindings.
///
/// This one is tied to the `PatId` instead of `BindingId`, because in some rare cases, a binding in an
@@ -636,9 +710,15 @@ pub struct InferenceResult {
/// the first `rest` has implicit `ref` binding mode, but the second `rest` binding mode is `move`.
pub(crate) binding_modes: ArenaMap<PatId, BindingMode>,
+ /// Set of reference patterns that match against a match-ergonomics inserted reference
+ /// (as opposed to against a reference in the scrutinee type).
+ skipped_ref_pats: FxHashSet<PatId>,
+
pub(crate) coercion_casts: FxHashSet<ExprId>,
pub closures_data: FxHashMap<ExprId, ClosureData>,
+
+ defined_anon_consts: ThinVec<AnonConstId>,
}
#[derive(Clone, PartialEq, Eq, Debug, Default)]
@@ -870,30 +950,21 @@ impl InferenceResult {
/// Returns an `InferenceResult` containing type information for array lengths,
/// const generic arguments, and other const expressions appearing in type
/// positions within the item's signature.
- #[salsa::tracked(returns(ref), cycle_result = infer_signature_cycle_result)]
- fn for_signature(db: &dyn HirDatabase, def: GenericDefId) -> InferenceResult {
- infer_signature_query(db, def)
- }
-
- #[salsa::tracked(returns(ref), cycle_result = infer_variant_fields_cycle_result)]
- fn for_variant_fields(db: &dyn HirDatabase, def: VariantId) -> InferenceResult {
- infer_variant_fields_query(db, def)
+ #[salsa::tracked(returns(ref), cycle_result = infer_anon_const_cycle_result)]
+ fn for_anon_const(db: &dyn HirDatabase, def: AnonConstId) -> InferenceResult {
+ infer_anon_const_query(db, def)
}
-}
-impl InferenceResult {
- pub fn of(db: &dyn HirDatabase, def: impl Into<ExpressionStoreOwnerId>) -> &InferenceResult {
+ #[inline]
+ pub fn of(db: &dyn HirDatabase, def: impl Into<InferBodyId>) -> &InferenceResult {
match def.into() {
- ExpressionStoreOwnerId::Signature(generic_def_id) => {
- Self::for_signature(db, generic_def_id)
- }
- ExpressionStoreOwnerId::Body(def_with_body_id) => Self::for_body(db, def_with_body_id),
- ExpressionStoreOwnerId::VariantFields(variant_id) => {
- Self::for_variant_fields(db, variant_id)
- }
+ InferBodyId::DefWithBodyId(it) => InferenceResult::for_body(db, it),
+ InferBodyId::AnonConstId(it) => InferenceResult::for_anon_const(db, it),
}
}
+}
+impl InferenceResult {
fn new(error_ty: Ty<'_>) -> Self {
Self {
method_resolutions: Default::default(),
@@ -902,12 +973,13 @@ impl InferenceResult {
assoc_resolutions: Default::default(),
tuple_field_access_types: Default::default(),
diagnostics: Default::default(),
+ nodes_with_type_mismatches: Default::default(),
type_of_expr: Default::default(),
type_of_pat: Default::default(),
type_of_binding: Default::default(),
type_of_type_placeholder: Default::default(),
type_of_opaque: Default::default(),
- type_mismatches: Default::default(),
+ skipped_ref_pats: Default::default(),
has_errors: Default::default(),
error_ty: error_ty.store(),
pat_adjustments: Default::default(),
@@ -915,6 +987,7 @@ impl InferenceResult {
expr_adjustments: Default::default(),
coercion_casts: Default::default(),
closures_data: Default::default(),
+ defined_anon_consts: Default::default(),
}
}
@@ -957,26 +1030,22 @@ impl InferenceResult {
ExprOrPatId::PatId(id) => self.assoc_resolutions_for_pat(id),
}
}
- pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> {
- self.type_mismatches.as_deref()?.get(&expr.into())
+ pub fn expr_or_pat_has_type_mismatch(&self, node: ExprOrPatId) -> bool {
+ self.nodes_with_type_mismatches.as_ref().is_some_and(|it| it.contains(&node))
}
- pub fn type_mismatch_for_pat(&self, pat: PatId) -> Option<&TypeMismatch> {
- self.type_mismatches.as_deref()?.get(&pat.into())
+ pub fn expr_has_type_mismatch(&self, expr: ExprId) -> bool {
+ self.expr_or_pat_has_type_mismatch(expr.into())
}
- pub fn type_mismatches(&self) -> impl Iterator<Item = (ExprOrPatId, &TypeMismatch)> {
- self.type_mismatches
- .as_deref()
- .into_iter()
- .flatten()
- .map(|(expr_or_pat, mismatch)| (*expr_or_pat, mismatch))
- }
- pub fn expr_type_mismatches(&self) -> impl Iterator<Item = (ExprId, &TypeMismatch)> {
- self.type_mismatches.as_deref().into_iter().flatten().filter_map(
- |(expr_or_pat, mismatch)| match *expr_or_pat {
- ExprOrPatId::ExprId(expr) => Some((expr, mismatch)),
- _ => None,
- },
- )
+ pub fn pat_has_type_mismatch(&self, pat: PatId) -> bool {
+ self.expr_or_pat_has_type_mismatch(pat.into())
+ }
+ pub fn exprs_have_type_mismatches(&self) -> bool {
+ self.nodes_with_type_mismatches
+ .as_ref()
+ .is_some_and(|it| it.iter().any(|node| node.is_expr()))
+ }
+ pub fn has_type_mismatches(&self) -> bool {
+ self.nodes_with_type_mismatches.is_some()
}
pub fn placeholder_types<'db>(&self) -> impl Iterator<Item = (TypeRefId, Ty<'db>)> {
self.type_of_type_placeholder.iter().map(|(&type_ref, ty)| (type_ref, ty.as_ref()))
@@ -1007,10 +1076,10 @@ impl InferenceResult {
None => self.type_of_expr.get(id).map(|it| it.as_ref()),
}
}
- pub fn type_of_pat_with_adjust<'db>(&self, id: PatId) -> Option<Ty<'db>> {
+ pub fn type_of_pat_with_adjust<'db>(&self, id: PatId) -> Ty<'db> {
match self.pat_adjustments.get(&id).and_then(|adjustments| adjustments.last()) {
- Some(adjusted) => Some(adjusted.as_ref()),
- None => self.type_of_pat.get(id).map(|it| it.as_ref()),
+ Some(adjusted) => adjusted.source.as_ref(),
+ None => self.pat_ty(id),
}
}
pub fn is_erroneous(&self) -> bool {
@@ -1025,7 +1094,7 @@ impl InferenceResult {
self.tuple_field_access_types[id.0 as usize].as_ref()
}
- pub fn pat_adjustment(&self, id: PatId) -> Option<&[StoredTy]> {
+ pub fn pat_adjustment(&self, id: PatId) -> Option<&[PatAdjustment]> {
self.pat_adjustments.get(&id).map(|it| &**it)
}
@@ -1100,23 +1169,37 @@ impl InferenceResult {
.values()
.flat_map(|captures| captures.iter().map(|capture| capture.captured_ty(db)))
}
+
+ pub fn is_skipped_ref_pat(&self, pat: PatId) -> bool {
+ self.skipped_ref_pats.contains(&pat)
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum DerefPatBorrowMode {
+ Borrow(Mutability),
+ Box,
}
/// The inference context contains all information needed during type inference.
-#[derive(Clone, Debug)]
+#[derive(Debug)]
pub(crate) struct InferenceContext<'body, 'db> {
pub(crate) db: &'db dyn HirDatabase,
- pub(crate) owner: ExpressionStoreOwnerId,
+ pub(crate) owner: InferBodyId,
+ pub(crate) store_owner: ExpressionStoreOwnerId,
+ pub(crate) generic_def: GenericDefId,
pub(crate) store: &'body ExpressionStore,
/// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext
/// and resolve the path via its methods. This will ensure proper error reporting.
pub(crate) resolver: Resolver<'db>,
target_features: OnceCell<(TargetFeatures<'db>, TargetFeatureIsSafeInTarget)>,
- pub(crate) unstable_features: MethodResolutionUnstableFeatures,
pub(crate) edition: Edition,
- pub(crate) generic_def: GenericDefId,
+ allow_using_generic_params: bool,
+ generics: OnceCell<Generics<'db>>,
+ identity_args: OnceCell<GenericArgs<'db>>,
pub(crate) table: unify::InferenceTable<'db>,
pub(crate) lang_items: &'db LangItems,
+ pub(crate) features: &'db UnstableFeatures,
/// The traits in scope, disregarding block modules. This is used for caching purposes.
traits_in_scope: FxHashSet<TraitId>,
pub(crate) result: InferenceResult,
@@ -1147,6 +1230,9 @@ pub(crate) struct InferenceContext<'body, 'db> {
deferred_call_resolutions: FxHashMap<ExprId, Vec<DeferredCallResolution<'db>>>,
diagnostics: Diagnostics,
+ vars_emitted_type_must_be_known_for: FxHashSet<Term<'db>>,
+
+ defined_anon_consts: RefCell<ThinVec<AnonConstId>>,
}
#[derive(Clone, Debug)]
@@ -1196,32 +1282,23 @@ fn find_continuable<'a, 'db>(
impl<'body, 'db> InferenceContext<'body, 'db> {
fn new(
db: &'db dyn HirDatabase,
- owner: ExpressionStoreOwnerId,
+ owner: InferBodyId,
+ store_owner: ExpressionStoreOwnerId,
+ generic_def: GenericDefId,
store: &'body ExpressionStore,
resolver: Resolver<'db>,
+ allow_using_generic_params: bool,
) -> Self {
- let trait_env = match owner {
- ExpressionStoreOwnerId::Signature(generic_def_id) => {
- db.trait_environment(ExpressionStoreOwnerId::from(generic_def_id))
- }
- ExpressionStoreOwnerId::Body(def_with_body_id) => {
- db.trait_environment(ExpressionStoreOwnerId::Body(def_with_body_id))
- }
- ExpressionStoreOwnerId::VariantFields(variant_id) => {
- db.trait_environment(ExpressionStoreOwnerId::VariantFields(variant_id))
- }
- };
- let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), Some(owner));
+ let trait_env = db.trait_environment(store_owner);
+ let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), store_owner);
let types = crate::next_solver::default_types(db);
InferenceContext {
result: InferenceResult::new(types.types.error),
return_ty: types.types.error, // set in collect_* calls
types,
target_features: OnceCell::new(),
- unstable_features: MethodResolutionUnstableFeatures::from_def_map(
- resolver.top_level_def_map(),
- ),
lang_items: table.interner().lang_items(),
+ features: resolver.top_level_def_map().features(),
edition: resolver.krate().data(db).edition,
table,
tuple_field_accesses_rev: Default::default(),
@@ -1229,7 +1306,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
return_coercion: None,
db,
owner,
- generic_def: owner.generic_def(db),
+ store_owner,
+ generic_def,
+ allow_using_generic_params,
+ generics: OnceCell::new(),
+ identity_args: OnceCell::new(),
store,
traits_in_scope: resolver.traits_in_scope(db),
resolver,
@@ -1238,7 +1319,99 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
deferred_cast_checks: Vec::new(),
inside_assignment: false,
diagnostics: Diagnostics::default(),
+ vars_emitted_type_must_be_known_for: FxHashSet::default(),
deferred_call_resolutions: FxHashMap::default(),
+ defined_anon_consts: RefCell::new(ThinVec::new()),
+ }
+ }
+
+ fn merge(&mut self, other: &InferenceResult) {
+ let InferenceResult {
+ method_resolutions,
+ field_resolutions,
+ variant_resolutions,
+ assoc_resolutions,
+ tuple_field_access_types: _,
+ type_of_expr,
+ type_of_pat,
+ type_of_binding,
+ type_of_type_placeholder,
+ type_of_opaque,
+ has_errors: _,
+ diagnostics: _,
+ error_ty: _,
+ expr_adjustments,
+ pat_adjustments,
+ binding_modes,
+ skipped_ref_pats,
+ coercion_casts,
+ closures_data,
+ nodes_with_type_mismatches,
+ defined_anon_consts: _,
+ } = &mut self.result;
+ merge_hash_maps(method_resolutions, &other.method_resolutions);
+ merge_hash_maps(variant_resolutions, &other.variant_resolutions);
+ merge_hash_maps(assoc_resolutions, &other.assoc_resolutions);
+ field_resolutions.extend(other.field_resolutions.iter().map(
+ |(&field_expr, &field_resolution)| {
+ let mut field_resolution = field_resolution;
+ if let Either::Right(tuple_field) = &mut field_resolution {
+ let tys = other.tuple_field_access_type(tuple_field.tuple);
+ tuple_field.tuple =
+ TupleId(self.tuple_field_accesses_rev.insert_full(tys).0 as u32);
+ };
+ (field_expr, field_resolution)
+ },
+ ));
+ merge_arena_maps(type_of_expr, &other.type_of_expr);
+ merge_arena_maps(type_of_pat, &other.type_of_pat);
+ merge_arena_maps(type_of_binding, &other.type_of_binding);
+ merge_hash_maps(type_of_type_placeholder, &other.type_of_type_placeholder);
+ merge_hash_maps(type_of_opaque, &other.type_of_opaque);
+ merge_hash_maps(expr_adjustments, &other.expr_adjustments);
+ merge_hash_maps(pat_adjustments, &other.pat_adjustments);
+ merge_arena_maps(binding_modes, &other.binding_modes);
+ merge_hash_set(skipped_ref_pats, &other.skipped_ref_pats);
+ merge_hash_set(coercion_casts, &other.coercion_casts);
+ merge_hash_maps(closures_data, &other.closures_data);
+ if let Some(other_nodes_with_type_mismatches) = &other.nodes_with_type_mismatches {
+ merge_hash_set(
+ nodes_with_type_mismatches.get_or_insert_default(),
+ other_nodes_with_type_mismatches,
+ );
+ }
+ self.defined_anon_consts.borrow_mut().extend(other.defined_anon_consts.iter().copied());
+
+ fn merge_hash_set<T: Hash + Eq + Clone>(dest: &mut FxHashSet<T>, source: &FxHashSet<T>) {
+ dest.extend(source.iter().cloned());
+ }
+
+ #[cfg_attr(debug_assertions, track_caller)]
+ fn merge_hash_maps<K: Hash + Eq + Clone, V: Clone + PartialEq>(
+ dest: &mut FxHashMap<K, V>,
+ source: &FxHashMap<K, V>,
+ ) {
+ if cfg!(debug_assertions) {
+ for (key, src) in source {
+ assert!(dest.get(key).is_none_or(|dst| dst == src));
+ }
+ }
+
+ dest.extend(source.iter().map(|(k, v)| (k.clone(), v.clone())));
+ }
+
+ #[cfg_attr(debug_assertions, track_caller)]
+ fn merge_arena_maps<K, V: Clone + PartialEq>(
+ dest: &mut ArenaMap<la_arena::Idx<K>, V>,
+ source: &ArenaMap<la_arena::Idx<K>, V>,
+ ) {
+ if cfg!(debug_assertions) {
+ for (key, src) in source.iter() {
+ assert!(dest.get(key).is_none_or(|dst| dst == src));
+ }
+ }
+
+ dest.extend(source.iter().map(|(k, v)| (k, v.clone())));
}
}
@@ -1249,7 +1422,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
fn target_features(&self) -> (&TargetFeatures<'db>, TargetFeatureIsSafeInTarget) {
let (target_features, target_feature_is_safe) = self.target_features.get_or_init(|| {
- let target_features = match self.owner {
+ let target_features = match self.store_owner {
ExpressionStoreOwnerId::Body(DefWithBodyId::FunctionId(id)) => {
TargetFeatures::from_fn(self.db, id)
}
@@ -1264,31 +1437,43 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
(target_features, *target_feature_is_safe)
}
+ /// How should a deref pattern find the place for its inner pattern to match on?
+ ///
+ /// In most cases, if the pattern recursively contains a `ref mut` binding, we find the inner
+ /// pattern's scrutinee by calling `DerefMut::deref_mut`, and otherwise we call `Deref::deref`.
+ /// However, for boxes we can use a built-in deref instead, which doesn't borrow the scrutinee;
+ /// in this case, we return `DerefPatBorrowMode::Box`.
+ fn deref_pat_borrow_mode(&self, pointer_ty: Ty<'_>, inner: PatId) -> DerefPatBorrowMode {
+ if pointer_ty.is_box() {
+ DerefPatBorrowMode::Box
+ } else {
+ let mutability =
+ if self.pat_has_ref_mut_binding(inner) { Mutability::Mut } else { Mutability::Not };
+ DerefPatBorrowMode::Borrow(mutability)
+ }
+ }
+
#[inline]
fn set_tainted_by_errors(&mut self) {
self.result.has_errors = true;
}
- /// Clones `self` and calls `resolve_all()` on it.
- // FIXME: Remove this.
- pub(crate) fn fixme_resolve_all_clone(&self) -> InferenceResult {
- let mut ctx = self.clone();
-
- ctx.type_inference_fallback();
-
- // Comment from rustc:
- // Even though coercion casts provide type hints, we check casts after fallback for
- // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
- let cast_checks = std::mem::take(&mut ctx.deferred_cast_checks);
- for mut cast in cast_checks.into_iter() {
- if let Err(diag) = cast.check(&mut ctx) {
- ctx.diagnostics.push(diag);
+ /// Copy the inference of defined anon consts to ourselves, so that we don't need to lookup the defining
+ /// anon const when looking the type of something.
+ fn merge_anon_consts(&mut self) {
+ let mut defined_anon_consts = std::mem::take(&mut *self.defined_anon_consts.borrow_mut());
+ defined_anon_consts.retain(|&konst| {
+ if konst.loc(self.db).owner != self.store_owner {
+ // This comes from the signature, we don't define it.
+ return false;
}
- }
-
- ctx.table.select_obligations_where_possible();
- ctx.resolve_all()
+ let const_infer = InferenceResult::of(self.db, konst);
+ self.merge(const_infer);
+ true
+ });
+ // Caution, other defined anon consts might have been added by `merge()`!
+ self.defined_anon_consts.borrow_mut().append(&mut defined_anon_consts);
}
// FIXME: This function should be private in module. It is currently only used in the consteval, since we need
@@ -1297,14 +1482,15 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
// there is no problem in it being `pub(crate)`, remove this comment.
fn resolve_all(self) -> InferenceResult {
let InferenceContext {
- mut table,
+ table,
mut result,
tuple_field_accesses_rev,
diagnostics,
types,
+ vars_emitted_type_must_be_known_for,
..
} = self;
- let mut diagnostics = diagnostics.finish();
+ let diagnostics = diagnostics.finish();
// Destructure every single field so whenever new fields are added to `InferenceResult` we
// don't forget to handle them here.
let InferenceResult {
@@ -1317,97 +1503,66 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
type_of_binding,
type_of_type_placeholder,
type_of_opaque,
- type_mismatches,
+ skipped_ref_pats,
closures_data,
has_errors,
error_ty: _,
pat_adjustments,
binding_modes: _,
expr_adjustments,
- tuple_field_access_types: _,
+ tuple_field_access_types,
coercion_casts: _,
- diagnostics: _,
+ diagnostics: result_diagnostics,
+ nodes_with_type_mismatches,
+ defined_anon_consts: result_defined_anon_consts,
} = &mut result;
+ *result_defined_anon_consts = self.defined_anon_consts.into_inner();
+ result_defined_anon_consts.shrink_to_fit();
+
+ let mut resolver =
+ WriteBackCtxt::new(table, diagnostics, vars_emitted_type_must_be_known_for);
+
+ skipped_ref_pats.shrink_to_fit();
for ty in type_of_expr.values_mut() {
- *ty = table.resolve_completely(ty.as_ref()).store();
- *has_errors = *has_errors || ty.as_ref().references_non_lt_error();
+ resolver.resolve_completely(ty);
}
type_of_expr.shrink_to_fit();
for ty in type_of_pat.values_mut() {
- *ty = table.resolve_completely(ty.as_ref()).store();
- *has_errors = *has_errors || ty.as_ref().references_non_lt_error();
+ resolver.resolve_completely(ty);
}
type_of_pat.shrink_to_fit();
for ty in type_of_binding.values_mut() {
- *ty = table.resolve_completely(ty.as_ref()).store();
- *has_errors = *has_errors || ty.as_ref().references_non_lt_error();
+ resolver.resolve_completely(ty);
}
type_of_binding.shrink_to_fit();
for ty in type_of_type_placeholder.values_mut() {
- *ty = table.resolve_completely(ty.as_ref()).store();
- *has_errors = *has_errors || ty.as_ref().references_non_lt_error();
+ resolver.resolve_completely(ty);
}
type_of_type_placeholder.shrink_to_fit();
type_of_opaque.shrink_to_fit();
- if let Some(type_mismatches) = type_mismatches {
+ if let Some(nodes_with_type_mismatches) = nodes_with_type_mismatches {
*has_errors = true;
- for mismatch in type_mismatches.values_mut() {
- mismatch.expected = table.resolve_completely(mismatch.expected.as_ref()).store();
- mismatch.actual = table.resolve_completely(mismatch.actual.as_ref()).store();
- }
- type_mismatches.shrink_to_fit();
+ nodes_with_type_mismatches.shrink_to_fit();
}
- diagnostics.retain_mut(|diagnostic| {
- use InferenceDiagnostic::*;
- match diagnostic {
- ExpectedFunction { found: ty, .. }
- | UnresolvedField { receiver: ty, .. }
- | UnresolvedMethodCall { receiver: ty, .. } => {
- *ty = table.resolve_completely(ty.as_ref()).store();
- // FIXME: Remove this when we are on par with rustc in terms of inference
- if ty.as_ref().references_non_lt_error() {
- return false;
- }
-
- if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic
- && let Some(ty) = field_with_same_name
- {
- *ty = table.resolve_completely(ty.as_ref()).store();
- if ty.as_ref().references_non_lt_error() {
- *field_with_same_name = None;
- }
- }
- }
- TypedHole { expected: ty, .. } => {
- *ty = table.resolve_completely(ty.as_ref()).store();
- }
- _ => (),
- }
- true
- });
- diagnostics.shrink_to_fit();
for (_, subst) in method_resolutions.values_mut() {
- *subst = table.resolve_completely(subst.as_ref()).store();
- *has_errors =
- *has_errors || subst.as_ref().types().any(|ty| ty.references_non_lt_error());
+ resolver.resolve_completely(subst);
}
method_resolutions.shrink_to_fit();
for (_, subst) in assoc_resolutions.values_mut() {
- *subst = table.resolve_completely(subst.as_ref()).store();
- *has_errors =
- *has_errors || subst.as_ref().types().any(|ty| ty.references_non_lt_error());
+ resolver.resolve_completely(subst);
}
assoc_resolutions.shrink_to_fit();
for adjustment in expr_adjustments.values_mut().flatten() {
- adjustment.target = table.resolve_completely(adjustment.target.as_ref()).store();
- *has_errors = *has_errors || adjustment.target.as_ref().references_non_lt_error();
+ resolver.resolve_completely(&mut adjustment.target);
}
expr_adjustments.shrink_to_fit();
- for adjustment in pat_adjustments.values_mut().flatten() {
- *adjustment = table.resolve_completely(adjustment.as_ref()).store();
- *has_errors = *has_errors || adjustment.as_ref().references_non_lt_error();
+ for adjustments in pat_adjustments.values_mut() {
+ for adjustment in &mut *adjustments {
+ resolver.resolve_completely(&mut adjustment.source);
+ }
+ adjustments.shrink_to_fit();
}
pat_adjustments.shrink_to_fit();
for closure_data in closures_data.values_mut() {
@@ -1419,7 +1574,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
};
for (place, _, sources) in fake_reads {
- *place = table.resolve_completely(std::mem::replace(place, dummy_place()));
+ resolver.resolve_completely_with_default(place, dummy_place());
place.projections.shrink_to_fit();
for source in &mut *sources {
source.shrink_to_fit();
@@ -1430,7 +1585,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
for min_capture in min_captures.values_mut() {
for captured in &mut *min_capture {
let CapturedPlace { place, info, mutability: _ } = captured;
- *place = table.resolve_completely(std::mem::replace(place, dummy_place()));
+ resolver.resolve_completely_with_default(place, dummy_place());
let CaptureInfo { sources, capture_kind: _ } = info;
for source in &mut *sources {
source.shrink_to_fit();
@@ -1442,17 +1597,18 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
min_captures.shrink_to_fit();
}
closures_data.shrink_to_fit();
- result.tuple_field_access_types = tuple_field_accesses_rev
+ *tuple_field_access_types = tuple_field_accesses_rev
.into_iter()
- .map(|subst| table.resolve_completely(subst).store())
- .inspect(|subst| {
- *has_errors =
- *has_errors || subst.as_ref().iter().any(|ty| ty.references_non_lt_error());
+ .map(|mut subst| {
+ resolver.resolve_completely(&mut subst);
+ subst.store()
})
.collect();
- result.tuple_field_access_types.shrink_to_fit();
+ tuple_field_access_types.shrink_to_fit();
- result.diagnostics = diagnostics;
+ let (diagnostics, resolver_has_errors) = resolver.resolve_diagnostics();
+ *result_diagnostics = diagnostics;
+ *has_errors |= resolver_has_errors;
result
}
@@ -1462,17 +1618,19 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
data.type_ref,
&data.store,
InferenceTyDiagnosticSource::Signature,
+ ExpressionStoreOwnerId::Signature(id.into()),
LifetimeElisionKind::for_const(self.interner(), id.loc(self.db).container),
);
self.return_ty = return_ty;
}
- fn collect_static(&mut self, data: &StaticSignature) {
+ fn collect_static(&mut self, id: StaticId, data: &StaticSignature) {
let return_ty = self.make_ty(
data.type_ref,
&data.store,
InferenceTyDiagnosticSource::Signature,
+ ExpressionStoreOwnerId::Signature(id.into()),
LifetimeElisionKind::Elided(self.types.regions.statik),
);
@@ -1484,6 +1642,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
let mut param_tys = self.with_ty_lowering(
&data.store,
InferenceTyDiagnosticSource::Signature,
+ ExpressionStoreOwnerId::Signature(func.into()),
LifetimeElisionKind::for_fn_params(data),
|ctx| data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>(),
);
@@ -1498,7 +1657,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
GenericArgs::for_item_with_defaults(
self.interner(),
va_list.into(),
- |_, id, _| self.table.next_var_for_param(id),
+ |_, id, _| self.table.var_for_def(id, Span::Dummy),
),
),
None => self.err_ty(),
@@ -1506,23 +1665,25 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
param_tys.push(va_list_ty);
}
- let mut param_tys = param_tys.into_iter().chain(iter::repeat(self.table.next_ty_var()));
+ let mut param_tys = param_tys.into_iter();
if let Some(self_param) = self_param
&& let Some(ty) = param_tys.next()
{
let ty = self.process_user_written_ty(ty);
self.write_binding_ty(self_param, ty);
}
- for (ty, pat) in param_tys.zip(params) {
+ for pat in params {
+ let ty = param_tys.next().unwrap_or_else(|| self.table.next_ty_var(Span::Dummy));
let ty = self.process_user_written_ty(ty);
- self.infer_top_pat(*pat, ty, None);
+ self.infer_top_pat(*pat, ty, PatOrigin::Param);
}
self.return_ty = match data.ret_type {
Some(return_ty) => {
let return_ty = self.with_ty_lowering(
&data.store,
InferenceTyDiagnosticSource::Signature,
+ ExpressionStoreOwnerId::Signature(func.into()),
LifetimeElisionKind::for_fn_ret(self.interner()),
|ctx| {
ctx.impl_trait_mode(ImplTraitLoweringMode::Opaque);
@@ -1547,6 +1708,33 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
&self.table.infer_ctxt
}
+ /// If `ty` is an error, returns an infer var instead. Otherwise, returns it.
+ ///
+ /// "Refreshing" types like this is useful for getting better types, but it is also
+ /// very dangerous: we might create duplicate diagnostics, for example if we try
+ /// to resolve it and fail. rustc doesn't do that for this reason (and is in general
+ /// more strict with how it uses error types; an error type in inputs will almost
+ /// always cause it to infer an error type in output, while we infer some type as much
+ /// as we can).
+ ///
+ /// Unfortunately, we cannot allow ourselves to do that. Not only we more often work
+ /// with incomplete code, we also have assists, for example "Generate constant", that
+ /// will assume the inferred type is the expected type even if the expression itself
+ /// cannot be inferred. Therefore, we choose a middle ground: refresh the type,
+ /// but if we return a new var, mark it so that no diagnostics will be issued on it.
+ fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> {
+ if ty.is_ty_error() {
+ let var = self.table.next_ty_var(Span::Dummy);
+
+ // Suppress future errors on this var. Add more things here when we add more diagnostics.
+ self.vars_emitted_type_must_be_known_for.insert(var.into());
+
+ var
+ } else {
+ ty
+ }
+ }
+
fn infer_body(&mut self, body_expr: ExprId) {
match self.return_coercion {
Some(_) => self.infer_return(body_expr),
@@ -1590,13 +1778,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
}
}
- fn write_pat_adj(&mut self, pat: PatId, adjustments: Box<[StoredTy]>) {
- if adjustments.is_empty() {
- return;
- }
- self.result.pat_adjustments.entry(pat).or_default().extend(adjustments);
- }
-
pub(crate) fn write_method_resolution(
&mut self,
expr: ExprId,
@@ -1623,10 +1804,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.result.type_of_pat.insert(pat, ty.store());
}
- fn write_type_placeholder_ty(&mut self, type_ref: TypeRefId, ty: Ty<'db>) {
- self.result.type_of_type_placeholder.insert(type_ref, ty.store());
- }
-
fn write_binding_ty(&mut self, id: BindingId, ty: Ty<'db>) {
self.result.type_of_binding.insert(id, ty.store());
}
@@ -1654,17 +1831,30 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
&mut self,
store: &ExpressionStore,
types_source: InferenceTyDiagnosticSource,
+ store_owner: ExpressionStoreOwnerId,
lifetime_elision: LifetimeElisionKind<'db>,
f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> R,
) -> R {
+ let infer_vars = match types_source {
+ InferenceTyDiagnosticSource::Body => Some(&mut InferenceTyLoweringVarsCtx {
+ table: &mut self.table,
+ type_of_type_placeholder: &mut self.result.type_of_type_placeholder,
+ } as _),
+ InferenceTyDiagnosticSource::Signature => None,
+ };
let mut ctx = TyLoweringContext::new(
self.db,
&self.resolver,
store,
&self.diagnostics,
types_source,
+ store_owner,
self.generic_def,
+ &self.generics,
lifetime_elision,
+ self.allow_using_generic_params,
+ infer_vars,
+ &self.defined_anon_consts,
);
f(&mut ctx)
}
@@ -1676,6 +1866,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.with_ty_lowering(
self.store,
InferenceTyDiagnosticSource::Body,
+ self.store_owner,
LifetimeElisionKind::Infer,
f,
)
@@ -1686,29 +1877,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
type_ref: TypeRefId,
store: &ExpressionStore,
type_source: InferenceTyDiagnosticSource,
+ store_owner: ExpressionStoreOwnerId,
lifetime_elision: LifetimeElisionKind<'db>,
) -> Ty<'db> {
- let ty = self
- .with_ty_lowering(store, type_source, lifetime_elision, |ctx| ctx.lower_ty(type_ref));
- let ty = self.process_user_written_ty(ty);
-
- // Record the association from placeholders' TypeRefId to type variables.
- // We only record them if their number matches. This assumes TypeRef::walk and TypeVisitable process the items in the same order.
- let type_variables = collect_type_inference_vars(&ty);
- let mut placeholder_ids = vec![];
- TypeRef::walk(type_ref, store, &mut |type_ref_id, type_ref| {
- if matches!(type_ref, TypeRef::Placeholder) {
- placeholder_ids.push(type_ref_id);
- }
+ let ty = self.with_ty_lowering(store, type_source, store_owner, lifetime_elision, |ctx| {
+ ctx.lower_ty(type_ref)
});
-
- if placeholder_ids.len() == type_variables.len() {
- for (placeholder_id, type_variable) in placeholder_ids.into_iter().zip(type_variables) {
- self.write_type_placeholder_ty(placeholder_id, type_variable);
- }
- }
-
- ty
+ self.process_user_written_ty(ty)
}
pub(crate) fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> {
@@ -1716,28 +1891,57 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
type_ref,
self.store,
InferenceTyDiagnosticSource::Body,
+ self.store_owner,
LifetimeElisionKind::Infer,
)
}
- pub(crate) fn make_body_const(&mut self, const_ref: ConstRef, ty: Ty<'db>) -> Const<'db> {
- let const_ = self.with_ty_lowering(
- self.store,
- InferenceTyDiagnosticSource::Body,
- LifetimeElisionKind::Infer,
- |ctx| ctx.lower_const(const_ref, ty),
- );
- self.insert_type_vars(const_)
+ fn generics(&self) -> &Generics<'db> {
+ self.generics.get_or_init(|| crate::generics::generics(self.db, self.generic_def))
+ }
+
+ fn identity_args(&self) -> GenericArgs<'db> {
+ *self.identity_args.get_or_init(|| {
+ GenericArgs::identity_for_item(self.interner(), self.store_owner.into())
+ })
}
- pub(crate) fn make_path_as_body_const(&mut self, path: &Path, ty: Ty<'db>) -> Const<'db> {
- let const_ = self.with_ty_lowering(
+ pub(crate) fn create_body_anon_const(
+ &mut self,
+ expr: ExprId,
+ expected_ty: Ty<'db>,
+ allow_using_generic_params: bool,
+ ) -> Const<'db> {
+ never!(expected_ty.has_infer(), "cannot have infer vars in an anon const's ty");
+ let konst = create_anon_const(
+ self.interner(),
+ self.store_owner,
self.store,
- InferenceTyDiagnosticSource::Body,
- LifetimeElisionKind::Infer,
- |ctx| ctx.lower_path_as_const(path, ty),
+ expr,
+ &self.resolver,
+ expected_ty,
+ &|| self.generics(),
+ Some(&mut |span| self.table.next_const_var(span)),
+ (!(allow_using_generic_params && self.allow_using_generic_params)).then_some(0),
);
- self.insert_type_vars(const_)
+
+ if let Ok(konst) = konst
+ && let ConstKind::Unevaluated(konst) = konst.kind()
+ && let GeneralConstId::AnonConstId(konst) = konst.def.0
+ {
+ self.defined_anon_consts.borrow_mut().push(konst);
+ }
+
+ self.write_expr_ty(expr, expected_ty);
+ // FIXME: Report an error if needed.
+ konst.unwrap_or_else(|_| self.table.next_const_var(Span::Dummy))
+ }
+
+ pub(crate) fn make_path_as_body_const(&mut self, path: &Path) -> Const<'db> {
+ let forbid_params_after = if self.allow_using_generic_params { None } else { Some(0) };
+ // FIXME: Report errors.
+ path_to_const(self.db, &self.resolver, &|| self.generics(), forbid_params_after, path)
+ .unwrap_or_else(|_| self.table.next_const_var(Span::Dummy))
}
fn err_ty(&self) -> Ty<'db> {
@@ -1748,17 +1952,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
let lt = self.with_ty_lowering(
self.store,
InferenceTyDiagnosticSource::Body,
+ self.store_owner,
LifetimeElisionKind::Infer,
|ctx| ctx.lower_lifetime(lifetime_ref),
);
self.insert_type_vars(lt)
}
- /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
- fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> {
- self.table.insert_type_vars_shallow(ty)
- }
-
fn insert_type_vars<T>(&mut self, ty: T) -> T
where
T: TypeFoldable<DbInterner<'db>>,
@@ -1766,10 +1966,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.table.insert_type_vars(ty)
}
- fn unify(&mut self, ty1: Ty<'db>, ty2: Ty<'db>) -> bool {
- 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.
@@ -1796,7 +1992,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
return self.err_ty();
}
match ty.kind() {
- TyKind::Adt(adt_def, substs) => match adt_def.def_id().0 {
+ TyKind::Adt(adt_def, substs) => match adt_def.def_id() {
AdtId::StructId(struct_id) => {
match self
.db
@@ -1806,7 +2002,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
.map(|it| it.get())
{
Some(field) => {
- ty = field.instantiate(self.interner(), substs);
+ ty = field.instantiate(self.interner(), substs).skip_norm_wip();
}
None => break,
}
@@ -1846,12 +2042,28 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.table.shallow_resolve(ty)
}
- fn resolve_associated_type(
+ pub(crate) fn resolve_vars_if_possible<T: TypeFoldable<DbInterner<'db>>>(&self, t: T) -> T {
+ self.table.resolve_vars_if_possible(t)
+ }
+
+ pub(crate) fn structurally_resolve_type(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> Ty<'db> {
+ let result = self.table.try_structurally_resolve_type(node.into(), ty);
+ if result.is_ty_var() { self.type_must_be_known_at_this_point(node, ty) } else { result }
+ }
+
+ pub(crate) fn emit_type_mismatch(
&mut self,
- inner_ty: Ty<'db>,
- assoc_ty: Option<TypeAliasId>,
- ) -> Ty<'db> {
- self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[])
+ node: ExprOrPatId,
+ expected: Ty<'db>,
+ found: Ty<'db>,
+ ) {
+ if self.result.nodes_with_type_mismatches.get_or_insert_default().insert(node) {
+ self.diagnostics.push(InferenceDiagnostic::TypeMismatch {
+ node,
+ expected: expected.store(),
+ found: found.store(),
+ });
+ }
}
fn demand_eqtype(
@@ -1860,14 +2072,15 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
expected: Ty<'db>,
actual: Ty<'db>,
) -> Result<(), ()> {
- let result = self.demand_eqtype_fixme_no_diag(expected, actual);
+ let result = self
+ .table
+ .at(&ObligationCause::new(id))
+ .eq(expected, actual)
+ .map(|infer_ok| self.table.register_infer_ok(infer_ok));
if result.is_err() {
- self.result
- .type_mismatches
- .get_or_insert_default()
- .insert(id, TypeMismatch { expected: expected.store(), actual: actual.store() });
+ self.emit_type_mismatch(id, expected, actual);
}
- result
+ result.map_err(drop)
}
fn demand_eqtype_fixme_no_diag(
@@ -1877,21 +2090,27 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
) -> Result<(), ()> {
let result = self
.table
- .at(&ObligationCause::new())
+ .at(&ObligationCause::dummy())
.eq(expected, actual)
.map(|infer_ok| self.table.register_infer_ok(infer_ok));
result.map_err(drop)
}
- fn demand_suptype(&mut self, expected: Ty<'db>, actual: Ty<'db>) {
+ fn demand_suptype(
+ &mut self,
+ id: ExprOrPatId,
+ expected: Ty<'db>,
+ actual: Ty<'db>,
+ ) -> Result<(), ()> {
let result = self
.table
- .at(&ObligationCause::new())
+ .at(&ObligationCause::new(id))
.sup(expected, actual)
.map(|infer_ok| self.table.register_infer_ok(infer_ok));
- if let Err(_err) = result {
- // FIXME: Emit diagnostic.
+ if result.is_err() {
+ self.emit_type_mismatch(id, expected, actual);
}
+ result.map_err(drop)
}
fn demand_coerce(
@@ -1902,7 +2121,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
allow_two_phase: AllowTwoPhase,
expr_is_read: ExprIsRead,
) -> Ty<'db> {
- let result = self.coerce(expr.into(), checked_ty, expected, allow_two_phase, expr_is_read);
+ let result = self.coerce(expr, checked_ty, expected, allow_two_phase, expr_is_read);
if let Err(_err) = result {
// FIXME: Emit diagnostic.
}
@@ -1910,19 +2129,24 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
}
pub(crate) fn type_must_be_known_at_this_point(
- &self,
- _id: ExprOrPatId,
- _ty: Ty<'db>,
+ &mut self,
+ node: ExprOrPatId,
+ ty: Ty<'db>,
) -> Ty<'db> {
- // FIXME: Emit an diagnostic.
+ if self.vars_emitted_type_must_be_known_for.insert(ty.into()) {
+ self.push_diagnostic(InferenceDiagnostic::TypeMustBeKnown {
+ at_point: node.into(),
+ top_term: None,
+ });
+ }
self.types.types.error
}
- pub(crate) fn require_type_is_sized(&mut self, ty: Ty<'db>) {
+ pub(crate) fn require_type_is_sized(&mut self, ty: Ty<'db>, span: Span) {
if !ty.references_non_lt_error()
&& let Some(sized_trait) = self.lang_items.Sized
{
- self.table.register_bound(ty, sized_trait, ObligationCause::new());
+ self.table.register_bound(ty, sized_trait, ObligationCause::new(span));
}
}
@@ -1940,39 +2164,16 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
ty.unwrap_or_else(|| self.expr_ty(e))
}
- fn resolve_associated_type_with_params(
- &mut self,
- inner_ty: Ty<'db>,
- assoc_ty: Option<TypeAliasId>,
- // FIXME(GATs): these are args for the trait ref, args for assoc type itself should be
- // handled when we support them.
- params: &[GenericArg<'db>],
- ) -> Ty<'db> {
- match assoc_ty {
- Some(res_assoc_ty) => {
- let alias = Ty::new_alias(
- self.interner(),
- AliasTy::new(
- self.interner(),
- AliasTyKind::Projection { def_id: res_assoc_ty.into() },
- iter::once(inner_ty.into()).chain(params.iter().copied()),
- ),
- );
- self.table.try_structurally_resolve_type(alias)
- }
- None => self.err_ty(),
- }
- }
-
fn resolve_variant(
&mut self,
node: ExprOrPatId,
- path: Option<&Path>,
+ path: &Path,
value_ns: bool,
) -> (Ty<'db>, Option<VariantId>) {
- let path = match path {
- Some(path) => path,
- None => return (self.err_ty(), None),
+ let interner = self.interner();
+ let mut vars_ctx = InferenceTyLoweringVarsCtx {
+ table: &mut self.table,
+ type_of_type_placeholder: &mut self.result.type_of_type_placeholder,
};
let mut ctx = TyLoweringContext::new(
self.db,
@@ -1980,21 +2181,26 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.store,
&self.diagnostics,
InferenceTyDiagnosticSource::Body,
+ self.store_owner,
self.generic_def,
+ &self.generics,
LifetimeElisionKind::Infer,
+ self.allow_using_generic_params,
+ Some(&mut vars_ctx),
+ &self.defined_anon_consts,
);
if let Some(type_anchor) = path.type_anchor() {
let mut segments = path.segments();
if segments.is_empty() {
- return (self.err_ty(), None);
+ return (self.types.types.error, None);
}
let (mut ty, type_ns) = ctx.lower_ty_ext(type_anchor);
- ty = self.table.process_user_written_ty(ty);
+ ty = ctx.expect_table().process_user_written_ty(ty);
if let Some(TypeNs::SelfType(impl_)) = type_ns
&& let Some(trait_ref) = self.db.impl_trait(impl_)
- && let trait_ref = trait_ref.instantiate_identity()
+ && let trait_ref = trait_ref.instantiate_identity().skip_norm_wip()
&& let Some(assoc_type) = trait_ref
.def_id
.0
@@ -2002,16 +2208,20 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
.associated_type_by_name(segments.first().unwrap().name)
{
// `<Self>::AssocType`
- let args = self.infcx().fill_rest_fresh_args(assoc_type.into(), trait_ref.args);
+ let args = ctx.expect_table().infer_ctxt.fill_rest_fresh_args(
+ node.into(),
+ assoc_type.into(),
+ trait_ref.args,
+ );
let alias = Ty::new_alias(
- self.interner(),
+ interner,
AliasTy::new_from_args(
- self.interner(),
+ interner,
AliasTyKind::Projection { def_id: assoc_type.into() },
args,
),
);
- ty = self.table.try_structurally_resolve_type(alias);
+ ty = ctx.expect_table().try_structurally_resolve_type(node.into(), alias);
segments = segments.skip(1);
}
@@ -2027,15 +2237,15 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
segments = segments.skip(1);
variant.into()
} else {
- return (self.err_ty(), None);
+ return (self.types.types.error, None);
}
}
- None => return (self.err_ty(), None),
+ None => return (self.types.types.error, None),
};
if !segments.is_empty() {
// FIXME: Report an error.
- return (self.err_ty(), None);
+ return (self.types.types.error, None);
} else {
return (ty, Some(variant));
}
@@ -2045,31 +2255,34 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
let interner = DbInterner::conjure();
let (resolution, unresolved) = if value_ns {
let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else {
- return (self.err_ty(), None);
+ return (self.types.types.error, None);
};
match res {
ResolveValueResult::ValueNs(value) => match value {
ValueNs::EnumVariantId(var) => {
- let args = path_ctx.substs_from_path(var.into(), true, false);
+ let args = path_ctx.substs_from_path(var.into(), true, false, node.into());
drop(ctx);
let ty = self
.db
.ty(var.lookup(self.db).parent.into())
- .instantiate(interner, args);
+ .instantiate(interner, args)
+ .skip_norm_wip();
let ty = self.insert_type_vars(ty);
return (ty, Some(var.into()));
}
ValueNs::StructId(strukt) => {
- let args = path_ctx.substs_from_path(strukt.into(), true, false);
+ let args =
+ path_ctx.substs_from_path(strukt.into(), true, false, node.into());
drop(ctx);
- let ty = self.db.ty(strukt.into()).instantiate(interner, args);
+ let ty =
+ self.db.ty(strukt.into()).instantiate(interner, args).skip_norm_wip();
let ty = self.insert_type_vars(ty);
return (ty, Some(strukt.into()));
}
ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None),
_ => {
drop(ctx);
- return (self.err_ty(), None);
+ return (self.types.types.error, None);
}
},
ResolveValueResult::Partial(typens, unresolved) => (typens, Some(unresolved)),
@@ -2077,41 +2290,45 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
} else {
match path_ctx.resolve_path_in_type_ns() {
Some((it, idx)) => (it, idx),
- None => return (self.err_ty(), None),
+ None => return (self.types.types.error, None),
}
};
return match resolution {
TypeNs::AdtId(AdtId::StructId(strukt)) => {
- let args = path_ctx.substs_from_path(strukt.into(), true, false);
+ let args = path_ctx.substs_from_path(strukt.into(), true, false, node.into());
drop(ctx);
- let ty = self.db.ty(strukt.into()).instantiate(interner, args);
+ let ty = self.db.ty(strukt.into()).instantiate(interner, args).skip_norm_wip();
let ty = self.insert_type_vars(ty);
forbid_unresolved_segments(self, (ty, Some(strukt.into())), unresolved)
}
TypeNs::AdtId(AdtId::UnionId(u)) => {
- let args = path_ctx.substs_from_path(u.into(), true, false);
+ let args = path_ctx.substs_from_path(u.into(), true, false, node.into());
drop(ctx);
- let ty = self.db.ty(u.into()).instantiate(interner, args);
+ let ty = self.db.ty(u.into()).instantiate(interner, args).skip_norm_wip();
let ty = self.insert_type_vars(ty);
forbid_unresolved_segments(self, (ty, Some(u.into())), unresolved)
}
TypeNs::EnumVariantId(var) => {
- let args = path_ctx.substs_from_path(var.into(), true, false);
+ let args = path_ctx.substs_from_path(var.into(), true, false, node.into());
drop(ctx);
- let ty = self.db.ty(var.lookup(self.db).parent.into()).instantiate(interner, args);
+ let ty = self
+ .db
+ .ty(var.lookup(self.db).parent.into())
+ .instantiate(interner, args)
+ .skip_norm_wip();
let ty = self.insert_type_vars(ty);
forbid_unresolved_segments(self, (ty, Some(var.into())), unresolved)
}
TypeNs::SelfType(impl_id) => {
- let mut ty = self.db.impl_self_ty(impl_id).instantiate_identity();
+ let mut ty = self.db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip();
let Some(remaining_idx) = unresolved else {
drop(ctx);
let Some(mod_path) = path.mod_path() else {
never!("resolver should always resolve lang item paths");
- return (self.err_ty(), None);
+ return (self.types.types.error, None);
};
- return self.resolve_variant_on_alias(ty, None, mod_path);
+ return self.resolve_variant_on_alias(node, ty, None, mod_path);
};
let mut remaining_segments = path.segments().skip(remaining_idx);
@@ -2127,7 +2344,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
// If we can resolve to an enum variant, it takes priority over associated type
// of the same name.
if let TyKind::Adt(adt_def, _) = ty.kind()
- && let AdtId::EnumId(id) = adt_def.def_id().0
+ && let AdtId::EnumId(id) = adt_def.def_id()
{
let enum_data = id.enum_variants(self.db);
if let Some(variant) = enum_data.variant(current_segment.name) {
@@ -2137,7 +2354,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
// We still have unresolved paths, but enum variants never have
// associated types!
// FIXME: Report an error.
- (self.err_ty(), None)
+ (self.types.types.error, None)
};
}
}
@@ -2151,12 +2368,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
// `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, _) = path_ctx.lower_partly_resolved_path(resolution, true);
+ (ty, _) = path_ctx.lower_partly_resolved_path(resolution, true, node.into());
tried_resolving_once = true;
- ty = self.table.process_user_written_ty(ty);
+ ty = path_ctx.expect_table().process_user_written_ty(ty);
if ty.is_ty_error() {
- return (self.err_ty(), None);
+ return (self.types.types.error, None);
}
remaining_segments = remaining_segments.skip(1);
@@ -2175,7 +2392,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
}
TypeNs::TraitId(_) => {
let Some(remaining_idx) = unresolved else {
- return (self.err_ty(), None);
+ return (self.types.types.error, None);
};
let remaining_segments = path.segments().skip(remaining_idx);
@@ -2184,8 +2401,9 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
path_ctx.ignore_last_segment();
}
- let (mut ty, _) = path_ctx.lower_partly_resolved_path(resolution, true);
- ty = self.table.process_user_written_ty(ty);
+ let (mut ty, _) =
+ path_ctx.lower_partly_resolved_path(resolution, true, node.into());
+ ty = ctx.expect_table().process_user_written_ty(ty);
if let Some(segment) = remaining_segments.get(1)
&& let Some((AdtId::EnumId(id), _)) = ty.as_adt()
@@ -2198,7 +2416,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
// We still have unresolved paths, but enum variants never have
// associated types!
// FIXME: Report an error.
- (self.err_ty(), None)
+ (self.types.types.error, None)
};
}
}
@@ -2216,27 +2434,28 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
TypeNs::TypeAliasId(it) => {
let Some(mod_path) = path.mod_path() else {
never!("resolver should always resolve lang item paths");
- return (self.err_ty(), None);
+ return (self.types.types.error, None);
};
- let args = path_ctx.substs_from_path_segment(it.into(), true, None, false);
+ let args =
+ path_ctx.substs_from_path_segment(it.into(), true, None, false, node.into());
drop(ctx);
let interner = DbInterner::conjure();
- let ty = self.db.ty(it.into()).instantiate(interner, args);
+ let ty = self.db.ty(it.into()).instantiate(interner, args).skip_norm_wip();
let ty = self.insert_type_vars(ty);
- self.resolve_variant_on_alias(ty, unresolved, mod_path)
+ self.resolve_variant_on_alias(node, ty, unresolved, mod_path)
}
TypeNs::AdtSelfType(_) => {
// FIXME this could happen in array size expressions, once we're checking them
- (self.err_ty(), None)
+ (self.types.types.error, None)
}
TypeNs::GenericParam(_) => {
// FIXME potentially resolve assoc type
- (self.err_ty(), None)
+ (self.types.types.error, None)
}
TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::ModuleId(_) => {
// FIXME diagnostic
- (self.err_ty(), None)
+ (self.types.types.error, None)
}
};
@@ -2256,12 +2475,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
fn resolve_variant_on_alias(
&mut self,
+ node: ExprOrPatId,
ty: Ty<'db>,
unresolved: Option<usize>,
path: &ModPath,
) -> (Ty<'db>, Option<VariantId>) {
let remaining = unresolved.map(|it| path.segments()[it..].len()).filter(|it| it > &0);
- let ty = self.table.try_structurally_resolve_type(ty);
+ let ty = self.table.try_structurally_resolve_type(node.into(), ty);
match remaining {
None => {
let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_id {
@@ -2293,19 +2513,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
}
}
- fn resolve_output_on(&self, trait_: TraitId) -> Option<TypeAliasId> {
- trait_.trait_items(self.db).associated_type_by_name(&Name::new_symbol_root(sym::Output))
- }
-
- fn resolve_future_future_output(&self) -> Option<TypeAliasId> {
- let ItemContainerId::TraitId(trait_) =
- self.lang_items.IntoFutureIntoFuture?.lookup(self.db).container
- else {
- return None;
- };
- self.resolve_output_on(trait_)
- }
-
fn resolve_boxed_box(&self) -> Option<AdtId> {
let struct_ = self.lang_items.OwnedBox?;
Some(struct_.into())
@@ -2317,7 +2524,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
}
fn has_new_range_feature(&self) -> bool {
- self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range)
+ self.features.new_range
}
fn resolve_range(&self) -> Option<AdtId> {
@@ -2374,6 +2581,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
Either::Right(&self.traits_in_scope)
}
}
+
+ fn has_applicable_non_exhaustive(&self, def: AttrDefId) -> bool {
+ AttrFlags::query(self.db, def).contains(AttrFlags::NON_EXHAUSTIVE)
+ && def.krate(self.db) != self.krate()
+ }
}
/// When inferring an expression, we propagate downward whatever type hint we
@@ -2432,7 +2644,7 @@ impl<'db> Expectation<'db> {
Expectation::None
}
- fn resolve(&self, table: &mut unify::InferenceTable<'db>) -> Expectation<'db> {
+ fn resolve(&self, table: &unify::InferenceTable<'db>) -> Expectation<'db> {
match self {
Expectation::None => Expectation::None,
Expectation::HasType(t) => Expectation::HasType(table.shallow_resolve(*t)),
@@ -2443,7 +2655,7 @@ impl<'db> Expectation<'db> {
}
}
- fn to_option(&self, table: &mut unify::InferenceTable<'db>) -> Option<Ty<'db>> {
+ fn to_option(&self, table: &unify::InferenceTable<'db>) -> Option<Ty<'db>> {
match self.resolve(table) {
Expectation::None => None,
Expectation::HasType(t)
@@ -2454,15 +2666,15 @@ impl<'db> Expectation<'db> {
fn only_has_type(&self, table: &mut unify::InferenceTable<'db>) -> Option<Ty<'db>> {
match self {
- Expectation::HasType(t) => Some(table.shallow_resolve(*t)),
+ Expectation::HasType(t) => Some(table.resolve_vars_if_possible(*t)),
Expectation::Castable(_) | Expectation::RValueLikeUnsized(_) | Expectation::None => {
None
}
}
}
- fn coercion_target_type(&self, table: &mut unify::InferenceTable<'db>) -> Ty<'db> {
- self.only_has_type(table).unwrap_or_else(|| table.next_ty_var())
+ fn coercion_target_type(&self, table: &mut unify::InferenceTable<'db>, span: Span) -> Ty<'db> {
+ self.only_has_type(table).unwrap_or_else(|| table.next_ty_var(span))
}
/// Comment copied from rustc:
@@ -2482,10 +2694,14 @@ impl<'db> Expectation<'db> {
/// an expected type. Otherwise, we might write parts of the type
/// when checking the 'then' block which are incompatible with the
/// 'else' branch.
- fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'db>) -> Expectation<'db> {
+ fn adjust_for_branches(
+ &self,
+ table: &mut unify::InferenceTable<'db>,
+ span: Span,
+ ) -> Expectation<'db> {
match *self {
Expectation::HasType(ety) => {
- let ety = table.structurally_resolve_type(ety);
+ let ety = table.try_structurally_resolve_type(span, ety);
if ety.is_ty_var() { Expectation::None } else { Expectation::HasType(ety) }
}
Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety),
diff --git a/crates/hir-ty/src/infer/autoderef.rs b/crates/hir-ty/src/infer/autoderef.rs
index a6c7b2dbb9..0ba3b3dd05 100644
--- a/crates/hir-ty/src/infer/autoderef.rs
+++ b/crates/hir-ty/src/infer/autoderef.rs
@@ -5,7 +5,7 @@ use std::iter;
use rustc_ast_ir::Mutability;
use crate::{
- Adjust, Adjustment, OverloadedDeref,
+ Adjust, Adjustment, OverloadedDeref, Span,
autoderef::{Autoderef, AutoderefCtx, AutoderefKind, GeneralAutoderef},
infer::unify::InferenceTable,
next_solver::{
@@ -15,12 +15,16 @@ use crate::{
};
impl<'db> InferenceTable<'db> {
- pub(crate) fn autoderef(&self, base_ty: Ty<'db>) -> Autoderef<'_, 'db, usize> {
- Autoderef::new(&self.infer_ctxt, self.param_env, base_ty)
+ pub(crate) fn autoderef(&self, base_ty: Ty<'db>, span: Span) -> Autoderef<'_, 'db, usize> {
+ Autoderef::new(&self.infer_ctxt, self.param_env, base_ty, span)
}
- pub(crate) fn autoderef_with_tracking(&self, base_ty: Ty<'db>) -> Autoderef<'_, 'db> {
- Autoderef::new_with_tracking(&self.infer_ctxt, self.param_env, base_ty)
+ pub(crate) fn autoderef_with_tracking(
+ &self,
+ base_ty: Ty<'db>,
+ span: Span,
+ ) -> Autoderef<'_, 'db> {
+ Autoderef::new_with_tracking(&self.infer_ctxt, self.param_env, base_ty, span)
}
}
diff --git a/crates/hir-ty/src/infer/callee.rs b/crates/hir-ty/src/infer/callee.rs
index 3d478912a3..057ba7fa86 100644
--- a/crates/hir-ty/src/infer/callee.rs
+++ b/crates/hir-ty/src/infer/callee.rs
@@ -2,17 +2,17 @@
use std::iter;
-use intern::sym;
+use rustc_abi::ExternAbi;
use tracing::debug;
-use hir_def::{CallableDefId, hir::ExprId, signatures::FunctionSignature};
+use hir_def::{CallableDefId, ConstParamId, hir::ExprId, signatures::FunctionSignature};
use rustc_type_ir::{
InferTy, Interner,
inherent::{GenericArgs as _, IntoKind, Ty as _},
};
use crate::{
- Adjust, Adjustment, AutoBorrow, FnAbi,
+ Adjust, Adjustment, AutoBorrow,
autoderef::{GeneralAutoderef, InferenceContextAutoderef},
infer::{
AllowTwoPhase, AutoBorrowMutability, Expectation, InferenceContext, InferenceDiagnostic,
@@ -20,7 +20,7 @@ use crate::{
},
method_resolution::{MethodCallee, TreatNotYetDefinedOpaques},
next_solver::{
- FnSig, Ty, TyKind,
+ ConstKind, FnSig, Ty, TyKind,
infer::{BoundRegionConversionTime, traits::ObligationCause},
},
};
@@ -43,13 +43,21 @@ impl<'db> InferenceContext<'_, 'db> {
) -> Ty<'db> {
let original_callee_ty = self.infer_expr_no_expect(callee_expr, ExprIsRead::Yes);
- let expr_ty = self.table.try_structurally_resolve_type(original_callee_ty);
+ let expr_ty =
+ self.table.try_structurally_resolve_type(callee_expr.into(), original_callee_ty);
- let mut autoderef = GeneralAutoderef::new_from_inference_context(self, expr_ty);
+ let mut autoderef =
+ GeneralAutoderef::new_from_inference_context(self, expr_ty, callee_expr.into());
let mut result = None;
+ let mut error_reported = false;
while result.is_none() && autoderef.next().is_some() {
- result =
- Self::try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &mut autoderef);
+ result = Self::try_overloaded_call_step(
+ call_expr,
+ callee_expr,
+ arg_exprs,
+ &mut autoderef,
+ &mut error_reported,
+ );
}
// FIXME: rustc does some ABI checks here, but the ABI mapping is in rustc_target and we don't have access to that crate.
@@ -65,16 +73,18 @@ impl<'db> InferenceContext<'_, 'db> {
self.infer_expr_no_expect(arg, ExprIsRead::Yes);
}
- self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
- call_expr,
- found: original_callee_ty.store(),
- });
+ if !error_reported {
+ self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
+ call_expr,
+ found: original_callee_ty.store(),
+ });
+ }
self.types.types.error
}
Some(CallStep::Builtin(callee_ty)) => {
- self.confirm_builtin_call(call_expr, callee_ty, arg_exprs, expected)
+ self.confirm_builtin_call(callee_expr, call_expr, callee_ty, arg_exprs, expected)
}
Some(CallStep::DeferredClosure(_def_id, fn_sig)) => {
@@ -87,7 +97,7 @@ impl<'db> InferenceContext<'_, 'db> {
};
// we must check that return type of called functions is WF:
- self.table.register_wf_obligation(output.into(), ObligationCause::new());
+ self.table.register_wf_obligation(output.into(), ObligationCause::new(call_expr));
output
}
@@ -97,9 +107,11 @@ impl<'db> InferenceContext<'_, 'db> {
callee_expr: ExprId,
arg_exprs: &[ExprId],
autoderef: &mut InferenceContextAutoderef<'_, '_, 'db>,
+ error_reported: &mut bool,
) -> Option<CallStep<'db>> {
let final_ty = autoderef.final_ty();
- let adjusted_ty = autoderef.ctx().table.try_structurally_resolve_type(final_ty);
+ let adjusted_ty =
+ autoderef.ctx().table.try_structurally_resolve_type(callee_expr.into(), final_ty);
// If the callee is a function pointer or a closure, then we're all set.
match adjusted_ty.kind() {
@@ -119,12 +131,13 @@ impl<'db> InferenceContext<'_, 'db> {
{
let closure_sig = args.as_closure().sig();
let closure_sig = autoderef.ctx().infcx().instantiate_binder_with_fresh_vars(
+ callee_expr.into(),
BoundRegionConversionTime::FnCall,
closure_sig,
);
let adjust_steps = autoderef.adjust_steps_as_infer_ok();
let adjustments = autoderef.ctx().table.register_infer_ok(adjust_steps);
- let def_id = def_id.0.loc(autoderef.ctx().db).1;
+ let def_id = def_id.0.loc(autoderef.ctx().db).expr;
autoderef.ctx().record_deferred_call_resolution(
def_id,
DeferredCallResolution {
@@ -149,15 +162,16 @@ impl<'db> InferenceContext<'_, 'db> {
let closure_args = args.as_coroutine_closure();
let coroutine_closure_sig =
autoderef.ctx().infcx().instantiate_binder_with_fresh_vars(
+ callee_expr.into(),
BoundRegionConversionTime::FnCall,
closure_args.coroutine_closure_sig(),
);
- let tupled_upvars_ty = autoderef.ctx().table.next_ty_var();
+ let tupled_upvars_ty = autoderef.ctx().table.next_ty_var(call_expr.into());
// We may actually receive a coroutine back whose kind is different
// from the closure that this dispatched from. This is because when
// we have no captures, we automatically implement `FnOnce`. This
// impl forces the closure kind to `FnOnce` i.e. `u8`.
- let kind_ty = autoderef.ctx().table.next_ty_var();
+ let kind_ty = autoderef.ctx().table.next_ty_var(call_expr.into());
let interner = autoderef.ctx().interner();
let call_sig = interner.mk_fn_sig(
[coroutine_closure_sig.tupled_inputs_ty],
@@ -168,13 +182,13 @@ impl<'db> InferenceContext<'_, 'db> {
interner.coroutine_for_closure(def_id),
tupled_upvars_ty,
),
- coroutine_closure_sig.c_variadic,
- coroutine_closure_sig.safety,
- coroutine_closure_sig.abi,
+ coroutine_closure_sig.fn_sig_kind.c_variadic(),
+ coroutine_closure_sig.fn_sig_kind.safety(),
+ coroutine_closure_sig.fn_sig_kind.abi(),
);
let adjust_steps = autoderef.adjust_steps_as_infer_ok();
let adjustments = autoderef.ctx().table.register_infer_ok(adjust_steps);
- let def_id = def_id.0.loc(autoderef.ctx().db).1;
+ let def_id = def_id.0.loc(autoderef.ctx().db).expr;
autoderef.ctx().record_deferred_call_resolution(
def_id,
DeferredCallResolution {
@@ -211,6 +225,7 @@ impl<'db> InferenceContext<'_, 'db> {
autoderef
.ctx()
.type_must_be_known_at_this_point(callee_expr.into(), adjusted_ty);
+ *error_reported = true;
return None;
}
@@ -230,8 +245,8 @@ impl<'db> InferenceContext<'_, 'db> {
// is implemented, and use this information for diagnostic.
autoderef
.ctx()
- .try_overloaded_call_traits(adjusted_ty, Some(arg_exprs))
- .or_else(|| autoderef.ctx().try_overloaded_call_traits(adjusted_ty, None))
+ .try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs))
+ .or_else(|| autoderef.ctx().try_overloaded_call_traits(call_expr, adjusted_ty, None))
.map(|(autoref, method)| {
let adjustments = autoderef.adjust_steps_as_infer_ok();
let mut adjustments = autoderef.ctx().table.register_infer_ok(adjustments);
@@ -243,6 +258,7 @@ impl<'db> InferenceContext<'_, 'db> {
fn try_overloaded_call_traits(
&mut self,
+ call_expr: ExprId,
adjusted_ty: Ty<'db>,
opt_arg_exprs: Option<&[ExprId]>,
) -> Option<(Option<Adjustment>, MethodCallee<'db>)> {
@@ -258,36 +274,38 @@ impl<'db> InferenceContext<'_, 'db> {
// ...or *ideally*, we just have `LendingFn`/`LendingFnMut`, which
// would naturally unify these two trait hierarchies in the most
// general way.
+
let call_trait_choices = if self.shallow_resolve(adjusted_ty).is_coroutine_closure() {
[
- (self.lang_items.AsyncFn, sym::async_call, true),
- (self.lang_items.AsyncFnMut, sym::async_call_mut, true),
- (self.lang_items.AsyncFnOnce, sym::async_call_once, false),
- (self.lang_items.Fn, sym::call, true),
- (self.lang_items.FnMut, sym::call_mut, true),
- (self.lang_items.FnOnce, sym::call_once, false),
+ (self.lang_items.AsyncFn, self.lang_items.AsyncFn_async_call, true),
+ (self.lang_items.AsyncFnMut, self.lang_items.AsyncFnMut_async_call_mut, true),
+ (self.lang_items.AsyncFnOnce, self.lang_items.AsyncFnOnce_async_call_once, false),
+ (self.lang_items.Fn, self.lang_items.Fn_call, true),
+ (self.lang_items.FnMut, self.lang_items.FnMut_call_mut, true),
+ (self.lang_items.FnOnce, self.lang_items.FnOnce_call_once, false),
]
} else {
[
- (self.lang_items.Fn, sym::call, true),
- (self.lang_items.FnMut, sym::call_mut, true),
- (self.lang_items.FnOnce, sym::call_once, false),
- (self.lang_items.AsyncFn, sym::async_call, true),
- (self.lang_items.AsyncFnMut, sym::async_call_mut, true),
- (self.lang_items.AsyncFnOnce, sym::async_call_once, false),
+ (self.lang_items.Fn, self.lang_items.Fn_call, true),
+ (self.lang_items.FnMut, self.lang_items.FnMut_call_mut, true),
+ (self.lang_items.FnOnce, self.lang_items.FnOnce_call_once, false),
+ (self.lang_items.AsyncFn, self.lang_items.AsyncFn_async_call, true),
+ (self.lang_items.AsyncFnMut, self.lang_items.AsyncFnMut_async_call_mut, true),
+ (self.lang_items.AsyncFnOnce, self.lang_items.AsyncFnOnce_async_call_once, false),
]
};
// Try the options that are least restrictive on the caller first.
- for (opt_trait_def_id, method_name, borrow) in call_trait_choices {
- let Some(trait_def_id) = opt_trait_def_id else {
+ for (opt_trait_def_id, opt_method_def_id, borrow) in call_trait_choices {
+ let (Some(trait_def_id), Some(method_def_id)) = (opt_trait_def_id, opt_method_def_id)
+ else {
continue;
};
let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
Ty::new_tup_from_iter(
self.interner(),
- arg_exprs.iter().map(|_| self.table.next_ty_var()),
+ arg_exprs.iter().map(|&arg| self.table.next_ty_var(arg.into())),
)
});
@@ -298,9 +316,9 @@ impl<'db> InferenceContext<'_, 'db> {
// one which may apply. So if we treat opaques as inference variables
// `Box<impl FnOnce()>: Fn` is considered ambiguous and chosen.
if let Some(ok) = self.table.lookup_method_for_operator(
- ObligationCause::new(),
- method_name,
+ ObligationCause::new(call_expr),
trait_def_id,
+ method_def_id,
adjusted_ty,
opt_input_type,
TreatNotYetDefinedOpaques::AsRigid,
@@ -337,12 +355,21 @@ impl<'db> InferenceContext<'_, 'db> {
fn check_legacy_const_generics(
&mut self,
callee: Option<CallableDefId>,
+ callee_ty: Ty<'db>,
args: &[ExprId],
) -> Box<[u32]> {
- let func = match callee {
- Some(CallableDefId::FunctionId(func)) => func,
+ let (func, fn_generic_args) = match (callee, callee_ty.kind()) {
+ (Some(CallableDefId::FunctionId(func)), TyKind::FnDef(_, fn_generic_args)) => {
+ (func, fn_generic_args)
+ }
_ => return Default::default(),
};
+ let generics = crate::generics::generics(self.db, func.into());
+ let const_params = generics
+ .iter_self_type_or_consts()
+ .filter(|(_, param_data)| param_data.const_param().is_some())
+ .map(|(id, _)| ConstParamId::from_unchecked(id))
+ .collect::<Vec<_>>();
let data = FunctionSignature::of(self.db, func);
let Some(legacy_const_generics_indices) = data.legacy_const_generics_indices(self.db, func)
@@ -364,11 +391,29 @@ impl<'db> InferenceContext<'_, 'db> {
}
// check legacy const parameters
- for arg_idx in legacy_const_generics_indices.iter().copied() {
+ for (const_idx, arg_idx) in legacy_const_generics_indices.iter().copied().enumerate() {
if arg_idx >= args.len() as u32 {
continue;
}
- let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly
+
+ if let Some(const_arg) = fn_generic_args.get(const_idx).and_then(|it| it.konst())
+ && let ConstKind::Infer(_) = const_arg.kind()
+ {
+ // Instantiate the generic arg with an error type, to prevent errors from it.
+ // FIXME: Actually lower the expression as const.
+ _ = self
+ .table
+ .at(&ObligationCause::dummy())
+ .eq(self.types.consts.error, const_arg)
+ .map(|infer_ok| self.table.register_infer_ok(infer_ok));
+ }
+
+ let expected = if let Some(&const_param) = const_params.get(const_idx) {
+ Expectation::has_type(self.db.const_param_ty(const_param))
+ } else {
+ Expectation::None
+ };
+
self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes);
// FIXME: evaluate and unify with the const
}
@@ -378,6 +423,7 @@ impl<'db> InferenceContext<'_, 'db> {
fn confirm_builtin_call(
&mut self,
+ callee_expr: ExprId,
call_expr: ExprId,
callee_ty: Ty<'db>,
arg_exprs: &[ExprId],
@@ -385,8 +431,11 @@ impl<'db> InferenceContext<'_, 'db> {
) -> Ty<'db> {
let (fn_sig, def_id) = match callee_ty.kind() {
TyKind::FnDef(def_id, args) => {
- let fn_sig =
- self.db.callable_item_signature(def_id.0).instantiate(self.interner(), args);
+ let fn_sig = self
+ .db
+ .callable_item_signature(def_id.0)
+ .instantiate(self.interner(), args)
+ .skip_norm_wip();
(fn_sig, Some(def_id.0))
}
@@ -401,11 +450,13 @@ impl<'db> InferenceContext<'_, 'db> {
// renormalize the associated types at this point, since they
// previously appeared within a `Binder<>` and hence would not
// have been normalized before.
- let fn_sig = self
- .infcx()
- .instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, fn_sig);
+ let fn_sig = self.infcx().instantiate_binder_with_fresh_vars(
+ callee_expr.into(),
+ BoundRegionConversionTime::FnCall,
+ fn_sig,
+ );
- let indices_to_skip = self.check_legacy_const_generics(def_id, arg_exprs);
+ let indices_to_skip = self.check_legacy_const_generics(def_id, callee_ty, arg_exprs);
self.check_call_arguments(
call_expr,
fn_sig.inputs(),
@@ -413,16 +464,17 @@ impl<'db> InferenceContext<'_, 'db> {
expected,
arg_exprs,
&indices_to_skip,
- fn_sig.c_variadic,
+ fn_sig.c_variadic(),
TupleArgumentsFlag::DontTupleArguments,
);
- if fn_sig.abi == FnAbi::RustCall
+ if fn_sig.abi() == ExternAbi::RustCall
&& let Some(ty) = fn_sig.inputs().last().copied()
&& let Some(tuple_trait) = self.lang_items.Tuple
{
- self.table.register_bound(ty, tuple_trait, ObligationCause::new());
- self.require_type_is_sized(ty);
+ let span = arg_exprs.last().copied().unwrap_or(call_expr);
+ self.table.register_bound(ty, tuple_trait, ObligationCause::new(span));
+ self.require_type_is_sized(ty, span.into());
}
fn_sig.output()
@@ -446,7 +498,7 @@ impl<'db> InferenceContext<'_, 'db> {
expected,
arg_exprs,
&[],
- fn_sig.c_variadic,
+ fn_sig.c_variadic(),
TupleArgumentsFlag::TupleArguments,
);
@@ -467,7 +519,7 @@ impl<'db> InferenceContext<'_, 'db> {
expected,
arg_exprs,
&[],
- method.sig.c_variadic,
+ method.sig.c_variadic(),
TupleArgumentsFlag::TupleArguments,
);
@@ -495,7 +547,7 @@ impl<'a, 'db> DeferredCallResolution<'db> {
assert!(ctx.infcx().closure_kind(self.closure_ty).is_some());
// We may now know enough to figure out fn vs fnmut etc.
- match ctx.try_overloaded_call_traits(self.closure_ty, None) {
+ match ctx.try_overloaded_call_traits(self.call_expr, self.closure_ty, None) {
Some((autoref, method_callee)) => {
// One problem is that when we get here, we are going
// to have a newly instantiated function signature
diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs
index e5ee734474..93aed344d4 100644
--- a/crates/hir-ty/src/infer/cast.rs
+++ b/crates/hir-ty/src/infer/cast.rs
@@ -10,7 +10,7 @@ use rustc_hash::FxHashSet;
use rustc_type_ir::{
InferTy, TypeVisitableExt, UintTy, elaborate,
error::TypeError,
- inherent::{AdtDef, BoundExistentialPredicates as _, IntoKind, Ty as _},
+ inherent::{BoundExistentialPredicates as _, IntoKind, Ty as _},
};
use stdx::never;
@@ -125,13 +125,14 @@ impl<'db> CastCheck<'db> {
&mut self,
ctx: &mut InferenceContext<'_, 'db>,
) -> Result<(), InferenceDiagnostic> {
- self.expr_ty = ctx.table.try_structurally_resolve_type(self.expr_ty);
- self.cast_ty = ctx.table.try_structurally_resolve_type(self.cast_ty);
+ self.expr_ty =
+ ctx.table.try_structurally_resolve_type(self.source_expr.into(), self.expr_ty);
+ self.cast_ty = ctx.table.try_structurally_resolve_type(self.expr.into(), self.cast_ty);
// This should always come first so that we apply the coercion, which impacts infer vars.
if ctx
.coerce(
- self.source_expr.into(),
+ self.source_expr,
self.expr_ty,
self.cast_ty,
AllowTwoPhase::No,
@@ -147,7 +148,8 @@ impl<'db> CastCheck<'db> {
return Ok(());
}
- if !self.cast_ty.has_infer_types() && !ctx.table.is_sized(self.cast_ty) {
+ if !self.cast_ty.has_infer_types() && !ctx.table.type_is_sized_modulo_regions(self.cast_ty)
+ {
return Err(InferenceDiagnostic::CastToUnsized {
expr: self.expr,
cast_ty: self.cast_ty.store(),
@@ -167,7 +169,7 @@ impl<'db> CastCheck<'db> {
let sig = self.expr_ty.fn_sig(ctx.interner());
let fn_ptr = Ty::new_fn_ptr(ctx.interner(), sig);
match ctx.coerce(
- self.source_expr.into(),
+ self.source_expr,
self.expr_ty,
fn_ptr,
AllowTwoPhase::No,
@@ -198,8 +200,9 @@ impl<'db> CastCheck<'db> {
},
// array-ptr-cast
CastTy::Ptr(t, m) => {
- let t = ctx.table.try_structurally_resolve_type(t);
- if !ctx.table.is_sized(t) {
+ let t =
+ ctx.table.try_structurally_resolve_type(self.expr.into(), t);
+ if !ctx.table.type_is_sized_modulo_regions(t) {
return Err(CastError::IllegalCast);
}
self.check_ref_cast(ctx, inner_ty, mutbl, t, m)
@@ -261,8 +264,8 @@ impl<'db> CastCheck<'db> {
t_cast: Ty<'db>,
m_cast: Mutability,
) -> Result<(), CastError> {
- let t_expr = ctx.table.try_structurally_resolve_type(t_expr);
- let t_cast = ctx.table.try_structurally_resolve_type(t_cast);
+ let t_expr = ctx.table.try_structurally_resolve_type(self.expr.into(), t_expr);
+ let t_cast = ctx.table.try_structurally_resolve_type(self.expr.into(), t_cast);
if m_expr >= m_cast
&& let TyKind::Array(ety, _) = t_expr.kind()
@@ -275,7 +278,7 @@ impl<'db> CastCheck<'db> {
let array_ptr_type = Ty::new_ptr(ctx.interner(), t_expr, m_expr);
if ctx
.coerce(
- self.source_expr.into(),
+ self.source_expr,
self.expr_ty,
array_ptr_type,
AllowTwoPhase::No,
@@ -305,8 +308,8 @@ impl<'db> CastCheck<'db> {
src: Ty<'db>,
dst: Ty<'db>,
) -> Result<(), CastError> {
- let src_kind = pointer_kind(src, ctx).map_err(|_| CastError::Unknown)?;
- let dst_kind = pointer_kind(dst, ctx).map_err(|_| CastError::Unknown)?;
+ let src_kind = pointer_kind(self.expr, src, ctx).map_err(|_| CastError::Unknown)?;
+ let dst_kind = pointer_kind(self.expr, dst, ctx).map_err(|_| CastError::Unknown)?;
match (src_kind, dst_kind) {
(Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()),
@@ -371,7 +374,7 @@ impl<'db> CastCheck<'db> {
// This is `fcx.demand_eqtype`, but inlined to give a better error.
if ctx
.table
- .at(&ObligationCause::dummy())
+ .at(&ObligationCause::new(self.expr))
.eq(src_obj, dst_obj)
.map(|infer_ok| ctx.table.register_infer_ok(infer_ok))
.is_err()
@@ -456,7 +459,7 @@ impl<'db> CastCheck<'db> {
ctx: &mut InferenceContext<'_, 'db>,
expr_ty: Ty<'db>,
) -> Result<(), CastError> {
- match pointer_kind(expr_ty, ctx).map_err(|_| CastError::Unknown)? {
+ match pointer_kind(self.expr, expr_ty, ctx).map_err(|_| CastError::Unknown)? {
// None => Err(CastError::UnknownExprPtrKind),
None => Ok(()),
Some(PointerKind::Error) => Ok(()),
@@ -470,7 +473,7 @@ impl<'db> CastCheck<'db> {
ctx: &mut InferenceContext<'_, 'db>,
cast_ty: Ty<'db>,
) -> Result<(), CastError> {
- match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? {
+ match pointer_kind(self.expr, cast_ty, ctx).map_err(|_| CastError::Unknown)? {
// None => Err(CastError::UnknownCastPtrKind),
None => Ok(()),
Some(PointerKind::Error) => Ok(()),
@@ -486,7 +489,7 @@ impl<'db> CastCheck<'db> {
ctx: &mut InferenceContext<'_, 'db>,
cast_ty: Ty<'db>,
) -> Result<(), CastError> {
- match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? {
+ match pointer_kind(self.expr, cast_ty, ctx).map_err(|_| CastError::Unknown)? {
// None => Err(CastError::UnknownCastPtrKind),
None => Ok(()),
Some(PointerKind::Error) => Ok(()),
@@ -515,12 +518,13 @@ enum PointerKind<'db> {
}
fn pointer_kind<'db>(
+ expr: ExprId,
ty: Ty<'db>,
ctx: &mut InferenceContext<'_, 'db>,
) -> Result<Option<PointerKind<'db>>, ()> {
- let ty = ctx.table.try_structurally_resolve_type(ty);
+ let ty = ctx.table.try_structurally_resolve_type(expr.into(), ty);
- if ctx.table.is_sized(ty) {
+ if ctx.table.type_is_sized_modulo_regions(ty) {
return Ok(Some(PointerKind::Thin));
}
@@ -528,7 +532,7 @@ fn pointer_kind<'db>(
TyKind::Slice(_) | TyKind::Str => Ok(Some(PointerKind::Length)),
TyKind::Dynamic(bounds, _) => Ok(Some(PointerKind::VTable(bounds))),
TyKind::Adt(adt_def, subst) => {
- let id = adt_def.def_id().0;
+ let id = adt_def.def_id();
let AdtId::StructId(id) = id else {
never!("`{:?}` should be sized but is not?", ty);
return Err(());
@@ -538,15 +542,16 @@ fn pointer_kind<'db>(
if let Some((last_field, _)) = struct_data.fields().iter().last() {
let last_field_ty = ctx.db.field_types(id.into())[last_field]
.get()
- .instantiate(ctx.interner(), subst);
- pointer_kind(last_field_ty, ctx)
+ .instantiate(ctx.interner(), subst)
+ .skip_norm_wip();
+ pointer_kind(expr, last_field_ty, ctx)
} else {
Ok(Some(PointerKind::Thin))
}
}
TyKind::Tuple(subst) => match subst.iter().next_back() {
None => Ok(Some(PointerKind::Thin)),
- Some(ty) => pointer_kind(ty, ctx),
+ Some(ty) => pointer_kind(expr, ty, ctx),
},
TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)),
TyKind::Alias(..) => Ok(Some(PointerKind::OfAlias)),
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 2207bc37e8..ab111736d5 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -5,25 +5,27 @@ pub(crate) mod analysis;
use std::{iter, mem, ops::ControlFlow};
use hir_def::{
- TraitId,
- hir::{ClosureKind, CoroutineSource, ExprId, PatId},
+ AdtId, TraitId,
+ hir::{ClosureKind, CoroutineKind, CoroutineSource, ExprId, PatId},
type_ref::TypeRefId,
};
+use rustc_abi::ExternAbi;
use rustc_type_ir::{
- ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, CoroutineClosureArgs,
- CoroutineClosureArgsParts, Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
- TypeVisitor,
+ AliasTyKind, ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts,
+ CoroutineClosureArgs, CoroutineClosureArgsParts, InferTy, Interner, TypeSuperVisitable,
+ TypeVisitable, TypeVisitableExt, TypeVisitor,
inherent::{BoundExistentialPredicates, GenericArgs as _, IntoKind, Ty as _},
};
-use tracing::debug;
+use tracing::{debug, instrument};
use crate::{
- FnAbi,
+ Span,
db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId},
- infer::{BreakableKind, Diverges, coerce::CoerceMany},
+ infer::{BreakableKind, Diverges, coerce::CoerceMany, pat::PatOrigin},
next_solver::{
- AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig,
- PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, Ty, TyKind,
+ AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArg, PolyFnSig,
+ PolyProjectionPredicate, Predicate, PredicateKind, SolverDefId, TermId, Ty, TyKind,
+ Unnormalized,
abi::Safety,
infer::{
BoundRegionConversionTime, InferOk, InferResult,
@@ -46,6 +48,22 @@ struct ClosureSignatures<'db> {
}
impl<'db> InferenceContext<'_, 'db> {
+ fn poll_option_ty(&mut self, item_ty: Ty<'db>) -> Ty<'db> {
+ let interner = self.interner();
+
+ let (Some(option), Some(poll)) = (self.lang_items.Option, self.lang_items.Poll) else {
+ return self.types.types.error;
+ };
+
+ let option_ty = Ty::new_adt(
+ interner,
+ AdtId::EnumId(option),
+ interner.mk_args(&[GenericArg::from(item_ty)]),
+ );
+
+ Ty::new_adt(interner, AdtId::EnumId(poll), interner.mk_args(&[GenericArg::from(option_ty)]))
+ }
+
pub(super) fn infer_closure(
&mut self,
body: ExprId,
@@ -62,23 +80,31 @@ impl<'db> InferenceContext<'_, 'db> {
// It's always helpful for inference if we know the kind of
// closure sooner rather than later, so first examine the expected
// type, and see if can glean a closure kind from there.
- let (expected_sig, expected_kind) = match expected.to_option(&mut self.table) {
+ let (expected_sig, expected_kind) = match expected.to_option(&self.table) {
Some(ty) => {
- let ty = self.table.try_structurally_resolve_type(ty);
- self.deduce_closure_signature(ty, closure_kind)
+ let ty = self.table.try_structurally_resolve_type(closure_expr.into(), ty);
+ self.deduce_closure_signature(closure_expr, ty, closure_kind)
}
None => (None, None),
};
- let ClosureSignatures { bound_sig, mut liberated_sig } =
- self.sig_of_closure(arg_types, ret_type, expected_sig);
+ let ClosureSignatures { bound_sig, mut liberated_sig } = self.sig_of_closure(
+ closure_expr,
+ args,
+ arg_types,
+ ret_type,
+ expected_sig,
+ closure_kind,
+ );
debug!(?bound_sig, ?liberated_sig);
- let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into());
+ let parent_args = self.identity_args();
- let tupled_upvars_ty = self.table.next_ty_var();
+ let tupled_upvars_ty = self.table.next_ty_var(closure_expr.into());
+ let closure_loc =
+ InternedClosure { owner: self.owner, expr: closure_expr, kind: closure_kind };
// FIXME: We could probably actually just unify this further --
// instead of having a `FnSig` and a `Option<CoroutineTypes>`,
// we can have a `ClosureSignature { Coroutine { .. }, Closure { .. } }`,
@@ -91,9 +117,9 @@ impl<'db> InferenceContext<'_, 'db> {
interner.mk_fn_sig(
[Ty::new_tup(interner, sig.inputs())],
sig.output(),
- sig.c_variadic,
- sig.safety,
- sig.abi,
+ sig.c_variadic(),
+ sig.safety(),
+ sig.abi(),
)
});
@@ -103,7 +129,7 @@ impl<'db> InferenceContext<'_, 'db> {
Some(kind) => Ty::from_closure_kind(interner, kind),
// Create a type variable (for now) to represent the closure kind.
// It will be unified during the upvar inference phase (`upvar.rs`)
- None => self.table.next_ty_var(),
+ None => self.table.next_ty_var(closure_expr.into()),
};
let closure_args = ClosureArgs::new(
@@ -116,15 +142,26 @@ impl<'db> InferenceContext<'_, 'db> {
},
);
- let closure_id =
- InternedClosureId::new(self.db, InternedClosure(self.owner, closure_expr));
+ let closure_id = InternedClosureId::new(self.db, closure_loc);
(Ty::new_closure(interner, closure_id.into(), closure_args.args), None)
}
- ClosureKind::Coroutine(_) | ClosureKind::AsyncBlock { .. } => {
+ ClosureKind::OldCoroutine(_) | ClosureKind::Coroutine { .. } => {
let yield_ty = match closure_kind {
- ClosureKind::Coroutine(_) => self.table.next_ty_var(),
- ClosureKind::AsyncBlock { .. } => self.types.types.unit,
+ ClosureKind::OldCoroutine(_)
+ | ClosureKind::Coroutine { kind: CoroutineKind::Gen, .. } => {
+ let yield_ty = self.table.next_ty_var(closure_expr.into());
+ self.require_type_is_sized(yield_ty, closure_expr.into());
+ yield_ty
+ }
+ ClosureKind::Coroutine { kind: CoroutineKind::Async, .. } => {
+ self.types.types.unit
+ }
+ ClosureKind::Coroutine { kind: CoroutineKind::AsyncGen, .. } => {
+ let yield_ty = self.table.next_ty_var(closure_expr.into());
+ self.require_type_is_sized(yield_ty, closure_expr.into());
+ self.poll_option_ty(yield_ty)
+ }
_ => unreachable!(),
};
@@ -137,8 +174,8 @@ impl<'db> InferenceContext<'_, 'db> {
// later during upvar analysis. Regular coroutines always have the kind
// ty of `().`
let kind_ty = match closure_kind {
- ClosureKind::AsyncBlock { source: CoroutineSource::Closure } => {
- self.table.next_ty_var()
+ ClosureKind::Coroutine { source: CoroutineSource::Closure, .. } => {
+ self.table.next_ty_var(closure_expr.into())
}
_ => self.types.types.unit,
};
@@ -155,31 +192,39 @@ impl<'db> InferenceContext<'_, 'db> {
},
);
- let coroutine_id =
- InternedCoroutineId::new(self.db, InternedClosure(self.owner, closure_expr));
+ let coroutine_id = InternedCoroutineId::new(self.db, closure_loc);
(
Ty::new_coroutine(interner, coroutine_id.into(), coroutine_args.args),
Some((resume_ty, yield_ty)),
)
}
- ClosureKind::AsyncClosure => {
- // async closures always return the type ascribed after the `->` (if present),
- // and yield `()`.
- let (bound_return_ty, bound_yield_ty) =
- (bound_sig.skip_binder().output(), self.types.types.unit);
+ ClosureKind::CoroutineClosure(coroutine_kind) => {
+ let (bound_return_ty, bound_yield_ty) = match coroutine_kind {
+ CoroutineKind::Gen => {
+ (self.types.types.unit, self.table.next_ty_var(closure_expr.into()))
+ }
+ CoroutineKind::Async => {
+ (bound_sig.skip_binder().output(), self.types.types.unit)
+ }
+ CoroutineKind::AsyncGen => {
+ let yield_ty = self.table.next_ty_var(closure_expr.into());
+ (self.types.types.unit, self.poll_option_ty(yield_ty))
+ }
+ };
+
// Compute all of the variables that will be used to populate the coroutine.
- let resume_ty = self.table.next_ty_var();
+ let resume_ty = self.table.next_ty_var(closure_expr.into());
let closure_kind_ty = match expected_kind {
Some(kind) => Ty::from_closure_kind(interner, kind),
// Create a type variable (for now) to represent the closure kind.
// It will be unified during the upvar inference phase (`upvar.rs`)
- None => self.table.next_ty_var(),
+ None => self.table.next_ty_var(closure_expr.into()),
};
- let coroutine_captures_by_ref_ty = self.table.next_ty_var();
+ let coroutine_captures_by_ref_ty = self.table.next_ty_var(closure_expr.into());
let closure_args = CoroutineClosureArgs::new(
interner,
@@ -198,9 +243,9 @@ impl<'db> InferenceContext<'_, 'db> {
),
],
Ty::new_tup(interner, &[bound_yield_ty, bound_return_ty]),
- sig.c_variadic,
- sig.safety,
- sig.abi,
+ sig.c_variadic(),
+ sig.safety(),
+ sig.abi(),
)
}),
),
@@ -214,15 +259,12 @@ impl<'db> InferenceContext<'_, 'db> {
// Create a type variable (for now) to represent the closure kind.
// It will be unified during the upvar inference phase (`upvar.rs`)
- None => self.table.next_ty_var(),
+ None => self.table.next_ty_var(closure_expr.into()),
};
- let coroutine_upvars_ty = self.table.next_ty_var();
+ let coroutine_upvars_ty = self.table.next_ty_var(closure_expr.into());
- let coroutine_closure_id = InternedCoroutineClosureId::new(
- self.db,
- InternedClosure(self.owner, closure_expr),
- );
+ let coroutine_closure_id = InternedCoroutineClosureId::new(self.db, closure_loc);
// We need to turn the liberated signature that we got from HIR, which
// looks something like `|Args...| -> T`, into a signature that is suitable
@@ -245,9 +287,9 @@ impl<'db> InferenceContext<'_, 'db> {
liberated_sig = interner.mk_fn_sig(
liberated_sig.inputs().iter().copied(),
coroutine_output_ty,
- liberated_sig.c_variadic,
- liberated_sig.safety,
- liberated_sig.abi,
+ liberated_sig.c_variadic(),
+ liberated_sig.safety(),
+ liberated_sig.abi(),
);
(
@@ -263,7 +305,7 @@ impl<'db> InferenceContext<'_, 'db> {
// Now go through the argument patterns
for (arg_pat, arg_ty) in args.iter().zip(bound_sig.skip_binder().inputs()) {
- self.infer_top_pat(*arg_pat, *arg_ty, None);
+ self.infer_top_pat(*arg_pat, *arg_ty, PatOrigin::Param);
}
// FIXME: lift these out into a struct
@@ -316,24 +358,27 @@ impl<'db> InferenceContext<'_, 'db> {
/// are about to type check:
fn deduce_closure_signature(
&mut self,
+ closure_expr: ExprId,
expected_ty: Ty<'db>,
closure_kind: ClosureKind,
) -> (Option<PolyFnSig<'db>>, Option<rustc_type_ir::ClosureKind>) {
match expected_ty.kind() {
TyKind::Alias(AliasTy { kind: rustc_type_ir::Opaque { def_id }, args, .. }) => self
.deduce_closure_signature_from_predicates(
+ closure_expr,
expected_ty,
closure_kind,
def_id
- .expect_opaque_ty()
+ .0
.predicates(self.db)
.iter_instantiated_copied(self.interner(), args.as_slice())
+ .map(Unnormalized::skip_norm_wip)
.map(|clause| clause.as_predicate()),
),
TyKind::Dynamic(object_type, ..) => {
let sig = object_type.projection_bounds().into_iter().find_map(|pb| {
let pb = pb.with_self_ty(self.interner(), Ty::new_unit(self.interner()));
- self.deduce_sig_from_projection(closure_kind, pb)
+ self.deduce_sig_from_projection(closure_expr, closure_kind, pb)
});
let kind = object_type
.principal_def_id()
@@ -342,6 +387,7 @@ impl<'db> InferenceContext<'_, 'db> {
}
TyKind::Infer(rustc_type_ir::TyVar(vid)) => self
.deduce_closure_signature_from_predicates(
+ closure_expr,
Ty::new_var(self.interner(), self.table.infer_ctxt.root_var(vid)),
closure_kind,
self.table.obligations_for_self_ty(vid).into_iter().map(|obl| obl.predicate),
@@ -351,9 +397,9 @@ impl<'db> InferenceContext<'_, 'db> {
let expected_sig = sig_tys.with(hdr);
(Some(expected_sig), Some(rustc_type_ir::ClosureKind::Fn))
}
- ClosureKind::Coroutine(_)
- | ClosureKind::AsyncClosure
- | ClosureKind::AsyncBlock { .. } => (None, None),
+ ClosureKind::OldCoroutine(_)
+ | ClosureKind::Coroutine { .. }
+ | ClosureKind::CoroutineClosure(_) => (None, None),
},
_ => (None, None),
}
@@ -361,6 +407,7 @@ impl<'db> InferenceContext<'_, 'db> {
fn deduce_closure_signature_from_predicates(
&mut self,
+ closure_expr: ExprId,
expected_ty: Ty<'db>,
closure_kind: ClosureKind,
predicates: impl DoubleEndedIterator<Item = Predicate<'db>>,
@@ -388,6 +435,7 @@ impl<'db> InferenceContext<'_, 'db> {
bound_predicate.skip_binder()
{
let inferred_sig = self.deduce_sig_from_projection(
+ closure_expr,
closure_kind,
bound_predicate.rebind(proj_predicate),
);
@@ -430,18 +478,17 @@ impl<'db> InferenceContext<'_, 'db> {
// This is a bit weird and means we may wind up discarding the goal due to it naming `expected_ty`
// even though the normalized form may not name `expected_ty`. However, this matches the existing
// behaviour of the old solver and would be technically a breaking change to fix.
- let generalized_fnptr_sig = self.table.next_ty_var();
+ let generalized_fnptr_sig = self.table.next_ty_var(closure_expr.into());
let inferred_fnptr_sig = Ty::new_fn_ptr(self.interner(), inferred_sig);
// FIXME: Report diagnostics.
_ = self
.table
.infer_ctxt
- .at(&ObligationCause::new(), self.table.param_env)
+ .at(&ObligationCause::new(closure_expr), self.table.param_env)
.eq(inferred_fnptr_sig, generalized_fnptr_sig)
.map(|infer_ok| self.table.register_infer_ok(infer_ok));
- let resolved_sig =
- self.table.infer_ctxt.resolve_vars_if_possible(generalized_fnptr_sig);
+ let resolved_sig = self.resolve_vars_if_possible(generalized_fnptr_sig);
if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() {
expected_sig = Some(resolved_sig.fn_sig(self.interner()));
@@ -465,8 +512,10 @@ impl<'db> InferenceContext<'_, 'db> {
if let Some(trait_def_id) = trait_def_id {
let found_kind = match closure_kind {
- ClosureKind::Closure => self.fn_trait_kind_from_def_id(trait_def_id),
- ClosureKind::AsyncClosure => self
+ ClosureKind::Closure | ClosureKind::CoroutineClosure(CoroutineKind::Gen) => {
+ self.fn_trait_kind_from_def_id(trait_def_id)
+ }
+ ClosureKind::CoroutineClosure(CoroutineKind::Async) => self
.async_fn_trait_kind_from_def_id(trait_def_id)
.or_else(|| self.fn_trait_kind_from_def_id(trait_def_id)),
_ => None,
@@ -501,6 +550,7 @@ impl<'db> InferenceContext<'_, 'db> {
/// know that.
fn deduce_sig_from_projection(
&mut self,
+ closure_expr: ExprId,
closure_kind: ClosureKind,
projection: PolyProjectionPredicate<'db>,
) -> Option<PolyFnSig<'db>> {
@@ -512,14 +562,18 @@ impl<'db> InferenceContext<'_, 'db> {
ClosureKind::Closure if Some(def_id) == self.lang_items.FnOnceOutput => {
self.extract_sig_from_projection(projection)
}
- ClosureKind::AsyncClosure if Some(def_id) == self.lang_items.AsyncFnOnceOutput => {
+ ClosureKind::CoroutineClosure(CoroutineKind::Async)
+ if Some(def_id) == self.lang_items.AsyncFnOnceOutput =>
+ {
self.extract_sig_from_projection(projection)
}
// It's possible we've passed the closure to a (somewhat out-of-fashion)
// `F: FnOnce() -> Fut, Fut: Future<Output = T>` style bound. Let's still
// guide inference here, since it's beneficial for the user.
- ClosureKind::AsyncClosure if Some(def_id) == self.lang_items.FnOnceOutput => {
- self.extract_sig_from_projection_and_future_bound(projection)
+ ClosureKind::CoroutineClosure(CoroutineKind::Async)
+ if Some(def_id) == self.lang_items.FnOnceOutput =>
+ {
+ self.extract_sig_from_projection_and_future_bound(closure_expr, projection)
}
_ => None,
}
@@ -531,7 +585,7 @@ impl<'db> InferenceContext<'_, 'db> {
&self,
projection: PolyProjectionPredicate<'db>,
) -> Option<PolyFnSig<'db>> {
- let projection = self.table.infer_ctxt.resolve_vars_if_possible(projection);
+ let projection = self.resolve_vars_if_possible(projection);
let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1);
debug!(?arg_param_ty);
@@ -574,9 +628,10 @@ impl<'db> InferenceContext<'_, 'db> {
/// projection, and the output will be an unconstrained type variable instead.
fn extract_sig_from_projection_and_future_bound(
&mut self,
+ closure_expr: ExprId,
projection: PolyProjectionPredicate<'db>,
) -> Option<PolyFnSig<'db>> {
- let projection = self.table.infer_ctxt.resolve_vars_if_possible(projection);
+ let projection = self.resolve_vars_if_possible(projection);
let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1);
debug!(?arg_param_ty);
@@ -603,7 +658,7 @@ impl<'db> InferenceContext<'_, 'db> {
bound.predicate.kind().skip_binder()
&& let ret_projection = bound.predicate.kind().rebind(ret_projection)
&& let Some(ret_projection) = ret_projection.no_bound_vars()
- && let SolverDefId::TypeAliasId(assoc_type) = ret_projection.def_id()
+ && let TermId::TypeAliasId(assoc_type) = ret_projection.def_id().0
&& Some(assoc_type) == self.lang_items.FutureOutput
{
return_ty = Some(ret_projection.term.expect_type());
@@ -625,7 +680,7 @@ impl<'db> InferenceContext<'_, 'db> {
//
// FIXME: We probably should store this signature inference output in a way
// that does not misuse a `FnSig` type, but that can be done separately.
- let return_ty = return_ty.unwrap_or_else(|| self.table.next_ty_var());
+ let return_ty = return_ty.unwrap_or_else(|| self.table.next_ty_var(closure_expr.into()));
let sig = projection.rebind(self.interner().mk_fn_sig_safe_rust_abi(input_tys, return_ty));
@@ -634,14 +689,29 @@ impl<'db> InferenceContext<'_, 'db> {
fn sig_of_closure(
&mut self,
- decl_inputs: &[Option<TypeRefId>],
- decl_output: Option<TypeRefId>,
+ closure_expr: ExprId,
+ decl_inputs: &[PatId],
+ decl_input_tys: &[Option<TypeRefId>],
+ decl_output_ty: Option<TypeRefId>,
expected_sig: Option<PolyFnSig<'db>>,
+ closure_kind: ClosureKind,
) -> ClosureSignatures<'db> {
if let Some(e) = expected_sig {
- self.sig_of_closure_with_expectation(decl_inputs, decl_output, e)
+ self.sig_of_closure_with_expectation(
+ closure_expr,
+ decl_inputs,
+ decl_input_tys,
+ decl_output_ty,
+ e,
+ closure_kind,
+ )
} else {
- self.sig_of_closure_no_expectation(decl_inputs, decl_output)
+ self.sig_of_closure_no_expectation(
+ closure_expr,
+ decl_input_tys,
+ decl_output_ty,
+ closure_kind,
+ )
}
}
@@ -649,10 +719,13 @@ impl<'db> InferenceContext<'_, 'db> {
/// types that the user gave into a signature.
fn sig_of_closure_no_expectation(
&mut self,
+ closure_expr: ExprId,
decl_inputs: &[Option<TypeRefId>],
decl_output: Option<TypeRefId>,
+ closure_kind: ClosureKind,
) -> ClosureSignatures<'db> {
- let bound_sig = self.supplied_sig_of_closure(decl_inputs, decl_output);
+ let bound_sig =
+ self.supplied_sig_of_closure(closure_expr, decl_inputs, decl_output, closure_kind);
self.closure_sigs(bound_sig)
}
@@ -706,18 +779,28 @@ impl<'db> InferenceContext<'_, 'db> {
/// regions with depth 1, which are bound then by the closure.
fn sig_of_closure_with_expectation(
&mut self,
- decl_inputs: &[Option<TypeRefId>],
- decl_output: Option<TypeRefId>,
+ closure_expr: ExprId,
+ decl_inputs: &[PatId],
+ decl_input_tys: &[Option<TypeRefId>],
+ decl_output_ty: Option<TypeRefId>,
expected_sig: PolyFnSig<'db>,
+ closure_kind: ClosureKind,
) -> ClosureSignatures<'db> {
// Watch out for some surprises and just ignore the
// expectation if things don't see to match up with what we
// expect.
if expected_sig.c_variadic() {
- return self.sig_of_closure_no_expectation(decl_inputs, decl_output);
- } else if expected_sig.skip_binder().inputs_and_output.len() != decl_inputs.len() + 1 {
- return self
- .sig_of_closure_with_mismatched_number_of_arguments(decl_inputs, decl_output);
+ return self.sig_of_closure_no_expectation(
+ closure_expr,
+ decl_input_tys,
+ decl_output_ty,
+ closure_kind,
+ );
+ } else if expected_sig.skip_binder().inputs_and_output.len() != decl_input_tys.len() + 1 {
+ return self.sig_of_closure_with_mismatched_number_of_arguments(
+ decl_input_tys,
+ decl_output_ty,
+ );
}
// Create a `PolyFnSig`. Note the oddity that late bound
@@ -728,9 +811,9 @@ impl<'db> InferenceContext<'_, 'db> {
self.interner().mk_fn_sig(
sig.inputs().iter().copied(),
sig.output(),
- sig.c_variadic,
+ sig.c_variadic(),
Safety::Safe,
- FnAbi::RustCall,
+ ExternAbi::RustCall,
)
});
@@ -746,9 +829,21 @@ impl<'db> InferenceContext<'_, 'db> {
// Along the way, it also writes out entries for types that the user
// wrote into our typeck results, which are then later used by the privacy
// check.
- match self.merge_supplied_sig_with_expectation(decl_inputs, decl_output, closure_sigs) {
+ match self.merge_supplied_sig_with_expectation(
+ closure_expr,
+ decl_inputs,
+ decl_input_tys,
+ decl_output_ty,
+ closure_sigs,
+ closure_kind,
+ ) {
Ok(infer_ok) => self.table.register_infer_ok(infer_ok),
- Err(_) => self.sig_of_closure_no_expectation(decl_inputs, decl_output),
+ Err(_) => self.sig_of_closure_no_expectation(
+ closure_expr,
+ decl_input_tys,
+ decl_output_ty,
+ closure_kind,
+ ),
}
}
@@ -767,15 +862,23 @@ impl<'db> InferenceContext<'_, 'db> {
/// strategy.
fn merge_supplied_sig_with_expectation(
&mut self,
- decl_inputs: &[Option<TypeRefId>],
- decl_output: Option<TypeRefId>,
+ closure_expr: ExprId,
+ decl_inputs: &[PatId],
+ decl_input_tys: &[Option<TypeRefId>],
+ decl_output_ty: Option<TypeRefId>,
mut expected_sigs: ClosureSignatures<'db>,
+ closure_kind: ClosureKind,
) -> InferResult<'db, ClosureSignatures<'db>> {
// Get the signature S that the user gave.
//
// (See comment on `sig_of_closure_with_expectation` for the
// meaning of these letters.)
- let supplied_sig = self.supplied_sig_of_closure(decl_inputs, decl_output);
+ let supplied_sig = self.supplied_sig_of_closure(
+ closure_expr,
+ decl_input_tys,
+ decl_output_ty,
+ closure_kind,
+ );
debug!(?supplied_sig);
@@ -796,25 +899,28 @@ impl<'db> InferenceContext<'_, 'db> {
self.table.commit_if_ok(|table| {
let mut all_obligations = PredicateObligations::new();
let supplied_sig = table.infer_ctxt.instantiate_binder_with_fresh_vars(
+ closure_expr.into(),
BoundRegionConversionTime::FnCall,
supplied_sig,
);
// The liberated version of this signature should be a subtype
// of the liberated form of the expectation.
- for (supplied_ty, expected_ty) in iter::zip(
- supplied_sig.inputs().iter().copied(),
+ for ((decl_input, supplied_ty), expected_ty) in iter::zip(
+ iter::zip(decl_inputs, supplied_sig.inputs().iter().copied()),
expected_sigs.liberated_sig.inputs().iter().copied(),
) {
// Check that E' = S'.
- let cause = ObligationCause::new();
+ let cause = ObligationCause::new(*decl_input);
let InferOk { value: (), obligations } =
table.infer_ctxt.at(&cause, table.param_env).eq(expected_ty, supplied_ty)?;
all_obligations.extend(obligations);
}
let supplied_output_ty = supplied_sig.output();
- let cause = ObligationCause::new();
+ let cause = ObligationCause::new(
+ decl_output_ty.map(Span::TypeRefId).unwrap_or(closure_expr.into()),
+ );
let InferOk { value: (), obligations } =
table
.infer_ctxt
@@ -822,18 +928,15 @@ impl<'db> InferenceContext<'_, 'db> {
.eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?;
all_obligations.extend(obligations);
- let inputs = supplied_sig
- .inputs()
- .iter()
- .copied()
- .map(|ty| table.infer_ctxt.resolve_vars_if_possible(ty));
+ let inputs =
+ supplied_sig.inputs().iter().copied().map(|ty| table.resolve_vars_if_possible(ty));
expected_sigs.liberated_sig = table.interner().mk_fn_sig(
inputs,
supplied_output_ty,
- expected_sigs.liberated_sig.c_variadic,
+ expected_sigs.liberated_sig.c_variadic(),
Safety::Safe,
- FnAbi::RustCall,
+ ExternAbi::RustCall,
);
Ok(InferOk { value: expected_sigs, obligations: all_obligations })
@@ -846,25 +949,54 @@ impl<'db> InferenceContext<'_, 'db> {
/// Also, record this closure signature for later.
fn supplied_sig_of_closure(
&mut self,
+ closure_expr: ExprId,
decl_inputs: &[Option<TypeRefId>],
decl_output: Option<TypeRefId>,
+ closure_kind: ClosureKind,
) -> PolyFnSig<'db> {
let interner = self.interner();
let supplied_return = match decl_output {
- Some(output) => {
- let output = self.make_body_ty(output);
- self.process_user_written_ty(output)
- }
- None => self.table.next_ty_var(),
+ Some(output) => self.make_body_ty(output),
+ None => match closure_kind {
+ // In the case of the async block that we create for a function body,
+ // we expect the return type of the block to match that of the enclosing
+ // function.
+ ClosureKind::Coroutine {
+ kind: CoroutineKind::Async,
+ source: CoroutineSource::Fn,
+ } => {
+ debug!("closure is async fn body");
+ self.deduce_future_output_from_obligations(closure_expr).unwrap_or_else(|| {
+ // AFAIK, deducing the future output
+ // always succeeds *except* in error cases
+ // like #65159. I'd like to return Error
+ // here, but I can't because I can't
+ // easily (and locally) prove that we
+ // *have* reported an
+ // error. --nikomatsakis
+ self.table.next_ty_var(closure_expr.into())
+ })
+ }
+ // All `gen {}` and `async gen {}` must return unit.
+ ClosureKind::Coroutine {
+ kind: CoroutineKind::Gen | CoroutineKind::AsyncGen,
+ ..
+ } => self.types.types.unit,
+
+ // For async blocks, we just fall back to `_` here.
+ // For closures/coroutines, we know nothing about the return
+ // type unless it was supplied.
+ ClosureKind::Coroutine { kind: CoroutineKind::Async, .. }
+ | ClosureKind::OldCoroutine(_)
+ | ClosureKind::Closure
+ | ClosureKind::CoroutineClosure(_) => self.table.next_ty_var(closure_expr.into()),
+ },
};
// First, convert the types that the user supplied (if any).
let supplied_arguments = decl_inputs.iter().map(|&input| match input {
- Some(input) => {
- let input = self.make_body_ty(input);
- self.process_user_written_ty(input)
- }
- None => self.table.next_ty_var(),
+ Some(input) => self.make_body_ty(input),
+ None => self.table.next_ty_var(closure_expr.into()),
});
Binder::dummy(interner.mk_fn_sig(
@@ -872,10 +1004,114 @@ impl<'db> InferenceContext<'_, 'db> {
supplied_return,
false,
Safety::Safe,
- FnAbi::RustCall,
+ ExternAbi::RustCall,
))
}
+ /// Invoked when we are translating the coroutine that results
+ /// from desugaring an `async fn`. Returns the "sugared" return
+ /// type of the `async fn` -- that is, the return type that the
+ /// user specified. The "desugared" return type is an `impl
+ /// Future<Output = T>`, so we do this by searching through the
+ /// obligations to extract the `T`.
+ #[instrument(skip(self), level = "debug", ret)]
+ fn deduce_future_output_from_obligations(&mut self, body_def_id: ExprId) -> Option<Ty<'db>> {
+ let ret_coercion = self
+ .return_coercion
+ .as_ref()
+ .unwrap_or_else(|| panic!("async fn coroutine outside of a fn"));
+
+ let ret_ty = ret_coercion.expected_ty();
+ let ret_ty = self.table.resolve_vars_with_obligations(ret_ty);
+
+ let get_future_output = |predicate: Predicate<'db>| {
+ // Search for a pending obligation like
+ //
+ // `<R as Future>::Output = T`
+ //
+ // where R is the return type we are expecting. This type `T`
+ // will be our output.
+ let bound_predicate = predicate.kind();
+ if let PredicateKind::Clause(ClauseKind::Projection(proj_predicate)) =
+ bound_predicate.skip_binder()
+ {
+ self.deduce_future_output_from_projection(bound_predicate.rebind(proj_predicate))
+ } else {
+ None
+ }
+ };
+
+ let output_ty = match ret_ty.kind() {
+ TyKind::Infer(InferTy::TyVar(ret_vid)) => self
+ .table
+ .obligations_for_self_ty(ret_vid)
+ .into_iter()
+ .find_map(|obligation| get_future_output(obligation.predicate))?,
+ TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => {
+ return Some(self.types.types.error);
+ }
+ TyKind::Alias(AliasTy { kind: AliasTyKind::Opaque { def_id }, args, .. }) => def_id
+ .0
+ .predicates(self.db)
+ .iter_instantiated_copied(self.interner(), &args)
+ .map(Unnormalized::skip_norm_wip)
+ .find_map(|p| get_future_output(p.as_predicate()))?,
+ TyKind::Error(_) => return Some(ret_ty),
+ _ => {
+ panic!("invalid async fn coroutine return type: {ret_ty:?}")
+ }
+ };
+
+ Some(output_ty)
+ }
+
+ /// Given a projection like
+ ///
+ /// `<X as Future>::Output = T`
+ ///
+ /// where `X` is some type that has no late-bound regions, returns
+ /// `Some(T)`. If the projection is for some other trait, returns
+ /// `None`.
+ fn deduce_future_output_from_projection(
+ &self,
+ predicate: PolyProjectionPredicate<'db>,
+ ) -> Option<Ty<'db>> {
+ debug!("deduce_future_output_from_projection(predicate={:?})", predicate);
+
+ // We do not expect any bound regions in our predicate, so
+ // skip past the bound vars.
+ let Some(predicate) = predicate.no_bound_vars() else {
+ debug!("deduce_future_output_from_projection: has late-bound regions");
+ return None;
+ };
+
+ // Check that this is a projection from the `Future` trait.
+ let trait_def_id = predicate.projection_term.trait_def_id(self.interner()).0;
+ if Some(trait_def_id) != self.lang_items.Future {
+ debug!("deduce_future_output_from_projection: not a future");
+ return None;
+ }
+
+ // The `Future` trait has only one associated item, `Output`,
+ // so check that this is what we see.
+ let output_assoc_item = self.lang_items.FutureOutput;
+ if output_assoc_item.map(Into::into) != Some(predicate.def_id().0) {
+ panic!(
+ "projecting associated item `{:?}` from future, which is not Output `{:?}`",
+ predicate.projection_term.kind(self.interner()),
+ output_assoc_item,
+ );
+ }
+
+ // Extract the type from the projection. Note that there can
+ // be no bound variables in this type because the "self type"
+ // does not have any regions in it.
+ let output_ty = self.resolve_vars_if_possible(predicate.term);
+ debug!("deduce_future_output_from_projection: output_ty={:?}", output_ty);
+ // This is a projection on a Fn trait so will always be a type.
+ Some(output_ty.expect_type())
+ }
+
/// Converts the types that the user supplied, in case that doing
/// so should yield an error, but returns back a signature where
/// all parameters are of type `ty::Error`.
@@ -903,7 +1139,7 @@ impl<'db> InferenceContext<'_, 'db> {
err_ty,
false,
Safety::Safe,
- FnAbi::RustCall,
+ ExternAbi::RustCall,
));
debug!("supplied_sig_of_closure: result={:?}", result);
diff --git a/crates/hir-ty/src/infer/closure/analysis.rs b/crates/hir-ty/src/infer/closure/analysis.rs
index 668d7496cd..5ea43bc03c 100644
--- a/crates/hir-ty/src/infer/closure/analysis.rs
+++ b/crates/hir-ty/src/infer/closure/analysis.rs
@@ -41,10 +41,11 @@ use hir_def::{
resolver::ValueNs,
};
use macros::{TypeFoldable, TypeVisitable};
+use rustc_abi::ExternAbi;
use rustc_ast_ir::Mutability;
use rustc_hash::{FxBuildHasher, FxHashMap};
use rustc_type_ir::{
- BoundVar, ClosureKind, TypeVisitableExt as _,
+ BoundVar, ClosureKind,
inherent::{AdtDef as _, GenericArgs as _, IntoKind as _, Ty as _},
};
use smallvec::{SmallVec, smallvec};
@@ -52,7 +53,7 @@ use span::Edition;
use tracing::{debug, instrument};
use crate::{
- FnAbi,
+ Span,
infer::{
CaptureInfo, CaptureSourceStack, CapturedPlace, InferenceContext, UpvarCapture,
closure::analysis::expr_use_visitor::{
@@ -195,7 +196,7 @@ type InferredCaptureInformation = Vec<(Place, CaptureInfo)>;
impl<'a, 'db> InferenceContext<'a, 'db> {
pub(crate) fn closure_analyze(&mut self) {
- let upvars = crate::upvars::upvars_mentioned(self.db, self.owner)
+ let upvars = crate::upvars::upvars_mentioned(self.db, self.store_owner)
.unwrap_or(const { &FxHashMap::with_hasher(FxBuildHasher) });
for root_expr in self.store.expr_roots() {
self.analyze_closures_in_expr(root_expr, upvars);
@@ -284,7 +285,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// coroutine-closures that are `move` since otherwise they themselves will
// be borrowing from the outer environment, so there's no self-borrows occurring.
if let UpvarArgs::Coroutine(..) = args
- && let hir_def::hir::ClosureKind::AsyncBlock { source: CoroutineSource::Closure } =
+ && let hir_def::hir::ClosureKind::Coroutine { source: CoroutineSource::Closure, .. } =
closure_kind
&& let parent_hir_id = ExpressionStore::closure_for_coroutine(closure_expr_id)
&& let parent_ty = self.result.expr_ty(parent_hir_id)
@@ -310,8 +311,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
//
// FIXME(async_closures): This could be cleaned up. It's a bit janky that we're just
// moving all of the `LocalSource::AsyncFn` locals here.
- if let hir_def::hir::ClosureKind::AsyncBlock {
+ if let hir_def::hir::ClosureKind::Coroutine {
source: CoroutineSource::Fn | CoroutineSource::Closure,
+ ..
} = closure_kind
{
let Expr::Block { statements, .. } = &self.store[body] else {
@@ -328,7 +330,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
let Expr::Path(path) = &self.store[init] else {
panic!();
};
- let update_guard = self.resolver.update_to_inner_scope(self.db, self.owner, init);
+ let update_guard =
+ self.resolver.update_to_inner_scope(self.db, self.store_owner, init);
let Some(ValueNs::LocalBinding(local_id)) =
self.resolver.resolve_path_in_value_ns_fully(
self.db,
@@ -402,9 +405,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// For coroutine-closures, we additionally must compute the
// `coroutine_captures_by_ref_ty` type, which is used to generate the by-ref
// version of the coroutine-closure's output coroutine.
- if let UpvarArgs::CoroutineClosure(args) = args
- && !args.references_error()
- {
+ if let UpvarArgs::CoroutineClosure(args) = args {
let closure_env_region: Region<'_> = Region::new_bound(
self.interner(),
rustc_type_ir::INNERMOST,
@@ -459,7 +460,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
tupled_upvars_ty_for_borrow,
false,
Safety::Safe,
- FnAbi::Rust,
+ ExternAbi::Rust,
),
self.types.coroutine_captures_by_ref_bound_var_kinds,
),
@@ -506,7 +507,11 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// Build a tuple (U0..Un) of the final upvar types U0..Un
// and unify the upvar tuple type in the closure with it:
let final_tupled_upvars_type = Ty::new_tup(self.interner(), &final_upvar_tys);
- self.demand_suptype(args.tupled_upvars_ty(), final_tupled_upvars_type);
+ _ = self.demand_suptype(
+ closure_expr_id.into(),
+ args.tupled_upvars_ty(),
+ final_tupled_upvars_type,
+ );
let fake_reads = delegate.fake_reads;
@@ -737,7 +742,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
};
let Some(min_cap_list) = root_var_min_capture_list.get_mut(&var_hir_id) else {
- let mutability = self.determine_capture_mutability(&place);
+ let mutability = self.determine_capture_mutability(closure_def_id, &place);
let min_cap_list = vec![CapturedPlace { place, info: capture_info, mutability }];
root_var_min_capture_list.insert(var_hir_id, min_cap_list);
continue;
@@ -846,7 +851,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// Only need to insert when we don't have an ancestor in the existing min capture list
if !ancestor_found {
- let mutability = self.determine_capture_mutability(&place);
+ let mutability = self.determine_capture_mutability(closure_def_id, &place);
let captured_place =
CapturedPlace { place, info: updated_capture_info, mutability };
min_cap_list.push(captured_place);
@@ -915,12 +920,12 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
self.result.closures_data.insert(closure_def_id, closure_data);
}
- fn normalize_capture_place(&self, place: Place) -> Place {
- let mut place = self.infcx().resolve_vars_if_possible(place);
+ fn normalize_capture_place(&mut self, span: Span, place: Place) -> Place {
+ let place = self.infcx().resolve_vars_if_possible(place);
// In the new solver, types in HIR `Place`s can contain unnormalized aliases,
// which can ICE later (e.g. when projecting fields for diagnostics).
- let cause = ObligationCause::misc();
+ let cause = ObligationCause::new(span);
let at = self.table.at(&cause);
match normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
at,
@@ -940,11 +945,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
}
normalized
}
- Err(_errors) => {
- place.base_ty = self.types.types.error.store();
- for proj in &mut place.projections {
- proj.ty = self.types.types.error.store();
- }
+ Err(errors) => {
+ self.table.trait_errors.extend(errors);
place
}
}
@@ -997,7 +999,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
}
}
- fn place_for_root_variable(&self, closure_def_id: ExprId, var_hir_id: BindingId) -> Place {
+ fn place_for_root_variable(&mut self, closure_def_id: ExprId, var_hir_id: BindingId) -> Place {
let place = Place {
base_ty: self.result.binding_ty(var_hir_id).store(),
base: PlaceBase::Upvar { closure: closure_def_id, var_id: var_hir_id },
@@ -1006,13 +1008,13 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// Normalize eagerly when inserting into `capture_information`, so all downstream
// capture analysis can assume a normalized `Place`.
- self.normalize_capture_place(place)
+ self.normalize_capture_place(var_hir_id.into(), place)
}
/// A captured place is mutable if
/// 1. Projections don't include a Deref of an immut-borrow, **and**
/// 2. PlaceBase is mut or projections include a Deref of a mut-borrow.
- fn determine_capture_mutability(&mut self, place: &Place) -> Mutability {
+ fn determine_capture_mutability(&mut self, closure_expr: ExprId, place: &Place) -> Mutability {
let var_hir_id = match place.base {
PlaceBase::Upvar { var_id, .. } => var_id,
_ => unreachable!(),
@@ -1025,7 +1027,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
};
for pointer_ty in place.deref_tys() {
- match self.table.structurally_resolve_type(pointer_ty).kind() {
+ match self.structurally_resolve_type(closure_expr.into(), pointer_ty).kind() {
// We don't capture derefs of raw ptrs
TyKind::RawPtr(_, _) => unreachable!(),
@@ -1127,7 +1129,7 @@ fn restrict_repr_packed_field_ref_capture(
// Return true for fields of packed structs.
match p.kind {
ProjectionKind::Field { .. } => match ty.kind() {
- TyKind::Adt(def, _) if def.repr().packed() => {
+ TyKind::Adt(def, _) if def.is_packed() => {
// We stop here regardless of field alignment. Field alignment can change as
// types change, including the types of private fields in other crates, and that
// shouldn't affect how we compute our captures.
@@ -1210,7 +1212,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind {
let mut dummy_capture_info =
CaptureInfo { sources: SmallVec::new(), capture_kind: dummy_capture_kind };
- let place = ctx.normalize_capture_place(place_with_id.place.clone());
+ let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone());
let place = restrict_capture_precision(place, &mut dummy_capture_info);
@@ -1226,7 +1228,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind {
};
assert_eq!(self.closure_def_id, upvar_closure);
- let place = ctx.normalize_capture_place(place_with_id.place.clone());
+ let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone());
self.capture_information.push((
place,
@@ -1241,7 +1243,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind {
};
assert_eq!(self.closure_def_id, upvar_closure);
- let place = ctx.normalize_capture_place(place_with_id.place.clone());
+ let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone());
self.capture_information.push((
place,
@@ -1266,7 +1268,7 @@ impl<'db> euv::Delegate<'db> for InferBorrowKind {
let mut capture_info =
CaptureInfo { sources: place_with_id.origins.iter().cloned().collect(), capture_kind };
- let place = ctx.normalize_capture_place(place_with_id.place.clone());
+ let place = ctx.normalize_capture_place(place_with_id.span(), place_with_id.place.clone());
// We only want repr packed restriction to be applied to reading references into a packed
// struct, and not when the data is being moved. Therefore we call this method here instead
diff --git a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
index 099fa18168..deafff6b43 100644
--- a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
+++ b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
@@ -14,20 +14,21 @@ use hir_def::{
},
resolver::ValueNs,
};
-use rustc_ast_ir::{try_visit, visit::VisitorResult};
-use rustc_type_ir::{
- FallibleTypeFolder, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor,
- inherent::{AdtDef, IntoKind, Ty as _},
-};
+use macros::{TypeFoldable, TypeVisitable};
+use rustc_type_ir::inherent::{IntoKind, Ty as _};
use smallvec::{SmallVec, smallvec};
+use stdx::impl_from;
use syntax::ast::{BinaryOp, UnaryOp};
-use tracing::{debug, instrument};
+use tracing::{debug, instrument, trace};
use crate::{
- Adjust, Adjustment, AutoBorrow, BindingMode,
- infer::{CaptureSourceStack, InferenceContext, UpvarCapture, closure::analysis::BorrowKind},
+ Adjust, Adjustment, AutoBorrow, Span,
+ infer::{
+ ByRef, CaptureSourceStack, DerefPatBorrowMode, InferenceContext, PatAdjust, PatAdjustment,
+ UpvarCapture, closure::analysis::BorrowKind,
+ },
method_resolution::CandidateId,
- next_solver::{DbInterner, ErrorGuaranteed, StoredTy, Ty, TyKind},
+ next_solver::{ErrorGuaranteed, StoredTy, Ty, TyKind},
upvars::UpvarsRef,
utils::EnumerateAndAdjustIterator,
};
@@ -69,12 +70,13 @@ pub enum PlaceBase {
Upvar { closure: ExprId, var_id: BindingId },
}
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
pub struct Projection {
/// Type after the projection is applied.
pub ty: StoredTy,
/// Defines the kind of access made by the projection.
+ #[type_visitable(ignore)]
pub kind: ProjectionKind,
}
@@ -82,61 +84,17 @@ pub struct Projection {
/// always correspond to a syntactic place expression. For example, when
/// processing a pattern, a `Place` can be used to refer to the sub-value
/// currently being inspected.
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
pub struct Place {
/// The type of the `PlaceBase`
pub base_ty: StoredTy,
/// The "outermost" place that holds this value.
+ #[type_visitable(ignore)]
pub base: PlaceBase,
/// How this place is derived from the base place.
pub projections: Vec<Projection>,
}
-impl<'db> TypeVisitable<DbInterner<'db>> for Place {
- fn visit_with<V: TypeVisitor<DbInterner<'db>>>(&self, visitor: &mut V) -> V::Result {
- let Self { base_ty, base: _, projections } = self;
- try_visit!(base_ty.as_ref().visit_with(visitor));
- for proj in projections {
- let Projection { ty, kind: _ } = proj;
- try_visit!(ty.as_ref().visit_with(visitor));
- }
- V::Result::output()
- }
-}
-
-impl<'db> TypeFoldable<DbInterner<'db>> for Place {
- fn try_fold_with<F: FallibleTypeFolder<DbInterner<'db>>>(
- self,
- folder: &mut F,
- ) -> Result<Self, F::Error> {
- let Self { base_ty, base, projections } = self;
- let base_ty = base_ty.as_ref().try_fold_with(folder)?.store();
- let projections = projections
- .into_iter()
- .map(|proj| {
- let Projection { ty, kind } = proj;
- let ty = ty.as_ref().try_fold_with(folder)?.store();
- Ok(Projection { ty, kind })
- })
- .collect::<Result<_, _>>()?;
- Ok(Self { base_ty, base, projections })
- }
-
- fn fold_with<F: TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
- let Self { base_ty, base, projections } = self;
- let base_ty = base_ty.as_ref().fold_with(folder).store();
- let projections = projections
- .into_iter()
- .map(|proj| {
- let Projection { ty, kind } = proj;
- let ty = ty.as_ref().fold_with(folder).store();
- Projection { ty, kind }
- })
- .collect();
- Self { base_ty, base, projections }
- }
-}
-
impl Place {
/// Returns an iterator of the types that have to be dereferenced to access
/// the `Place`.
@@ -214,6 +172,13 @@ impl PlaceWithOrigin {
origin_stack.push(origin);
}
}
+
+ pub(crate) fn span(&self) -> Span {
+ match self.origins.first() {
+ Some(origin) => origin.final_source().into(),
+ None => Span::Dummy,
+ }
+ }
}
/// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists.
@@ -446,14 +411,6 @@ pub(crate) struct ExprUseVisitor<'a, 'b, 'db, D: Delegate<'db>> {
upvars: UpvarsRef<'db>,
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum PatWalkMode {
- /// `let`, `match`.
- Declaration,
- /// Destructuring assignment.
- Assignment,
-}
-
impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
/// Creates the ExprUseVisitor, configuring it with the various options provided:
///
@@ -477,7 +434,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
let param_place = self.cat_rvalue(param.into(), param_ty);
self.fake_read_scrutinee(param_place.clone(), false);
- self.walk_pat(param_place, param, false, PatWalkMode::Declaration)?;
+ self.walk_pat(param_place, param, false)?;
}
self.consume_expr(body)?;
@@ -518,7 +475,6 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
Ok(())
}
- // FIXME: It's suspicious that this is public; clippy should probably use `walk_expr`.
#[instrument(skip(self), level = "debug")]
pub(crate) fn consume_expr(&mut self, expr: ExprId) -> Result {
let place_with_id = self.cat_expr(expr)?;
@@ -700,8 +656,8 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
self.walk_expr(value)?;
let expr_place = self.cat_expr(value)?;
let update_guard =
- self.cx.resolver.update_to_inner_scope(self.cx.db, self.cx.owner, expr);
- self.walk_pat(expr_place, target, false, PatWalkMode::Assignment)?;
+ self.cx.resolver.update_to_inner_scope(self.cx.db, self.cx.store_owner, expr);
+ self.walk_pat(expr_place, target, false)?;
self.cx.resolver.reset_to_guard(update_guard);
}
@@ -784,7 +740,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
let expr_place = self.cat_expr(expr)?;
f(self)?;
self.fake_read_scrutinee(expr_place.clone(), els.is_some());
- self.walk_pat(expr_place, pat, false, PatWalkMode::Declaration)?;
+ self.walk_pat(expr_place, pat, false)?;
if let Some(els) = els {
self.walk_expr(els)?;
}
@@ -803,9 +759,9 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
// Select just those fields of the `with`
// expression that will actually be used
- match self.cx.table.structurally_resolve_type(with_place.place.ty()).kind() {
+ match self.cx.structurally_resolve_type(with_expr.into(), with_place.place.ty()).kind() {
TyKind::Adt(adt, args) if adt.is_struct() => {
- let AdtId::StructId(adt) = adt.def_id().0 else { unreachable!() };
+ let AdtId::StructId(adt) = adt.def_id() else { unreachable!() };
let adt_fields = VariantId::from(adt).fields(self.cx.db).fields();
let adt_field_types = self.cx.db.field_types(adt.into());
// Consume those fields of the with expression that are needed.
@@ -815,7 +771,10 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
let field_place = self.cat_projection(
with_expr.into(),
with_place.clone(),
- adt_field_types[f_index].get().instantiate(self.cx.interner(), args),
+ adt_field_types[f_index]
+ .get()
+ .instantiate(self.cx.interner(), args)
+ .skip_norm_wip(),
ProjectionKind::Field {
field_idx: f_index.into_raw().into_u32(),
variant_idx: 0,
@@ -840,6 +799,11 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
self.cx.result.expr_adjustment(expr).unwrap_or_default().into()
}
+ fn pat_adjustments(&self, pat: PatId) -> SmallVec<[PatAdjustment; 5]> {
+ // Due to borrowck problems, we cannot borrow the adjustments, unfortunately.
+ self.cx.result.pat_adjustment(pat).unwrap_or_default().into()
+ }
+
/// Invoke the appropriate delegate calls for anything that gets
/// consumed or borrowed as part of the automatic adjustment
/// process.
@@ -896,7 +860,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
}
fn walk_arm(&mut self, discr_place: PlaceWithOrigin, arm: &MatchArm) -> Result {
- self.walk_pat(discr_place, arm.pat, arm.guard.is_some(), PatWalkMode::Declaration)?;
+ self.walk_pat(discr_place, arm.pat, arm.guard.is_some())?;
if let Some(e) = arm.guard {
self.consume_expr(e)?;
@@ -920,14 +884,33 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
/// Do note that discrepancies like these do still create obscure corners
/// in the semantics of the language, and should be avoided if possible.
#[instrument(skip(self), level = "debug")]
- fn walk_pat(
- &mut self,
- discr_place: PlaceWithOrigin,
- pat: PatId,
- has_guard: bool,
- mode: PatWalkMode,
- ) -> Result {
+ fn walk_pat(&mut self, discr_place: PlaceWithOrigin, pat: PatId, has_guard: bool) -> Result {
self.cat_pattern(discr_place.clone(), pat, &mut |this, place, pat| {
+ let walk_deref_pat = |this: &mut Self, subpattern: PatId, place: PlaceWithOrigin| {
+ // A deref pattern is a bit special: the binding mode of its inner bindings
+ // determines whether to borrow *at the level of the deref pattern* rather than
+ // borrowing the bound place (since that inner place is inside the temporary that
+ // stores the result of calling `deref()`/`deref_mut()` so can't be captured).
+ // Deref patterns on boxes don't borrow, so we ignore them here.
+ // HACK: this could be a fake pattern corresponding to a deref inserted by match
+ // ergonomics, in which case `pat.hir_id` will be the id of the subpattern.
+ if let DerefPatBorrowMode::Borrow(mutability) =
+ this.cx.deref_pat_borrow_mode(place.place.ty(), subpattern)
+ {
+ let bk = BorrowKind::from_mutbl(mutability);
+ this.delegate.borrow(place, bk, this.cx);
+ }
+ };
+
+ let pat = match pat {
+ CatPatternPat::PatId(pat) => pat,
+ CatPatternPat::DerefPat { inner } => {
+ debug!("walk_pat: Deref {{ inner: {:?} }}", inner);
+ walk_deref_pat(this, inner, place);
+ return Ok(());
+ }
+ };
+
debug!("walk_pat: pat.kind={:?}", this.cx.store[pat]);
let read_discriminant = {
let place = place.clone();
@@ -961,17 +944,18 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
// In a cases of pattern like `let pat = upvar`, don't use the span
// of the pattern, as this just looks confusing, instead use the span
// of the discriminant.
- match this.cx.result.binding_mode(pat) {
- Some(BindingMode::Ref(m)) => {
+ match this.cx.result.binding_mode(pat).ok_or(ErrorGuaranteed)?.0 {
+ ByRef::Yes(m) => {
let bk = BorrowKind::from_mutbl(m);
this.delegate.borrow(place, bk, this.cx);
}
- None | Some(BindingMode::Move) => {
+ ByRef::No => {
debug!("walk_pat binding consuming pat");
this.consume_or_copy(place);
}
}
}
+ Pat::Deref { inner: subpattern } => walk_deref_pat(this, subpattern, place),
Pat::Path(ref path) => {
// A `Path` pattern is just a name like `Foo`. This is either a
// named constant or else it refers to an ADT variant
@@ -987,20 +971,14 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
this.cx.store.pat_path_hygiene(pat),
);
let is_normal_const = matches!(resolution, Some(ValueNs::ConstId(_)));
- if mode == PatWalkMode::Assignment
- && let Some(ValueNs::LocalBinding(local)) = resolution
- {
- let pat_ty = this.pat_ty(pat)?;
- let place = this.cat_local(pat.into(), pat_ty, local)?;
- this.delegate.mutate(place, this.cx);
- } else if is_assoc_const || is_normal_const {
+ if is_assoc_const || is_normal_const {
// Named constants have to be equated with the value
// being matched, so that's a read of the value being matched.
//
// FIXME: Does the MIR code skip this read when matching on a ZST?
// If so, we can also skip it here.
read_discriminant(this);
- } else if this.is_multivariant_adt(place.place.ty()) {
+ } else if this.is_multivariant_adt(pat.into(), place.place.ty()) {
// Otherwise, this is a struct/enum variant, and so it's
// only a read if we need to read the discriminant.
read_discriminant(this);
@@ -1019,7 +997,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
read_discriminant(this);
}
Pat::Record { .. } | Pat::TupleStruct { .. } => {
- if this.is_multivariant_adt(place.place.ty()) {
+ if this.is_multivariant_adt(pat.into(), place.place.ty()) {
read_discriminant(this);
}
}
@@ -1035,7 +1013,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
read_discriminant(this);
}
}
- Pat::Expr(expr) if mode == PatWalkMode::Assignment => {
+ Pat::Expr(expr) => {
// Destructuring assignment.
this.mutate_expr(expr)?;
}
@@ -1044,13 +1022,13 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
| Pat::Ref { .. }
| Pat::Tuple { .. }
| Pat::Wild
- | Pat::Missing => {
+ | Pat::Missing
+ | Pat::Rest => {
// If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses
// are made later as these patterns contains subpatterns.
// If the PatKind is Missing, Wild or Err, any relevant accesses are made when processing
// the other patterns that are part of the match
}
- Pat::Expr(_) => {}
}
Ok(())
@@ -1167,6 +1145,13 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
}
}
+#[derive(Debug, Clone, Copy)]
+enum CatPatternPat {
+ PatId(PatId),
+ DerefPat { inner: PatId },
+}
+impl_from!(PatId for CatPatternPat);
+
/// The job of the methods whose name starts with `cat_` is to analyze
/// expressions and construct the corresponding [`Place`]s. The `cat`
/// stands for "categorize", this is a leftover from long ago when
@@ -1196,10 +1181,6 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
self.node_ty(expr.into())
}
- fn pat_ty(&mut self, pat: PatId) -> Result<Ty<'db>> {
- self.node_ty(pat.into())
- }
-
fn expr_ty_adjusted(&mut self, expr: ExprId) -> Result<Ty<'db>> {
self.expect_and_resolve_type(self.cx.result.type_of_expr_with_adjust(expr))
}
@@ -1219,18 +1200,52 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
// that these are never attached to binding patterns, so
// actually this is somewhat "disjoint" from the code below
// that aims to account for `ref x`.
- if let Some(vec) = self.cx.result.pat_adjustments.get(&pat)
- && let Some(first_adjust) = vec.first()
+ if let Some(vec) = self.cx.result.pat_adjustment(pat) {
+ if let Some(first_adjust) = vec.first() {
+ debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust);
+ return Ok(first_adjust.source.as_ref());
+ }
+ } else if let Pat::Ref { pat: subpat, .. } = self.cx.store[pat]
+ && self.cx.result.is_skipped_ref_pat(pat)
{
- debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust);
- return Ok(first_adjust.as_ref());
+ return self.pat_ty_adjusted(subpat);
}
+
self.pat_ty_unadjusted(pat)
}
/// Like [`Self::pat_ty_adjusted`], but ignores implicit `&` patterns.
fn pat_ty_unadjusted(&mut self, pat: PatId) -> Result<Ty<'db>> {
- Ok(self.cx.result.pat_ty(pat))
+ let base_ty = self.node_ty(pat.into())?;
+ trace!(?base_ty);
+
+ // This code detects whether we are looking at a `ref x`,
+ // and if so, figures out what the type *being borrowed* is.
+ match self.cx.store[pat] {
+ Pat::Bind { .. } => {
+ let bm = self.cx.result.binding_mode(pat).ok_or(ErrorGuaranteed)?;
+
+ if let ByRef::Yes(_) = bm.0 {
+ // a bind-by-ref means that the base_ty will be the type of the ident itself,
+ // but what we want here is the type of the underlying value being borrowed.
+ // So peel off one-level, turning the &T into T.
+ match self
+ .cx
+ .structurally_resolve_type(pat.into(), base_ty)
+ .builtin_deref(false)
+ {
+ Some(ty) => Ok(ty),
+ None => {
+ debug!("By-ref binding of non-derefable type: {base_ty:?}");
+ Err(ErrorGuaranteed)
+ }
+ }
+ } else {
+ Ok(base_ty)
+ }
+ }
+ _ => Ok(base_ty),
+ }
}
fn cat_expr(&mut self, expr: ExprId) -> Result<PlaceWithOrigin> {
@@ -1335,7 +1350,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
Expr::Path(ref path) => {
let resolver_guard =
- self.cx.resolver.update_to_inner_scope(self.cx.db, self.cx.owner, expr);
+ self.cx.resolver.update_to_inner_scope(self.cx.db, self.cx.store_owner, expr);
let resolution = self.cx.resolver.resolve_path_in_value_ns_fully(
self.cx.db,
path,
@@ -1423,7 +1438,8 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
let place_ty = self.expr_ty(expr)?;
let base_ty = self.expr_ty_adjusted(base)?;
- let TyKind::Ref(region, _, mutbl) = self.cx.table.structurally_resolve_type(base_ty).kind()
+ let TyKind::Ref(region, _, mutbl) =
+ self.cx.structurally_resolve_type(base.into(), base_ty).kind()
else {
return Err(ErrorGuaranteed);
};
@@ -1440,7 +1456,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
) -> Result<PlaceWithOrigin> {
let base_curr_ty = base_place.place.ty();
let Some(deref_ty) =
- self.cx.table.structurally_resolve_type(base_curr_ty).builtin_deref(true)
+ self.cx.structurally_resolve_type(node, base_curr_ty).builtin_deref(true)
else {
debug!("explicit deref of non-derefable type: {:?}", base_curr_ty);
return Err(ErrorGuaranteed);
@@ -1467,7 +1483,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
/// Here `pat_hir_id` is the ExprId of the pattern itself.
fn total_fields_in_tuple(&mut self, pat_id: PatId) -> usize {
let ty = self.cx.result.pat_ty(pat_id);
- match self.cx.table.structurally_resolve_type(ty).kind() {
+ match self.cx.structurally_resolve_type(pat_id.into(), ty).kind() {
TyKind::Tuple(args) => args.len(),
_ => panic!("tuple pattern not applied to a tuple"),
}
@@ -1486,7 +1502,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
op: &mut F,
) -> Result
where
- F: FnMut(&mut Self, PlaceWithOrigin, PatId) -> Result,
+ F: FnMut(&mut Self, PlaceWithOrigin, CatPatternPat) -> Result,
{
// If (pattern) adjustments are active for this pattern, adjust the `PlaceWithId` correspondingly.
// `PlaceWithId`s are constructed differently from patterns. For example, in
@@ -1520,11 +1536,26 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
// Then we see that to get the same result, we must start with
// `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)`
// and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`.
- let adjustments_len = self.cx.result.pat_adjustment(pat).map_or(0, |it| it.len());
- for _ in 0..adjustments_len {
+ let adjustments = self.pat_adjustments(pat);
+ let mut adjusts = adjustments.iter().peekable();
+ while let Some(adjust) = adjusts.next() {
debug!("applying adjustment to place_with_id={:?}", place_with_id);
- // FIXME: We need to adjust this once we implement deref patterns (or pin ergonomics, for that matter).
- place_with_id = self.cat_deref(pat.into(), place_with_id)?;
+ place_with_id = match adjust.kind {
+ PatAdjust::BuiltinDeref => self.cat_deref(pat.into(), place_with_id)?,
+ PatAdjust::OverloadedDeref => {
+ // This adjustment corresponds to an overloaded deref; unless it's on a box, it
+ // borrows the scrutinee to call `Deref::deref` or `DerefMut::deref_mut`. Invoke
+ // the callback before setting `place_with_id` to the temporary storing the
+ // result of the deref.
+ op(self, place_with_id.clone(), CatPatternPat::DerefPat { inner: pat })?;
+ let target_ty = match adjusts.peek() {
+ Some(next_adjust) => next_adjust.source.as_ref(),
+ // At the end of the deref chain, we get `pat`'s scrutinee.
+ None => self.pat_ty_unadjusted(pat)?,
+ };
+ self.pat_deref_place(pat.into(), place_with_id, pat, target_ty)?
+ }
+ };
}
let place_with_id = place_with_id; // lose mutability
debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);
@@ -1538,7 +1569,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
// `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)`
// result in the place `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with
// that (where the `ref` on `x` is implied).
- op(self, place_with_id.clone(), pat)?;
+ op(self, place_with_id.clone(), pat.into())?;
match self.cx.store[pat] {
Pat::Tuple { args: ref subpats, ellipsis: dots_pos } => {
@@ -1618,16 +1649,20 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
let subplace = self.cat_deref(pat.into(), place_with_id)?;
self.cat_pattern(subplace, subpat, op)?;
}
+ Pat::Deref { inner: subpat } => {
+ let ty = self.pat_ty_adjusted(subpat)?;
+ let place = self.pat_deref_place(pat.into(), place_with_id, subpat, ty)?;
+ self.cat_pattern(place, subpat, op)?;
+ }
Pat::Slice { prefix: ref before, slice, suffix: ref after } => {
let Some(element_ty) = self
.cx
- .table
- .structurally_resolve_type(place_with_id.place.ty())
+ .structurally_resolve_type(pat.into(), place_with_id.place.ty())
.builtin_index()
else {
debug!("explicit index of non-indexable type {:?}", place_with_id);
- panic!("explicit index of non-indexable type");
+ return Err(ErrorGuaranteed);
};
let elt_place = self.cat_projection(
pat.into(),
@@ -1660,6 +1695,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
| Pat::ConstBlock(..)
| Pat::Range { .. }
| Pat::Missing
+ | Pat::Rest
| Pat::Wild => {
// always ok
}
@@ -1668,6 +1704,29 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
Ok(())
}
+ /// Represents the place matched on by a deref pattern's interior.
+ fn pat_deref_place(
+ &mut self,
+ node: ExprOrPatId,
+ base_place: PlaceWithOrigin,
+ inner: PatId,
+ target_ty: Ty<'db>,
+ ) -> Result<PlaceWithOrigin> {
+ match self.cx.deref_pat_borrow_mode(base_place.place.ty(), inner) {
+ // Deref patterns on boxes are lowered using a built-in deref.
+ DerefPatBorrowMode::Box => self.cat_deref(node, base_place),
+ // For other types, we create a temporary to match on.
+ DerefPatBorrowMode::Borrow(mutability) => {
+ let re_erased = self.cx.types.regions.erased;
+ let ty = Ty::new_ref(self.cx.interner(), re_erased, target_ty, mutability);
+ // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ...
+ let base = self.cat_rvalue(node, ty);
+ // ... and the inner pattern matches on the place behind that reference.
+ self.cat_deref(node, base)
+ }
+ }
+ }
+
/// Checks whether a type has multiple variants, and therefore, whether a
/// read of the discriminant might be necessary. Note that the actual MIR
/// builder code does a more specific check, filtering out variants that
@@ -1682,13 +1741,13 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
/// FIXME(never_patterns): update this comment once the aforementioned MIR builder
/// code is changed to be insensitive to inhhabitedness.
#[instrument(skip(self), level = "debug")]
- fn is_multivariant_adt(&mut self, ty: Ty<'db>) -> bool {
- if let TyKind::Adt(def, _) = self.cx.table.structurally_resolve_type(ty).kind() {
+ fn is_multivariant_adt(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> bool {
+ if let TyKind::Adt(def, _) = self.cx.structurally_resolve_type(node, ty).kind() {
// Note that if a non-exhaustive SingleVariant is defined in another crate, we need
// to assume that more cases will be added to the variant in the future. This mean
// that we should handle non-exhaustive SingleVariant the same way we would handle
// a MultiVariant.
- match def.def_id().0 {
+ match def.def_id() {
AdtId::StructId(_) | AdtId::UnionId(_) => false,
AdtId::EnumId(did) => {
let has_foreign_non_exhaustive = || {
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 732a583047..343919f5ba 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -38,10 +38,7 @@
use std::ops::ControlFlow;
use hir_def::{
- CallableDefId, TraitId,
- attrs::AttrFlags,
- hir::{ExprId, ExprOrPatId},
- signatures::FunctionSignature,
+ CallableDefId, TraitId, attrs::AttrFlags, hir::ExprId, signatures::FunctionSignature,
};
use rustc_ast_ir::Mutability;
use rustc_type_ir::{
@@ -55,12 +52,10 @@ use smallvec::SmallVec;
use tracing::{debug, instrument};
use crate::{
- Adjust, Adjustment, AutoBorrow, ParamEnvAndCrate, PointerCast, TargetFeatures,
+ Adjust, Adjustment, AutoBorrow, ParamEnvAndCrate, PointerCast, Span, TargetFeatures,
autoderef::Autoderef,
db::{HirDatabase, InternedClosure, InternedClosureId},
- infer::{
- AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch, expr::ExprIsRead,
- },
+ infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext, expr::ExprIsRead},
next_solver::{
Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, CallableIdWrapper,
Canonical, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs,
@@ -155,10 +150,8 @@ where
let snapshot = self.infcx().start_snapshot();
let result = f(self);
match result {
- Ok(_) => {}
- Err(_) => {
- self.infcx().rollback_to(snapshot);
- }
+ Ok(_) => self.infcx().commit_from(snapshot),
+ Err(_) => self.infcx().rollback_to(snapshot),
}
result
}
@@ -321,14 +314,15 @@ where
if b.is_infer() {
// Two unresolved type variables: create a `Coerce` predicate.
- let target_ty = if self.use_lub { self.infcx().next_ty_var() } else { b };
+ let target_ty =
+ if self.use_lub { self.infcx().next_ty_var(self.cause.span()) } else { b };
let mut obligations = PredicateObligations::with_capacity(2);
for &source_ty in &[a, b] {
if source_ty != target_ty {
obligations.push(Obligation::new(
self.interner(),
- self.cause.clone(),
+ self.cause,
self.param_env(),
Binder::dummy(PredicateKind::Coerce(CoercePredicate {
a: source_ty,
@@ -381,7 +375,8 @@ where
let mut first_error = None;
let mut r_borrow_var = None;
- let mut autoderef = Autoderef::new_with_tracking(self.infcx(), self.param_env(), a);
+ let mut autoderef =
+ Autoderef::new_with_tracking(self.infcx(), self.param_env(), a, self.cause.span());
let mut found = None;
for (referent_ty, autoderefs) in autoderef.by_ref() {
@@ -468,7 +463,7 @@ where
} else {
if r_borrow_var.is_none() {
// create var lazily, at most once
- let r = self.infcx().next_region_var();
+ let r = self.infcx().next_region_var(self.cause.span());
r_borrow_var = Some(r); // [4] above
}
r_borrow_var.unwrap()
@@ -629,7 +624,7 @@ where
(TyKind::Ref(_, ty_a, mutbl_a), TyKind::Ref(_, _, mutbl_b)) => {
coerce_mutbls(mutbl_a, mutbl_b)?;
- let r_borrow = self.infcx().next_region_var();
+ let r_borrow = self.infcx().next_region_var(self.cause.span());
// We don't allow two-phase borrows here, at least for initial
// implementation. If it happens that this coercion is a function argument,
@@ -663,7 +658,7 @@ where
// the `CoerceUnsized` target type and the expected type.
// We only have the latter, so we use an inference variable
// for the former and let type inference do the rest.
- let coerce_target = self.infcx().next_ty_var();
+ let coerce_target = self.infcx().next_ty_var(self.cause.span());
let mut coercion = self.unify_and(
coerce_target,
@@ -673,7 +668,7 @@ where
)?;
// Create an obligation for `Source: CoerceUnsized<Target>`.
- let cause = self.cause.clone();
+ let cause = self.cause;
let pred = TraitRef::new(
self.interner(),
coerce_unsized_did.into(),
@@ -693,6 +688,7 @@ where
errored: false,
unsize_did,
coerce_unsized_did,
+ span: self.cause.span(),
},
)
.is_break()
@@ -714,7 +710,7 @@ where
self.commit_if_ok(|this| {
if let TyKind::FnPtr(_, hdr_b) = b.kind()
&& fn_ty_a.safety().is_safe()
- && !hdr_b.safety.is_safe()
+ && !hdr_b.safety().is_safe()
{
let unsafe_a = Ty::safe_to_unsafe_fn_ty(this.interner(), fn_ty_a);
this.unify_and(
@@ -763,7 +759,8 @@ where
return Err(TypeError::ForceInlineCast);
}
- if b_hdr.safety.is_safe() && attrs.contains(AttrFlags::HAS_TARGET_FEATURE) {
+ if b_hdr.safety().is_safe() && attrs.contains(AttrFlags::HAS_TARGET_FEATURE)
+ {
let fn_target_features =
TargetFeatures::from_fn_no_implications(self.db(), def_id);
// Allow the coercion if the current function has all the features that would be
@@ -811,7 +808,7 @@ where
// `fn(arg0,arg1,...) -> _`
// or
// `unsafe fn(arg0,arg1,...) -> _`
- let safety = hdr.safety;
+ let safety = hdr.safety();
let closure_sig =
self.interner().signature_unclosure(args_a.as_closure().sig(), safety);
let pointer_ty = Ty::new_fn_ptr(self.interner(), closure_sig);
@@ -894,24 +891,18 @@ impl<'db> InferenceContext<'_, 'db> {
/// The expressions *must not* have any preexisting adjustments.
pub(crate) fn coerce(
&mut self,
- expr: ExprOrPatId,
+ expr: ExprId,
expr_ty: Ty<'db>,
mut target: Ty<'db>,
allow_two_phase: AllowTwoPhase,
expr_is_read: ExprIsRead,
) -> RelateResult<'db, Ty<'db>> {
- let source = self.table.try_structurally_resolve_type(expr_ty);
- target = self.table.try_structurally_resolve_type(target);
+ let source = self.table.try_structurally_resolve_type(expr.into(), expr_ty);
+ target = self.table.try_structurally_resolve_type(expr.into(), target);
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
- let cause = ObligationCause::new();
- let coerce_never = match expr {
- ExprOrPatId::ExprId(idx) => {
- self.expr_guaranteed_to_constitute_read_for_never(idx, expr_is_read)
- }
- // `PatId` is passed for `PatKind::Path`.
- ExprOrPatId::PatId(_) => false,
- };
+ let cause = ObligationCause::new(expr);
+ let coerce_never = self.expr_guaranteed_to_constitute_read_for_never(expr, expr_is_read);
let mut coerce = Coerce {
delegate: InferenceCoercionDelegate(self),
cause,
@@ -922,11 +913,7 @@ impl<'db> InferenceContext<'_, 'db> {
let ok = coerce.commit_if_ok(|coerce| coerce.coerce(source, target))?;
let (adjustments, _) = self.table.register_infer_ok(ok);
- match expr {
- ExprOrPatId::ExprId(expr) => self.write_expr_adj(expr, adjustments.into_boxed_slice()),
- ExprOrPatId::PatId(pat) => self
- .write_pat_adj(pat, adjustments.into_iter().map(|adjust| adjust.target).collect()),
- }
+ self.write_expr_adj(expr, adjustments.into_boxed_slice());
Ok(target)
}
@@ -943,8 +930,8 @@ impl<'db> InferenceContext<'_, 'db> {
new: ExprId,
new_ty: Ty<'db>,
) -> RelateResult<'db, Ty<'db>> {
- let prev_ty = self.table.try_structurally_resolve_type(prev_ty);
- let new_ty = self.table.try_structurally_resolve_type(new_ty);
+ let prev_ty = self.table.try_structurally_resolve_type(new.into(), prev_ty);
+ let new_ty = self.table.try_structurally_resolve_type(new.into(), new_ty);
debug!(
"coercion::try_find_coercion_lub({:?}, {:?}, exprs={:?} exprs)",
prev_ty,
@@ -989,8 +976,12 @@ impl<'db> InferenceContext<'_, 'db> {
match self.table.commit_if_ok(|table| {
// We need to eagerly handle nested obligations due to lazy norm.
let mut ocx = ObligationCtxt::new(&table.infer_ctxt);
- let value =
- ocx.lub(&ObligationCause::new(), table.param_env, prev_ty, new_ty)?;
+ let value = ocx.lub(
+ &ObligationCause::new(new),
+ table.param_env,
+ prev_ty,
+ new_ty,
+ )?;
if ocx.try_evaluate_obligations().is_empty() {
Ok(InferOk { value, obligations: ocx.into_pending_obligations() })
} else {
@@ -1038,7 +1029,7 @@ impl<'db> InferenceContext<'_, 'db> {
let sig = self
.table
.infer_ctxt
- .at(&ObligationCause::new(), self.table.param_env)
+ .at(&ObligationCause::new(new), self.table.param_env)
.lub(a_sig, b_sig)
.map(|ok| self.table.register_infer_ok(ok))?;
@@ -1084,7 +1075,7 @@ impl<'db> InferenceContext<'_, 'db> {
// operate on values and not places, so a never coercion is valid.
let mut coerce = Coerce {
delegate: InferenceCoercionDelegate(self),
- cause: ObligationCause::new(),
+ cause: ObligationCause::new(new),
allow_two_phase: AllowTwoPhase::No,
coerce_never: true,
use_lub: true,
@@ -1120,7 +1111,7 @@ impl<'db> InferenceContext<'_, 'db> {
.commit_if_ok(|table| {
table
.infer_ctxt
- .at(&ObligationCause::new(), table.param_env)
+ .at(&ObligationCause::new(new), table.param_env)
.lub(prev_ty, new_ty)
})
.unwrap_err())
@@ -1335,7 +1326,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> {
// To be honest, I'm not entirely sure why we do this.
// We don't allow two-phase borrows, see comment in try_find_coercion_lub for why
icx.coerce(
- expression.into(),
+ expression,
expression_ty,
self.expected_ty,
AllowTwoPhase::No,
@@ -1401,14 +1392,11 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> {
self.final_ty = Some(icx.types.types.error);
- icx.result.type_mismatches.get_or_insert_default().insert(
- expression.into(),
- if label_expression_as_expected {
- TypeMismatch { expected: found.store(), actual: expected.store() }
- } else {
- TypeMismatch { expected: expected.store(), actual: found.store() }
- },
- );
+ if label_expression_as_expected {
+ icx.emit_type_mismatch(expression.into(), found, expected);
+ } else {
+ icx.emit_type_mismatch(expression.into(), expected, found);
+ }
}
}
@@ -1466,9 +1454,9 @@ fn coerce<'db>(
) -> Result<(Vec<Adjustment>, Ty<'db>), TypeError<DbInterner<'db>>> {
let interner = DbInterner::new_with(db, env.krate);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
- let ((ty1_with_vars, ty2_with_vars), vars) = infcx.instantiate_canonical(tys);
+ let ((ty1_with_vars, ty2_with_vars), vars) = infcx.instantiate_canonical(Span::Dummy, tys);
- let cause = ObligationCause::new();
+ let cause = ObligationCause::dummy();
// FIXME: Target features.
let target_features = TargetFeatures::default();
let mut coerce = Coerce {
@@ -1610,8 +1598,8 @@ fn coerce<'db>(
}
fn is_capturing_closure(db: &dyn HirDatabase, closure: InternedClosureId) -> bool {
- let InternedClosure(owner, expr) = closure.loc(db);
- upvars_mentioned(db, owner)
+ let InternedClosure { owner, expr, .. } = closure.loc(db);
+ upvars_mentioned(db, owner.expression_store_owner(db))
.is_some_and(|upvars| upvars.get(&expr).is_some_and(|upvars| !upvars.is_empty()))
}
@@ -1626,11 +1614,16 @@ struct CoerceVisitor<'a, D> {
errored: bool,
unsize_did: TraitId,
coerce_unsized_did: TraitId,
+ span: Span,
}
impl<'a, 'db, D: CoerceDelegate<'db>> ProofTreeVisitor<'db> for CoerceVisitor<'a, D> {
type Result = ControlFlow<()>;
+ fn span(&self) -> Span {
+ self.span
+ }
+
fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result {
let Some(pred) = goal.goal().predicate.as_trait_clause() else {
return ControlFlow::Continue(());
diff --git a/crates/hir-ty/src/infer/diagnostics.rs b/crates/hir-ty/src/infer/diagnostics.rs
index 2bdc6f9491..dd0efea4d7 100644
--- a/crates/hir-ty/src/infer/diagnostics.rs
+++ b/crates/hir-ty/src/infer/diagnostics.rs
@@ -2,22 +2,28 @@
//! and a wrapper around [`TyLoweringContext`] ([`InferenceTyLoweringContext`]) that replaces
//! it and takes care of diagnostics in inference.
-use std::cell::RefCell;
+use std::cell::{OnceCell, RefCell};
use std::ops::{Deref, DerefMut};
use either::Either;
-use hir_def::GenericDefId;
-use hir_def::expr_store::ExpressionStore;
use hir_def::expr_store::path::Path;
+use hir_def::{ExpressionStoreOwnerId, GenericDefId};
+use hir_def::{expr_store::ExpressionStore, type_ref::TypeRefId};
use hir_def::{hir::ExprOrPatId, resolver::Resolver};
use la_arena::{Idx, RawIdx};
+use rustc_hash::FxHashMap;
use thin_vec::ThinVec;
use crate::{
- InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic,
- db::HirDatabase,
- lower::path::{PathDiagnosticCallback, PathLoweringContext},
- lower::{LifetimeElisionKind, TyLoweringContext},
+ InferenceDiagnostic, InferenceTyDiagnosticSource, Span, TyLoweringDiagnostic,
+ db::{AnonConstId, HirDatabase},
+ generics::Generics,
+ infer::unify::InferenceTable,
+ lower::{
+ ForbidParamsAfterReason, LifetimeElisionKind, TyLoweringContext, TyLoweringInferVarsCtx,
+ path::{PathDiagnosticCallback, PathLoweringContext},
+ },
+ next_solver::{Const, Region, StoredTy, Ty},
};
// Unfortunately, this struct needs to use interior mutability (but we encapsulate it)
@@ -35,7 +41,7 @@ impl Diagnostics {
fn push_ty_diagnostics(
&self,
source: InferenceTyDiagnosticSource,
- diagnostics: Vec<TyLoweringDiagnostic>,
+ diagnostics: ThinVec<TyLoweringDiagnostic>,
) {
self.0.borrow_mut().extend(
diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }),
@@ -52,10 +58,38 @@ pub(crate) struct PathDiagnosticCallbackData<'a> {
diagnostics: &'a Diagnostics,
}
+pub(super) struct InferenceTyLoweringVarsCtx<'a, 'db> {
+ pub(super) table: &'a mut InferenceTable<'db>,
+ pub(super) type_of_type_placeholder: &'a mut FxHashMap<TypeRefId, StoredTy>,
+}
+
+impl<'db> TyLoweringInferVarsCtx<'db> for InferenceTyLoweringVarsCtx<'_, 'db> {
+ fn next_ty_var(&mut self, span: Span) -> Ty<'db> {
+ let ty = self.table.infer_ctxt.next_ty_var(span);
+
+ if let Span::TypeRefId(type_ref) = span {
+ self.type_of_type_placeholder.insert(type_ref, ty.store());
+ }
+
+ ty
+ }
+ fn next_const_var(&mut self, span: Span) -> Const<'db> {
+ self.table.infer_ctxt.next_const_var(span)
+ }
+ fn next_region_var(&mut self, span: Span) -> Region<'db> {
+ self.table.infer_ctxt.next_region_var(span)
+ }
+
+ fn as_table(&mut self) -> Option<&mut InferenceTable<'db>> {
+ Some(self.table)
+ }
+}
+
pub(super) struct InferenceTyLoweringContext<'db, 'a> {
ctx: TyLoweringContext<'db, 'a>,
diagnostics: &'a Diagnostics,
source: InferenceTyDiagnosticSource,
+ defined_anon_consts: &'a RefCell<ThinVec<AnonConstId>>,
}
impl<'db, 'a> InferenceTyLoweringContext<'db, 'a> {
@@ -66,14 +100,28 @@ impl<'db, 'a> InferenceTyLoweringContext<'db, 'a> {
store: &'a ExpressionStore,
diagnostics: &'a Diagnostics,
source: InferenceTyDiagnosticSource,
+ def: ExpressionStoreOwnerId,
generic_def: GenericDefId,
+ generics: &'a OnceCell<Generics<'db>>,
lifetime_elision: LifetimeElisionKind<'db>,
+ allow_using_generic_params: bool,
+ infer_vars: Option<&'a mut dyn TyLoweringInferVarsCtx<'db>>,
+ defined_anon_consts: &'a RefCell<ThinVec<AnonConstId>>,
) -> Self {
- Self {
- ctx: TyLoweringContext::new(db, resolver, store, generic_def, lifetime_elision),
- diagnostics,
- source,
+ let mut ctx = TyLoweringContext::new(
+ db,
+ resolver,
+ store,
+ def,
+ generic_def,
+ generics,
+ lifetime_elision,
+ )
+ .with_infer_vars_behavior(infer_vars);
+ if !allow_using_generic_params {
+ ctx.forbid_params_after(0, ForbidParamsAfterReason::AnonConst);
}
+ Self { ctx, diagnostics, source, defined_anon_consts }
}
#[inline]
@@ -135,5 +183,6 @@ impl Drop for InferenceTyLoweringContext<'_, '_> {
fn drop(&mut self) {
self.diagnostics
.push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics));
+ self.defined_anon_consts.borrow_mut().extend(self.ctx.defined_anon_consts.iter().copied());
}
}
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index d80ea71674..0675b5e857 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -4,11 +4,11 @@ use std::{iter::repeat_with, mem};
use either::Either;
use hir_def::{
- FieldId, GenericDefId, ItemContainerId, Lookup, TupleFieldId, TupleId,
+ AdtId, FieldId, TupleFieldId, TupleId, VariantId,
expr_store::path::{GenericArgs as HirGenericArgs, Path},
hir::{
Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId,
- InlineAsmKind, LabelId, Literal, Pat, PatId, RecordSpread, Statement, UnaryOp,
+ InlineAsmKind, LabelId, Pat, PatId, RecordLitField, RecordSpread, Statement, UnaryOp,
},
resolver::ValueNs,
signatures::VariantFields,
@@ -16,35 +16,33 @@ use hir_def::{
use hir_def::{FunctionId, hir::ClosureKind};
use hir_expand::name::Name;
use rustc_ast_ir::Mutability;
+use rustc_hash::FxHashMap;
use rustc_type_ir::{
InferTy, Interner,
- inherent::{AdtDef, GenericArgs as _, IntoKind, Ty as _},
+ inherent::{GenericArgs as _, IntoKind, Ty as _},
};
+use stdx::never;
use syntax::ast::RangeOp;
use tracing::debug;
use crate::{
- Adjust, Adjustment, CallableDefId, DeclContext, DeclOrigin, Rawness, consteval,
- generics::generics,
- infer::{
- AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable,
- pat::contains_explicit_ref_binding,
- },
- lower::{GenericPredicates, lower_mutability},
+ Adjust, Adjustment, CallableDefId, Rawness, Span,
+ consteval::literal_ty,
+ infer::{AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable, pat::PatOrigin},
+ lower::lower_mutability,
method_resolution::{self, CandidateId, MethodCallee, MethodError},
next_solver::{
- ClauseKind, FnSig, GenericArg, GenericArgs, TraitRef, Ty, TyKind, TypeError,
+ ClauseKind, FnSig, GenericArg, GenericArgs, Ty, TyKind, TypeError,
infer::{
BoundRegionConversionTime, InferOk,
traits::{Obligation, ObligationCause},
},
obligation_ctxt::ObligationCtxt,
- util::clauses_as_obligations,
},
};
use super::{
- BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch,
+ BreakableContext, Diverges, Expectation, InferenceContext, InferenceDiagnostic,
cast::CastCheck, find_breakable,
};
@@ -63,17 +61,41 @@ impl<'db> InferenceContext<'_, 'db> {
) -> Ty<'db> {
let ty = self.infer_expr_inner(tgt_expr, expected, is_read);
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.get_or_insert_default().insert(
- tgt_expr.into(),
- TypeMismatch { expected: expected_ty.store(), actual: ty.store() },
- );
- }
+ _ = self.demand_eqtype(tgt_expr.into(), expected_ty, ty);
}
ty
}
+ pub(crate) fn infer_expr_suptype_coerce_never(
+ &mut self,
+ expr: ExprId,
+ expected: &Expectation<'db>,
+ is_read: ExprIsRead,
+ ) -> Ty<'db> {
+ let ty = self.infer_expr_inner(expr, expected, is_read);
+ if ty.is_never() {
+ if let Some(adjustments) = self.result.expr_adjustments.get(&expr) {
+ return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments {
+ target.as_ref()
+ } else {
+ self.err_ty()
+ };
+ }
+
+ if let Some(target) = expected.only_has_type(&mut self.table) {
+ self.coerce(expr, ty, target, AllowTwoPhase::No, ExprIsRead::Yes)
+ .expect("never-to-any coercion should always succeed")
+ } else {
+ ty
+ }
+ } else {
+ if let Some(expected_ty) = expected.only_has_type(&mut self.table) {
+ _ = self.demand_suptype(expr.into(), expected_ty, ty);
+ }
+ ty
+ }
+ }
+
pub(crate) fn infer_expr_no_expect(
&mut self,
tgt_expr: ExprId,
@@ -92,13 +114,10 @@ impl<'db> InferenceContext<'_, 'db> {
) -> Ty<'db> {
let ty = self.infer_expr_inner(expr, expected, is_read);
if let Some(target) = expected.only_has_type(&mut self.table) {
- match self.coerce(expr.into(), ty, target, AllowTwoPhase::No, is_read) {
+ match self.coerce(expr, ty, target, AllowTwoPhase::No, is_read) {
Ok(res) => res,
Err(_) => {
- self.result.type_mismatches.get_or_insert_default().insert(
- expr.into(),
- TypeMismatch { expected: target.store(), actual: ty.store() },
- );
+ self.emit_type_mismatch(expr.into(), target, ty);
target
}
}
@@ -154,7 +173,7 @@ impl<'db> InferenceContext<'_, 'db> {
fn pat_guaranteed_to_constitute_read_for_never(&self, pat: PatId) -> bool {
match &self.store[pat] {
// Does not constitute a read.
- Pat::Wild => false,
+ Pat::Wild | Pat::Rest => false,
// This is unnecessarily restrictive when the pattern that doesn't
// constitute a read is unreachable.
@@ -175,6 +194,7 @@ impl<'db> InferenceContext<'_, 'db> {
| Pat::Path(_)
| Pat::Tuple { .. }
| Pat::Box { .. }
+ | Pat::Deref { .. }
| Pat::Ref { .. }
| Pat::Lit(_)
| Pat::Range { .. }
@@ -251,13 +271,12 @@ impl<'db> InferenceContext<'_, 'db> {
}
}
- #[expect(clippy::needless_return)]
pub(crate) fn check_lhs_assignable(&self, lhs: ExprId) {
if self.is_syntactic_place_expr(lhs) {
return;
}
- // FIXME: Emit diagnostic.
+ self.push_diagnostic(InferenceDiagnostic::InvalidLhsOfAssignment { lhs });
}
fn infer_expr_coerce_never(
@@ -279,27 +298,21 @@ impl<'db> InferenceContext<'_, 'db> {
}
if let Some(target) = expected.only_has_type(&mut self.table) {
- self.coerce(expr.into(), ty, target, AllowTwoPhase::No, ExprIsRead::Yes)
+ self.coerce(expr, ty, target, AllowTwoPhase::No, ExprIsRead::Yes)
.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.get_or_insert_default().insert(
- expr.into(),
- TypeMismatch { expected: expected_ty.store(), actual: ty.store() },
- );
- }
+ _ = self.demand_eqtype(expr.into(), ty, expected_ty);
}
ty
}
}
#[tracing::instrument(level = "debug", skip(self, is_read), ret)]
- fn infer_expr_inner(
+ pub(super) fn infer_expr_inner(
&mut self,
tgt_expr: ExprId,
expected: &Expectation<'db>,
@@ -312,7 +325,7 @@ impl<'db> InferenceContext<'_, 'db> {
let ty = match expr {
Expr::Missing => self.err_ty(),
&Expr::If { condition, then_branch, else_branch } => {
- let expected = &expected.adjust_for_branches(&mut self.table);
+ let expected = &expected.adjust_for_branches(&mut self.table, tgt_expr.into());
self.infer_expr_coerce_never(
condition,
&Expectation::HasType(self.types.types.bool),
@@ -328,17 +341,23 @@ impl<'db> InferenceContext<'_, 'db> {
coercion_sites[1] = else_branch;
}
let mut coerce = CoerceMany::with_coercion_sites(
- expected.coercion_target_type(&mut self.table),
+ expected.coercion_target_type(&mut self.table, then_branch.into()),
&coercion_sites,
);
- coerce.coerce(self, &ObligationCause::new(), then_branch, then_ty, ExprIsRead::Yes);
+ coerce.coerce(
+ self,
+ &ObligationCause::new(then_branch),
+ then_branch,
+ then_ty,
+ ExprIsRead::Yes,
+ );
match else_branch {
Some(else_branch) => {
let else_ty = self.infer_expr_inner(else_branch, expected, ExprIsRead::Yes);
let else_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
coerce.coerce(
self,
- &ObligationCause::new(),
+ &ObligationCause::new(else_branch),
else_branch,
else_ty,
ExprIsRead::Yes,
@@ -349,7 +368,7 @@ impl<'db> InferenceContext<'_, 'db> {
coerce.coerce_forced_unit(
self,
tgt_expr,
- &ObligationCause::new(),
+ &ObligationCause::new(tgt_expr),
true,
ExprIsRead::Yes,
);
@@ -366,11 +385,7 @@ impl<'db> InferenceContext<'_, 'db> {
ExprIsRead::No
};
let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read);
- self.infer_top_pat(
- pat,
- input_ty,
- Some(DeclContext { origin: DeclOrigin::LetExpr }),
- );
+ self.infer_top_pat(pat, input_ty, PatOrigin::LetExpr);
self.types.types.bool
}
Expr::Block { statements, tail, label, id: _ } => {
@@ -386,9 +401,7 @@ impl<'db> InferenceContext<'_, 'db> {
.1
}
&Expr::Loop { body, label } => {
- // FIXME: should be:
- // let ty = expected.coercion_target_type(&mut self.table);
- let ty = self.table.next_ty_var();
+ let ty = expected.coercion_target_type(&mut self.table, tgt_expr.into());
let (breaks, ()) =
self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| {
this.infer_expr(
@@ -449,15 +462,15 @@ impl<'db> InferenceContext<'_, 'db> {
let matchee_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let mut all_arms_diverge = Diverges::Always;
for arm in arms.iter() {
- self.infer_top_pat(arm.pat, input_ty, None);
+ self.infer_top_pat(arm.pat, input_ty, PatOrigin::MatchArm);
}
- let expected = expected.adjust_for_branches(&mut self.table);
+ let expected = expected.adjust_for_branches(&mut self.table, tgt_expr.into());
let result_ty = match &expected {
// We don't coerce to `()` so that if the match expression is a
// statement it's branches can have any consistent type.
Expectation::HasType(ty) if *ty != self.types.types.unit => *ty,
- _ => self.table.next_ty_var(),
+ _ => self.table.next_ty_var((*expr).into()),
};
let mut coerce = CoerceMany::new(result_ty);
@@ -476,7 +489,7 @@ impl<'db> InferenceContext<'_, 'db> {
all_arms_diverge &= self.diverges;
coerce.coerce(
self,
- &ObligationCause::new(),
+ &ObligationCause::new(arm.expr),
arm.expr,
arm_ty,
ExprIsRead::Yes,
@@ -527,10 +540,11 @@ impl<'db> InferenceContext<'_, 'db> {
match find_breakable(&mut self.breakables, label) {
Some(ctxt) => match ctxt.coerce.take() {
Some(mut coerce) => {
+ let expr = expr.unwrap_or(tgt_expr);
coerce.coerce(
self,
- &ObligationCause::new(),
- expr.unwrap_or(tgt_expr),
+ &ObligationCause::new(expr),
+ expr,
val_ty,
ExprIsRead::Yes,
);
@@ -566,7 +580,7 @@ impl<'db> InferenceContext<'_, 'db> {
} else {
let unit = self.types.types.unit;
let _ = self.coerce(
- tgt_expr.into(),
+ tgt_expr,
unit,
yield_ty,
AllowTwoPhase::No,
@@ -586,80 +600,10 @@ impl<'db> InferenceContext<'_, 'db> {
self.types.types.never
}
Expr::RecordLit { path, fields, spread, .. } => {
- let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path.as_deref(), false);
-
- if let Some(t) = expected.only_has_type(&mut self.table) {
- self.unify(ty, t);
- }
-
- let substs = ty.as_adt().map(|(_, s)| s).unwrap_or(self.types.empty.generic_args);
- if let Some(variant) = def_id {
- self.write_variant_resolution(tgt_expr.into(), variant);
- }
- match def_id {
- _ if fields.is_empty() => {}
- Some(def) => {
- let field_types = self.db.field_types(def);
- let variant_data = def.fields(self.db);
- let visibilities = VariantFields::field_visibilities(self.db, def);
- for field in fields.iter() {
- let field_def = {
- match variant_data.field(&field.name) {
- Some(local_id) => {
- if !visibilities[local_id]
- .is_visible_from(self.db, self.resolver.module())
- {
- self.push_diagnostic(
- InferenceDiagnostic::NoSuchField {
- field: field.expr.into(),
- private: Some(local_id),
- variant: def,
- },
- );
- }
- Some(local_id)
- }
- None => {
- self.push_diagnostic(InferenceDiagnostic::NoSuchField {
- field: field.expr.into(),
- private: None,
- variant: def,
- });
- None
- }
- }
- };
- let field_ty = field_def.map_or(self.err_ty(), |it| {
- field_types[it].get().instantiate(self.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),
- ExprIsRead::Yes,
- );
- }
- }
- None => {
- for field in fields.iter() {
- // Field projections don't constitute reads.
- self.infer_expr_coerce(field.expr, &Expectation::None, ExprIsRead::No);
- }
- }
- }
- if let RecordSpread::Expr(expr) = *spread {
- self.infer_expr_coerce_never(expr, &Expectation::has_type(ty), ExprIsRead::Yes);
- }
- ty
+ self.infer_record_expr(tgt_expr, expected, path, fields, *spread)
}
Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected),
- Expr::Await { expr } => {
- let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes);
- self.resolve_associated_type(inner_ty, self.resolve_future_future_output())
- }
+ Expr::Await { expr } => self.infer_await_expr(tgt_expr, *expr),
Expr::Cast { expr, type_ref } => {
let cast_ty = self.make_body_ty(*type_ref);
let expr_ty =
@@ -667,34 +611,13 @@ impl<'db> InferenceContext<'_, 'db> {
self.deferred_cast_checks.push(CastCheck::new(tgt_expr, *expr, expr_ty, cast_ty));
cast_ty
}
- Expr::Ref { expr, rawness, mutability } => {
- let mutability = lower_mutability(*mutability);
- let expectation = if let Some((exp_inner, exp_rawness, exp_mutability)) = expected
- .only_has_type(&mut self.table)
- .as_ref()
- .and_then(|t| t.as_reference_or_ptr())
- {
- if exp_mutability == Mutability::Mut && mutability == Mutability::Not {
- // FIXME: record type error - expected mut reference but found shared ref,
- // which cannot be coerced
- }
- if exp_rawness == Rawness::Ref && *rawness == Rawness::RawPtr {
- // FIXME: record type error - expected reference but found ptr,
- // which cannot be coerced
- }
- Expectation::rvalue_hint(self, exp_inner)
- } else {
- Expectation::none()
- };
- let inner_ty = self.infer_expr_inner(*expr, &expectation, ExprIsRead::Yes);
- match rawness {
- Rawness::RawPtr => Ty::new_ptr(self.interner(), inner_ty, mutability),
- Rawness::Ref => {
- let lt = self.table.next_region_var();
- Ty::new_ref(self.interner(), lt, inner_ty, mutability)
- }
- }
- }
+ Expr::Ref { expr, rawness, mutability } => self.infer_ref_expr(
+ *rawness,
+ lower_mutability(*mutability),
+ *expr,
+ expected,
+ tgt_expr,
+ ),
&Expr::Box { expr } => self.infer_expr_box(expr, expected),
Expr::UnaryOp { expr, op } => self.infer_unop_expr(*op, *expr, expected, tgt_expr),
Expr::BinaryOp { lhs, rhs, op } => match op {
@@ -715,29 +638,6 @@ impl<'db> InferenceContext<'_, 'db> {
&Pat::Expr(expr) => {
Some(self.infer_expr(expr, &Expectation::none(), ExprIsRead::No))
}
- Pat::Path(path) => {
- let resolver_guard =
- self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr);
- let resolution = self.resolver.resolve_path_in_value_ns_fully(
- self.db,
- path,
- self.store.pat_path_hygiene(target),
- );
- self.resolver.reset_to_guard(resolver_guard);
-
- if matches!(
- resolution,
- Some(
- ValueNs::ConstId(_)
- | ValueNs::StructId(_)
- | ValueNs::EnumVariantId(_)
- )
- ) {
- None
- } else {
- Some(self.infer_expr_path(path, target.into(), tgt_expr))
- }
- }
_ => None,
};
let is_destructuring_assignment = lhs_ty.is_none();
@@ -748,9 +648,9 @@ impl<'db> InferenceContext<'_, 'db> {
} else {
let rhs_ty = self.infer_expr(value, &Expectation::none(), ExprIsRead::Yes);
let resolver_guard =
- self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr);
+ self.resolver.update_to_inner_scope(self.db, self.store_owner, tgt_expr);
self.inside_assignment = true;
- self.infer_top_pat(target, rhs_ty, None);
+ self.infer_top_pat(target, rhs_ty, PatOrigin::DestructuringAssignment);
self.inside_assignment = false;
self.resolver.reset_to_guard(resolver_guard);
}
@@ -759,7 +659,7 @@ impl<'db> InferenceContext<'_, 'db> {
// However, rustc lowers destructuring assignments into blocks, and blocks return `!` if they have no tail
// expression and they diverge. Therefore, we have to do the same here, even though we don't lower destructuring
// assignments into blocks.
- self.table.new_maybe_never_var()
+ self.table.new_maybe_never_var(value.into())
} else {
self.types.types.unit
}
@@ -814,8 +714,8 @@ impl<'db> InferenceContext<'_, 'db> {
let base_t = self.infer_expr_no_expect(*base, ExprIsRead::Yes);
let idx_t = self.infer_expr_no_expect(*index, ExprIsRead::Yes);
- let base_t = self.table.structurally_resolve_type(base_t);
- match self.lookup_indexing(tgt_expr, *base, base_t, idx_t) {
+ let base_t = self.structurally_resolve_type((*base).into(), base_t);
+ match self.lookup_indexing(tgt_expr, *base, *index, base_t, idx_t) {
Some((trait_index_ty, trait_element_ty)) => {
// two-phase not needed because index_ty is never mutable
self.demand_coerce(
@@ -835,14 +735,14 @@ impl<'db> InferenceContext<'_, 'db> {
Expr::Tuple { exprs, .. } => {
let mut tys = match expected
.only_has_type(&mut self.table)
- .map(|t| self.table.try_structurally_resolve_type(t).kind())
+ .map(|t| self.table.try_structurally_resolve_type(tgt_expr.into(), t).kind())
{
Some(TyKind::Tuple(substs)) => substs
.iter()
- .chain(repeat_with(|| self.table.next_ty_var()))
+ .chain(repeat_with(|| self.table.next_ty_var(Span::Dummy)))
.take(exprs.len())
.collect::<Vec<_>>(),
- _ => (0..exprs.len()).map(|_| self.table.next_ty_var()).collect(),
+ _ => exprs.iter().map(|&expr| self.table.next_ty_var(expr.into())).collect(),
};
for (expr, ty) in exprs.iter().zip(tys.iter_mut()) {
@@ -852,107 +752,51 @@ impl<'db> InferenceContext<'_, 'db> {
Ty::new_tup(self.interner(), &tys)
}
- Expr::Array(array) => self.infer_expr_array(array, expected),
- Expr::Literal(lit) => match lit {
- Literal::Bool(..) => self.types.types.bool,
- Literal::String(..) => self.types.types.static_str_ref,
- Literal::ByteString(bs) => {
- let byte_type = self.types.types.u8;
-
- let len = consteval::usize_const(
- self.db,
- Some(bs.len() as u128),
- self.resolver.krate(),
- );
-
- let array_type = Ty::new_array_with_const_len(self.interner(), byte_type, len);
- Ty::new_ref(
- self.interner(),
- self.types.regions.statik,
- array_type,
- Mutability::Not,
- )
- }
- Literal::CString(..) => Ty::new_ref(
- self.interner(),
- self.types.regions.statik,
- self.lang_items.CStr.map_or_else(
- || self.err_ty(),
- |strukt| {
- Ty::new_adt(
- self.interner(),
- strukt.into(),
- self.types.empty.generic_args,
- )
- },
- ),
- Mutability::Not,
- ),
- Literal::Char(..) => self.types.types.char,
- Literal::Int(_v, ty) => match ty {
- Some(int_ty) => match int_ty {
- hir_def::builtin_type::BuiltinInt::Isize => self.types.types.isize,
- hir_def::builtin_type::BuiltinInt::I8 => self.types.types.i8,
- hir_def::builtin_type::BuiltinInt::I16 => self.types.types.i16,
- hir_def::builtin_type::BuiltinInt::I32 => self.types.types.i32,
- hir_def::builtin_type::BuiltinInt::I64 => self.types.types.i64,
- hir_def::builtin_type::BuiltinInt::I128 => self.types.types.i128,
- },
- None => {
- let expected_ty = expected.to_option(&mut self.table);
- tracing::debug!(?expected_ty);
- let opt_ty = match expected_ty.as_ref().map(|it| it.kind()) {
- Some(TyKind::Int(_) | TyKind::Uint(_)) => expected_ty,
- Some(TyKind::Char) => Some(self.types.types.u8),
- Some(TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::FnPtr(..)) => {
- Some(self.types.types.usize)
- }
- _ => None,
- };
- opt_ty.unwrap_or_else(|| self.table.next_int_var())
- }
+ Expr::Array(Array::ElementList { elements }) => {
+ self.infer_array_elements_expr(elements, expected, tgt_expr)
+ }
+ Expr::Array(Array::Repeat { initializer, repeat }) => {
+ self.infer_array_repeat_expr(*initializer, *repeat, expected, tgt_expr)
+ }
+ Expr::Literal(lit) => literal_ty(
+ self.interner(),
+ lit,
+ |_| {
+ let expected_ty = expected.to_option(&self.table);
+ tracing::debug!(?expected_ty);
+ let opt_ty = match expected_ty.as_ref().map(|it| it.kind()) {
+ Some(TyKind::Int(_) | TyKind::Uint(_)) => expected_ty,
+ Some(TyKind::Char) => Some(self.types.types.u8),
+ Some(TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::FnPtr(..)) => {
+ Some(self.types.types.usize)
+ }
+ _ => None,
+ };
+ opt_ty.unwrap_or_else(|| self.table.next_int_var())
},
- Literal::Uint(_v, ty) => match ty {
- Some(int_ty) => match int_ty {
- hir_def::builtin_type::BuiltinUint::Usize => self.types.types.usize,
- hir_def::builtin_type::BuiltinUint::U8 => self.types.types.u8,
- hir_def::builtin_type::BuiltinUint::U16 => self.types.types.u16,
- hir_def::builtin_type::BuiltinUint::U32 => self.types.types.u32,
- hir_def::builtin_type::BuiltinUint::U64 => self.types.types.u64,
- hir_def::builtin_type::BuiltinUint::U128 => self.types.types.u128,
- },
- None => {
- let expected_ty = expected.to_option(&mut self.table);
- let opt_ty = match expected_ty.as_ref().map(|it| it.kind()) {
- Some(TyKind::Int(_) | TyKind::Uint(_)) => expected_ty,
- Some(TyKind::Char) => Some(self.types.types.u8),
- Some(TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::FnPtr(..)) => {
- Some(self.types.types.usize)
- }
- _ => None,
- };
- opt_ty.unwrap_or_else(|| self.table.next_int_var())
- }
+ |_| {
+ let expected_ty = expected.to_option(&self.table);
+ let opt_ty = match expected_ty.as_ref().map(|it| it.kind()) {
+ Some(TyKind::Int(_) | TyKind::Uint(_)) => expected_ty,
+ Some(TyKind::Char) => Some(self.types.types.u8),
+ Some(TyKind::RawPtr(..) | TyKind::FnDef(..) | TyKind::FnPtr(..)) => {
+ Some(self.types.types.usize)
+ }
+ _ => None,
+ };
+ opt_ty.unwrap_or_else(|| self.table.next_int_var())
},
- Literal::Float(_v, ty) => match ty {
- Some(float_ty) => match float_ty {
- hir_def::builtin_type::BuiltinFloat::F16 => self.types.types.f16,
- hir_def::builtin_type::BuiltinFloat::F32 => self.types.types.f32,
- hir_def::builtin_type::BuiltinFloat::F64 => self.types.types.f64,
- hir_def::builtin_type::BuiltinFloat::F128 => self.types.types.f128,
- },
- None => {
- let opt_ty = expected
- .to_option(&mut self.table)
- .filter(|ty| matches!(ty.kind(), TyKind::Float(_)));
- opt_ty.unwrap_or_else(|| self.table.next_float_var())
- }
+ |_| {
+ let opt_ty = expected
+ .to_option(&self.table)
+ .filter(|ty| matches!(ty.kind(), TyKind::Float(_)));
+ opt_ty.unwrap_or_else(|| self.table.next_float_var())
},
- },
+ ),
Expr::Underscore => {
// 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());
+ let expected = expected.to_option(&self.table).unwrap_or_else(|| self.err_ty());
self.push_diagnostic(InferenceDiagnostic::TypedHole {
expr: tgt_expr,
expected: expected.store(),
@@ -972,17 +816,18 @@ impl<'db> InferenceContext<'_, 'db> {
// allows them to be inferred based on how they are used later in the
// function.
if is_input {
- let ty = this.table.structurally_resolve_type(ty);
+ let ty = this.structurally_resolve_type(expr.into(), ty);
match ty.kind() {
TyKind::FnDef(def, parameters) => {
let fnptr_ty = Ty::new_fn_ptr(
this.interner(),
this.interner()
.fn_sig(def)
- .instantiate(this.interner(), parameters),
+ .instantiate(this.interner(), parameters)
+ .skip_norm_wip(),
);
_ = this.coerce(
- expr.into(),
+ expr,
ty,
fnptr_ty,
AllowTwoPhase::No,
@@ -992,7 +837,7 @@ impl<'db> InferenceContext<'_, 'db> {
TyKind::Ref(_, base_ty, mutbl) => {
let ptr_ty = Ty::new_ptr(this.interner(), base_ty, mutbl);
_ = this.coerce(
- expr.into(),
+ expr,
ty,
ptr_ty,
AllowTwoPhase::No,
@@ -1037,7 +882,6 @@ impl<'db> InferenceContext<'_, 'db> {
}
}
};
- // use a new type variable if we got unknown here
let ty = self.insert_type_vars_shallow(ty);
self.write_expr_ty(tgt_expr, ty);
if self.shallow_resolve(ty).is_never()
@@ -1049,6 +893,301 @@ impl<'db> InferenceContext<'_, 'db> {
ty
}
+ fn infer_ref_expr(
+ &mut self,
+ rawness: Rawness,
+ mutbl: Mutability,
+ oprnd: ExprId,
+ expected: &Expectation<'db>,
+ expr: ExprId,
+ ) -> Ty<'db> {
+ let hint = expected.only_has_type(&mut self.table).map_or(Expectation::None, |ty| {
+ match self.table.resolve_vars_with_obligations(ty).kind() {
+ TyKind::Ref(_, ty, _) | TyKind::RawPtr(ty, _) => {
+ if self.is_syntactic_place_expr(oprnd) {
+ // Places may legitimately have unsized types.
+ // For example, dereferences of a wide pointer and
+ // the last field of a struct can be unsized.
+ Expectation::has_type(ty)
+ } else {
+ Expectation::rvalue_hint(self, ty)
+ }
+ }
+ _ => Expectation::None,
+ }
+ });
+ let ty = self.infer_expr_inner(oprnd, &hint, ExprIsRead::No);
+
+ match rawness {
+ Rawness::RawPtr => Ty::new_ptr(self.interner(), ty, mutbl),
+ Rawness::Ref => {
+ // Note: at this point, we cannot say what the best lifetime
+ // is to use for resulting pointer. We want to use the
+ // shortest lifetime possible so as to avoid spurious borrowck
+ // errors. Moreover, the longest lifetime will depend on the
+ // precise details of the value whose address is being taken
+ // (and how long it is valid), which we don't know yet until
+ // type inference is complete.
+ //
+ // Therefore, here we simply generate a region variable. The
+ // region inferencer will then select a suitable value.
+ // Finally, borrowck will infer the value of the region again,
+ // this time with enough precision to check that the value
+ // whose address was taken can actually be made to live as long
+ // as it needs to live.
+ let region = self.table.next_region_var(expr.into());
+ Ty::new_ref(self.interner(), region, ty, mutbl)
+ }
+ }
+ }
+
+ fn infer_await_expr(&mut self, expr: ExprId, awaitee: ExprId) -> Ty<'db> {
+ let awaitee_ty = self.infer_expr_no_expect(awaitee, ExprIsRead::Yes);
+ let (Some(into_future), Some(into_future_output)) =
+ (self.lang_items.IntoFuture, self.lang_items.IntoFutureOutput)
+ else {
+ return self.types.types.error;
+ };
+ self.table.register_bound(awaitee_ty, into_future, ObligationCause::new(expr));
+ // Do not eagerly normalize.
+ Ty::new_projection(self.interner(), into_future_output.into(), [awaitee_ty])
+ }
+
+ fn infer_record_expr(
+ &mut self,
+ expr: ExprId,
+ expected: &Expectation<'db>,
+ path: &Path,
+ fields: &[RecordLitField],
+ base_expr: RecordSpread,
+ ) -> Ty<'db> {
+ // Find the relevant variant
+ let (adt_ty, Some(variant)) = self.resolve_variant(expr.into(), path, false) else {
+ // FIXME: Emit an error.
+ for field in fields {
+ self.infer_expr_no_expect(field.expr, ExprIsRead::Yes);
+ }
+
+ return self.types.types.error;
+ };
+ self.write_variant_resolution(expr.into(), variant);
+
+ // Prohibit struct expressions when non-exhaustive flag is set.
+ if self.has_applicable_non_exhaustive(variant.into()) {
+ self.push_diagnostic(InferenceDiagnostic::NonExhaustiveRecordExpr { expr });
+ }
+
+ self.check_record_expr_fields(adt_ty, expected, expr, variant, fields, base_expr);
+
+ self.require_type_is_sized(adt_ty, expr.into());
+ adt_ty
+ }
+
+ fn check_record_expr_fields(
+ &mut self,
+ adt_ty: Ty<'db>,
+ expected: &Expectation<'db>,
+ expr: ExprId,
+ variant: VariantId,
+ hir_fields: &[RecordLitField],
+ base_expr: RecordSpread,
+ ) {
+ let interner = self.interner();
+
+ let adt_ty = self.table.try_structurally_resolve_type(expr.into(), adt_ty);
+ let adt_ty_hint = expected.only_has_type(&mut self.table).and_then(|expected| {
+ self.infcx()
+ .fudge_inference_if_ok(|| {
+ let mut ocx = ObligationCtxt::new(self.infcx());
+ ocx.sup(&ObligationCause::new(expr), self.table.param_env, expected, adt_ty)?;
+ if !ocx.try_evaluate_obligations().is_empty() {
+ return Err(TypeError::Mismatch);
+ }
+ Ok(self.resolve_vars_if_possible(adt_ty))
+ })
+ .ok()
+ });
+ if let Some(adt_ty_hint) = adt_ty_hint {
+ // re-link the variables that the fudging above can create.
+ _ = self.demand_eqtype(expr.into(), adt_ty_hint, adt_ty);
+ }
+
+ let TyKind::Adt(adt, args) = adt_ty.kind() else {
+ never!("non-ADT passed to check_struct_expr_fields");
+ return;
+ };
+ let adt_id = adt.def_id();
+
+ let variant_fields = variant.fields(self.db);
+ let variant_field_tys = self.db.field_types(variant);
+ let variant_field_vis = VariantFields::field_visibilities(self.db, variant);
+ let mut remaining_fields = variant_fields
+ .fields()
+ .iter()
+ .map(|(i, field)| (field.name.clone(), i))
+ .collect::<FxHashMap<_, _>>();
+
+ let mut seen_fields = FxHashMap::default();
+
+ // Type-check each field.
+ for field in hir_fields {
+ let name = &field.name;
+ let field_type = if let Some(i) = remaining_fields.remove(name) {
+ seen_fields.insert(name, i);
+
+ if !self.resolver.is_visible(self.db, variant_field_vis[i]) {
+ self.push_diagnostic(InferenceDiagnostic::NoSuchField {
+ field: field.expr.into(),
+ private: Some(i),
+ variant,
+ });
+ }
+
+ variant_field_tys[i].get().instantiate(interner, args).skip_norm_wip()
+ } else {
+ if let Some(field_idx) = seen_fields.get(&name) {
+ self.push_diagnostic(InferenceDiagnostic::DuplicateField {
+ field: field.expr.into(),
+ variant,
+ });
+ variant_field_tys[*field_idx].get().instantiate(interner, args).skip_norm_wip()
+ } else {
+ self.push_diagnostic(InferenceDiagnostic::NoSuchField {
+ field: field.expr.into(),
+ private: None,
+ variant,
+ });
+ self.types.types.error
+ }
+ };
+
+ // Check that the expected field type is WF. Otherwise, we emit no use-site error
+ // in the case of coercions for non-WF fields, which leads to incorrect error
+ // tainting. See issue #126272.
+ self.table.register_wf_obligation(field_type.into(), ObligationCause::new(field.expr));
+
+ // Make sure to give a type to the field even if there's
+ // an error, so we can continue type-checking.
+ self.infer_expr_coerce(field.expr, &Expectation::has_type(field_type), ExprIsRead::Yes);
+ }
+
+ // Make sure the programmer specified correct number of fields.
+ if matches!(adt_id, AdtId::UnionId(_)) && hir_fields.len() != 1 {
+ self.push_diagnostic(InferenceDiagnostic::UnionExprMustHaveExactlyOneField { expr });
+ }
+
+ match base_expr {
+ RecordSpread::FieldDefaults => {
+ let mut missing_mandatory_fields = Vec::new();
+ let mut missing_optional_fields = Vec::new();
+ for (field_idx, field) in variant_fields.fields().iter() {
+ if remaining_fields.remove(&field.name).is_some() {
+ if field.default_value.is_none() {
+ missing_mandatory_fields.push(field_idx);
+ } else {
+ missing_optional_fields.push(field_idx);
+ }
+ }
+ }
+ if !missing_mandatory_fields.is_empty() {
+ // FIXME: Emit an error: missing fields.
+ }
+ }
+ RecordSpread::Expr(base_expr) => {
+ // FIXME: We are currently creating two branches here in order to maintain
+ // consistency. But they should be merged as much as possible.
+ if self.features.type_changing_struct_update {
+ if matches!(adt_id, AdtId::StructId(_)) {
+ // Make some fresh generic parameters for our ADT type.
+ let fresh_args = self.table.fresh_args_for_item(expr.into(), adt_id.into());
+ // We do subtyping on the FRU fields first, so we can
+ // learn exactly what types we expect the base expr
+ // needs constrained to be compatible with the struct
+ // type we expect from the expectation value.
+ for (field_idx, field) in variant_fields.fields().iter() {
+ let fru_ty = variant_field_tys[field_idx]
+ .get()
+ .instantiate(interner, fresh_args)
+ .skip_norm_wip();
+ if remaining_fields.remove(&field.name).is_some() {
+ let target_ty = variant_field_tys[field_idx]
+ .get()
+ .instantiate(interner, args)
+ .skip_norm_wip();
+ let cause = ObligationCause::new(expr);
+ match self.table.at(&cause).sup(target_ty, fru_ty) {
+ Ok(InferOk { obligations, value: () }) => {
+ self.table.register_predicates(obligations)
+ }
+ Err(_) => {
+ never!(
+ "subtyping remaining fields of type changing FRU \
+ failed: {target_ty:?} != {fru_ty:?}: {:?}",
+ field.name,
+ );
+ }
+ }
+ }
+ }
+ // The use of fresh args that we have subtyped against
+ // our base ADT type's fields allows us to guide inference
+ // along so that, e.g.
+ // ```
+ // MyStruct<'a, F1, F2, const C: usize> {
+ // f: F1,
+ // // Other fields that reference `'a`, `F2`, and `C`
+ // }
+ //
+ // let x = MyStruct {
+ // f: 1usize,
+ // ..other_struct
+ // };
+ // ```
+ // will have the `other_struct` expression constrained to
+ // `MyStruct<'a, _, F2, C>`, as opposed to just `_`...
+ // This is important to allow coercions to happen in
+ // `other_struct` itself. See `coerce-in-base-expr.rs`.
+ let fresh_base_ty = Ty::new_adt(self.interner(), adt_id, fresh_args);
+ self.infer_expr_suptype_coerce_never(
+ base_expr,
+ &Expectation::has_type(self.resolve_vars_if_possible(fresh_base_ty)),
+ ExprIsRead::Yes,
+ );
+ } else {
+ // Check the base_expr, regardless of a bad expected adt_ty, so we can get
+ // type errors on that expression, too.
+ self.infer_expr_no_expect(base_expr, ExprIsRead::Yes);
+ self.push_diagnostic(
+ InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr },
+ );
+ }
+ } else {
+ self.infer_expr_suptype_coerce_never(
+ base_expr,
+ &Expectation::has_type(adt_ty),
+ ExprIsRead::Yes,
+ );
+ if !matches!(adt_id, AdtId::StructId(_)) {
+ self.push_diagnostic(
+ InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr },
+ );
+ }
+ }
+ }
+ RecordSpread::None => {
+ if !matches!(adt_id, AdtId::UnionId(_))
+ && !remaining_fields.is_empty()
+ //~ non_exhaustive already reported, which will only happen for extern modules
+ && !self.has_applicable_non_exhaustive(adt_id.into())
+ {
+ debug!(?remaining_fields);
+
+ // FIXME: Emit an error: missing fields.
+ }
+ }
+ }
+ }
+
fn demand_scrutinee_type(
&mut self,
scrut: ExprId,
@@ -1114,16 +1253,16 @@ impl<'db> InferenceContext<'_, 'db> {
// ...but otherwise we want to use any supertype of the
// scrutinee. This is sort of a workaround, see note (*) in
// `check_pat` for some details.
- let scrut_ty = self.table.next_ty_var();
+ let scrut_ty = self.table.next_ty_var(scrut.into());
self.infer_expr_coerce_never(scrut, &Expectation::HasType(scrut_ty), scrutinee_is_read);
scrut_ty
}
}
fn infer_expr_path(&mut self, path: &Path, id: ExprOrPatId, scope_id: ExprId) -> Ty<'db> {
- let g = self.resolver.update_to_inner_scope(self.db, self.owner, scope_id);
+ let g = self.resolver.update_to_inner_scope(self.db, self.store_owner, scope_id);
let ty = match self.infer_path(path, id) {
- Some(ty) => ty,
+ Some((_, ty)) => ty,
None => {
if path.mod_path().is_some_and(|mod_path| mod_path.is_ident() || mod_path.is_self())
{
@@ -1149,7 +1288,7 @@ impl<'db> InferenceContext<'_, 'db> {
};
let mut oprnd_t = self.infer_expr_inner(oprnd, expected_inner, ExprIsRead::Yes);
- oprnd_t = self.table.structurally_resolve_type(oprnd_t);
+ oprnd_t = self.structurally_resolve_type(oprnd.into(), oprnd_t);
match unop {
UnaryOp::Deref => {
if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) {
@@ -1176,63 +1315,83 @@ impl<'db> InferenceContext<'_, 'db> {
}
oprnd_t
}
- fn infer_expr_array(&mut self, array: &Array, expected: &Expectation<'db>) -> Ty<'db> {
- let elem_ty = match expected
- .to_option(&mut self.table)
- .map(|t| self.table.try_structurally_resolve_type(t).kind())
- {
- Some(TyKind::Array(st, _) | TyKind::Slice(st)) => st,
- _ => self.table.next_ty_var(),
- };
- let krate = self.resolver.krate();
+ fn infer_array_repeat_expr(
+ &mut self,
+ element: ExprId,
+ count: ExprId,
+ expected: &Expectation<'db>,
+ expr: ExprId,
+ ) -> Ty<'db> {
+ let interner = self.interner();
+ let count_ct = self.create_body_anon_const(count, self.types.types.usize, true);
+ let count = self.table.try_structurally_resolve_const(count.into(), count_ct);
+
+ let uty = match expected {
+ Expectation::HasType(uty) => uty.builtin_index(),
+ _ => None,
+ };
- let expected = Expectation::has_type(elem_ty);
- let (elem_ty, len) = match array {
- Array::ElementList { elements, .. } if elements.is_empty() => {
- (elem_ty, consteval::usize_const(self.db, Some(0), krate))
+ let t = match uty {
+ Some(uty) => {
+ self.infer_expr_coerce(element, &Expectation::has_type(uty), ExprIsRead::Yes);
+ uty
}
- Array::ElementList { elements, .. } => {
- let mut coerce = CoerceMany::with_coercion_sites(elem_ty, elements);
- for &expr in elements.iter() {
- let cur_elem_ty = self.infer_expr_inner(expr, &expected, ExprIsRead::Yes);
- coerce.coerce(
- self,
- &ObligationCause::new(),
- expr,
- cur_elem_ty,
- ExprIsRead::Yes,
- );
- }
- (
- coerce.complete(self),
- consteval::usize_const(self.db, Some(elements.len() as u128), krate),
- )
+ None => {
+ let ty = self.table.next_ty_var(element.into());
+ self.infer_expr(element, &Expectation::has_type(ty), ExprIsRead::Yes);
+ ty
}
- &Array::Repeat { initializer, repeat } => {
- self.infer_expr_coerce(
- initializer,
- &Expectation::has_type(elem_ty),
- ExprIsRead::Yes,
- );
- let usize = self.types.types.usize;
- let len = match self.store[repeat] {
- Expr::Underscore => {
- self.write_expr_ty(repeat, usize);
- self.table.next_const_var()
- }
- _ => {
- self.infer_expr(repeat, &Expectation::HasType(usize), ExprIsRead::Yes);
- consteval::eval_to_const(repeat, self)
- }
- };
+ };
+
+ // We defer checking whether the element type is `Copy` as it is possible to have
+ // an inference variable as a repeat count and it seems unlikely that `Copy` would
+ // have inference side effects required for type checking to succeed.
+ // FIXME: Do it here like rustc.
+ // self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count));
+
+ let ty = Ty::new_array_with_const_len(interner, t, count);
+ self.table.register_wf_obligation(ty.into(), ObligationCause::new(expr));
+ ty
+ }
- (elem_ty, len)
+ fn infer_array_elements_expr(
+ &mut self,
+ args: &[ExprId],
+ expected: &Expectation<'db>,
+ expr: ExprId,
+ ) -> Ty<'db> {
+ let element_ty = if !args.is_empty() {
+ let coerce_to = expected
+ .to_option(&self.table)
+ .and_then(|uty| {
+ self.table
+ .resolve_vars_with_obligations(uty)
+ .builtin_index()
+ // Avoid using the original type variable as the coerce_to type, as it may resolve
+ // during the first coercion instead of being the LUB type.
+ .filter(|t| !self.table.resolve_vars_with_obligations(*t).is_ty_var())
+ })
+ .unwrap_or_else(|| self.table.next_ty_var(expr.into()));
+ let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args);
+
+ for &e in args {
+ // FIXME: the element expectation should use
+ // `try_structurally_resolve_and_adjust_for_branches` just like in `if` and `match`.
+ // While that fixes nested coercion, it will break [some
+ // code like this](https://github.com/rust-lang/rust/pull/140283#issuecomment-2958776528).
+ // If we find a way to support recursive tuple coercion, this break can be avoided.
+ let e_ty =
+ self.infer_expr_inner(e, &Expectation::has_type(coerce_to), ExprIsRead::Yes);
+ let cause = ObligationCause::new(e);
+ coerce.coerce(self, &cause, e, e_ty, ExprIsRead::Yes);
}
+ coerce.complete(self)
+ } else {
+ self.table.next_ty_var(expr.into())
};
- // Try to evaluate unevaluated constant, and insert variable if is not possible.
- let len = self.table.insert_const_vars_shallow(len);
- Ty::new_array_with_const_len(self.interner(), elem_ty, len)
+ let array_len = args.len() as u128;
+ Ty::new_array(self.interner(), element_ty, array_len)
}
pub(super) fn infer_return(&mut self, expr: ExprId) {
@@ -1244,7 +1403,13 @@ impl<'db> InferenceContext<'_, 'db> {
let return_expr_ty =
self.infer_expr_inner(expr, &Expectation::HasType(ret_ty), ExprIsRead::Yes);
let mut coerce_many = self.return_coercion.take().unwrap();
- coerce_many.coerce(self, &ObligationCause::new(), expr, return_expr_ty, ExprIsRead::Yes);
+ coerce_many.coerce(
+ self,
+ &ObligationCause::new(expr),
+ expr,
+ return_expr_ty,
+ ExprIsRead::Yes,
+ );
self.return_coercion = Some(coerce_many);
}
@@ -1258,7 +1423,7 @@ impl<'db> InferenceContext<'_, 'db> {
coerce.coerce_forced_unit(
self,
ret,
- &ObligationCause::new(),
+ &ObligationCause::new(ret),
true,
ExprIsRead::Yes,
);
@@ -1285,7 +1450,7 @@ impl<'db> InferenceContext<'_, 'db> {
// NB: this should *not* coerce.
// tail calls don't support any coercions except lifetimes ones (like `&'static u8 -> &'a u8`).
- self.unify(call_expr_ty, ret_ty);
+ _ = self.demand_eqtype(expr.into(), call_expr_ty, ret_ty);
}
None => {
// FIXME: diagnose `become` outside of functions
@@ -1311,16 +1476,7 @@ impl<'db> InferenceContext<'_, 'db> {
.unwrap_or_else(Expectation::none);
let inner_ty = self.infer_expr_inner(inner_expr, &inner_exp, ExprIsRead::Yes);
- Ty::new_adt(
- self.interner(),
- box_id,
- GenericArgs::fill_with_defaults(
- self.interner(),
- box_id.into(),
- [inner_ty.into()],
- |_, id, _| self.table.next_var_for_param(id),
- ),
- )
+ Ty::new_box(self.interner(), inner_ty)
} else {
self.err_ty()
}
@@ -1334,8 +1490,8 @@ impl<'db> InferenceContext<'_, 'db> {
label: Option<LabelId>,
expected: &Expectation<'db>,
) -> Ty<'db> {
- let coerce_ty = expected.coercion_target_type(&mut self.table);
- let g = self.resolver.update_to_inner_scope(self.db, self.owner, expr);
+ let coerce_ty = expected.coercion_target_type(&mut self.table, expr.into());
+ let g = self.resolver.update_to_inner_scope(self.db, self.store_owner, expr);
let (break_ty, ty) =
self.with_breakable_ctx(BreakableKind::Block, Some(coerce_ty), label, |this| {
@@ -1345,7 +1501,7 @@ impl<'db> InferenceContext<'_, 'db> {
let decl_ty = type_ref
.as_ref()
.map(|&tr| this.make_body_ty(tr))
- .unwrap_or_else(|| this.table.next_ty_var());
+ .unwrap_or_else(|| this.table.next_ty_var((*pat).into()));
let ty = if let Some(expr) = initializer {
// If we have a subpattern that performs a read, we want to consider this
@@ -1356,7 +1512,7 @@ impl<'db> InferenceContext<'_, 'db> {
} else {
ExprIsRead::No
};
- let ty = if contains_explicit_ref_binding(this.store, *pat) {
+ let ty = if this.contains_explicit_ref_binding(*pat) {
this.infer_expr(
*expr,
&Expectation::has_type(decl_ty),
@@ -1374,11 +1530,11 @@ impl<'db> InferenceContext<'_, 'db> {
decl_ty
};
- let decl = DeclContext {
- origin: DeclOrigin::LocalDecl { has_else: else_branch.is_some() },
- };
-
- this.infer_top_pat(*pat, ty, Some(decl));
+ this.infer_top_pat(
+ *pat,
+ ty,
+ PatOrigin::LetStmt { has_else: else_branch.is_some() },
+ );
if let Some(expr) = else_branch {
let previous_diverges =
mem::replace(&mut this.diverges, Diverges::Maybe);
@@ -1418,11 +1574,11 @@ impl<'db> InferenceContext<'_, 'db> {
// `!`).
if this.diverges.is_always() {
// we don't even make an attempt at coercion
- this.table.new_maybe_never_var()
+ this.table.new_maybe_never_var(expr.into())
} else if let Some(t) = expected.only_has_type(&mut this.table) {
if this
.coerce(
- expr.into(),
+ expr,
this.types.types.unit,
t,
AllowTwoPhase::No,
@@ -1430,13 +1586,7 @@ impl<'db> InferenceContext<'_, 'db> {
)
.is_err()
{
- this.result.type_mismatches.get_or_insert_default().insert(
- expr.into(),
- TypeMismatch {
- expected: t.store(),
- actual: this.types.types.unit.store(),
- },
- );
+ this.emit_type_mismatch(expr.into(), t, this.types.types.unit);
}
t
} else {
@@ -1451,11 +1601,12 @@ impl<'db> InferenceContext<'_, 'db> {
fn lookup_field(
&mut self,
+ field_expr: ExprId,
receiver_ty: Ty<'db>,
name: &Name,
) -> Option<(Ty<'db>, Either<FieldId, TupleFieldId>, Vec<Adjustment>, bool)> {
let interner = self.interner();
- let mut autoderef = self.table.autoderef_with_tracking(receiver_ty);
+ let mut autoderef = self.table.autoderef_with_tracking(receiver_ty, field_expr.into());
let mut private_field = None;
let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
let (field_id, parameters) = match derefed_ty.kind() {
@@ -1474,7 +1625,7 @@ impl<'db> InferenceContext<'_, 'db> {
})
});
}
- TyKind::Adt(adt, parameters) => match adt.def_id().0 {
+ TyKind::Adt(adt, parameters) => match adt.def_id() {
hir_def::AdtId::StructId(s) => {
let local_id = s.fields(self.db).field(name)?;
let field = FieldId { parent: s.into(), local_id };
@@ -1500,7 +1651,8 @@ impl<'db> InferenceContext<'_, 'db> {
}
let ty = self.db.field_types(field_id.parent)[field_id.local_id]
.get()
- .instantiate(interner, parameters);
+ .instantiate(interner, parameters)
+ .skip_norm_wip();
Some((Either::Left(field_id), ty))
});
@@ -1518,7 +1670,8 @@ impl<'db> InferenceContext<'_, 'db> {
self.table.register_infer_ok(autoderef.adjust_steps_as_infer_ok());
let ty = self.db.field_types(field_id.parent)[field_id.local_id]
.get()
- .instantiate(self.interner(), subst);
+ .instantiate(self.interner(), subst)
+ .skip_norm_wip();
let ty = self.process_remote_user_written_ty(ty);
(ty, Either::Left(field_id), adjustments, false)
@@ -1535,7 +1688,7 @@ impl<'db> InferenceContext<'_, 'db> {
) -> Ty<'db> {
// Field projections don't constitute reads.
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::No);
- let receiver_ty = self.table.structurally_resolve_type(receiver_ty);
+ let receiver_ty = self.structurally_resolve_type(receiver.into(), receiver_ty);
if name.is_missing() {
// Bail out early, don't even try to look up field. Also, we don't issue an unresolved
@@ -1543,7 +1696,7 @@ impl<'db> InferenceContext<'_, 'db> {
return self.err_ty();
}
- match self.lookup_field(receiver_ty, name) {
+ match self.lookup_field(tgt_expr, receiver_ty, name) {
Some((ty, field_id, adjustments, is_public)) => {
self.write_expr_adj(receiver, adjustments.into_boxed_slice());
self.result.field_resolutions.insert(tgt_expr, field_id);
@@ -1585,14 +1738,21 @@ impl<'db> InferenceContext<'_, 'db> {
fn instantiate_erroneous_method(&mut self, def_id: FunctionId) -> MethodCallee<'db> {
// FIXME: Using fresh infer vars for the method args isn't optimal,
// we can do better by going thorough the full probe/confirm machinery.
- let args = self.table.fresh_args_for_item(def_id.into());
- let sig = self.db.callable_item_signature(def_id.into()).instantiate(self.interner(), args);
- let sig =
- self.infcx().instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, sig);
+ let args = self.table.fresh_args_for_item(Span::Dummy, def_id.into());
+ let sig = self
+ .db
+ .callable_item_signature(def_id.into())
+ .instantiate(self.interner(), args)
+ .skip_norm_wip();
+ let sig = self.infcx().instantiate_binder_with_fresh_vars(
+ Span::Dummy,
+ BoundRegionConversionTime::FnCall,
+ sig,
+ );
MethodCallee { def_id, args, sig }
}
- fn check_call(
+ fn infer_method_call_as_call(
&mut self,
tgt_expr: ExprId,
args: &[ExprId],
@@ -1603,7 +1763,14 @@ impl<'db> InferenceContext<'_, 'db> {
is_varargs: bool,
expected: &Expectation<'db>,
) -> Ty<'db> {
- self.register_obligations_for_call(callee_ty);
+ if let TyKind::FnDef(def_id, args) = callee_ty.kind() {
+ let def_id = match def_id.0 {
+ CallableDefId::FunctionId(it) => it.into(),
+ CallableDefId::StructId(it) => it.into(),
+ CallableDefId::EnumVariantId(it) => it.loc(self.db).parent.into(),
+ };
+ self.add_required_obligations_for_value_path(tgt_expr.into(), def_id, args);
+ }
self.check_call_arguments(
tgt_expr,
@@ -1628,7 +1795,7 @@ impl<'db> InferenceContext<'_, 'db> {
expected: &Expectation<'db>,
) -> Ty<'db> {
let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::Yes);
- let receiver_ty = self.table.try_structurally_resolve_type(receiver_ty);
+ let receiver_ty = self.table.try_structurally_resolve_type(receiver.into(), receiver_ty);
let resolved = self.lookup_method_including_private(
receiver_ty,
@@ -1650,30 +1817,31 @@ impl<'db> InferenceContext<'_, 'db> {
// Failed to resolve, report diagnostic and try to resolve as call to field access or
// assoc function
Err(_) => {
- let field_with_same_name_exists = match self.lookup_field(receiver_ty, method_name)
- {
- Some((ty, field_id, adjustments, _public)) => {
- self.write_expr_adj(receiver, adjustments.into_boxed_slice());
- self.result.field_resolutions.insert(tgt_expr, field_id);
- Some(ty)
- }
- None => None,
- };
+ let field_with_same_name_exists =
+ match self.lookup_field(tgt_expr, receiver_ty, method_name) {
+ Some((ty, field_id, adjustments, _public)) => {
+ self.write_expr_adj(receiver, adjustments.into_boxed_slice());
+ self.result.field_resolutions.insert(tgt_expr, field_id);
+ Some(ty)
+ }
+ None => None,
+ };
- let assoc_func_with_same_name = self.with_method_resolution(|ctx| {
- if !matches!(
- receiver_ty.kind(),
- TyKind::Infer(InferTy::TyVar(_)) | TyKind::Error(_)
- ) {
- ctx.probe_for_name(
- method_resolution::Mode::Path,
- method_name.clone(),
- receiver_ty,
- )
- } else {
- Err(MethodError::ErrorReported)
- }
- });
+ let assoc_func_with_same_name =
+ self.with_method_resolution(tgt_expr.into(), receiver.into(), |ctx| {
+ if !matches!(
+ receiver_ty.kind(),
+ TyKind::Infer(InferTy::TyVar(_)) | TyKind::Error(_)
+ ) {
+ ctx.probe_for_name(
+ method_resolution::Mode::Path,
+ method_name.clone(),
+ receiver_ty,
+ )
+ } else {
+ Err(MethodError::ErrorReported)
+ }
+ });
let assoc_func_with_same_name = match assoc_func_with_same_name {
Ok(method_resolution::Pick {
item: CandidateId::FunctionId(def_id), ..
@@ -1709,7 +1877,7 @@ impl<'db> InferenceContext<'_, 'db> {
}),
};
match recovered {
- Some((callee_ty, sig, strip_first)) => self.check_call(
+ Some((callee_ty, sig, strip_first)) => self.infer_method_call_as_call(
tgt_expr,
args,
callee_ty,
@@ -1751,7 +1919,7 @@ impl<'db> InferenceContext<'_, 'db> {
expected,
args,
&[],
- sig.c_variadic,
+ sig.c_variadic(),
TupleArgumentsFlag::DontTupleArguments,
);
ret_ty
@@ -1778,7 +1946,7 @@ impl<'db> InferenceContext<'_, 'db> {
let formal_input_tys: Vec<_> = formal_input_tys
.iter()
.map(|&ty| {
- let generalized_ty = self.table.next_ty_var();
+ let generalized_ty = self.table.next_ty_var(call_expr.into());
let _ = self.demand_eqtype(call_expr.into(), ty, generalized_ty);
generalized_ty
})
@@ -1801,13 +1969,13 @@ impl<'db> InferenceContext<'_, 'db> {
// return type (likely containing type variables if the function
// is polymorphic) and the expected return type.
// No argument expectations are produced if unification fails.
- let origin = ObligationCause::new();
+ let origin = ObligationCause::new(call_expr);
ocx.sup(&origin, self.table.param_env, expected_output, formal_output)?;
for &ty in &formal_input_tys {
ocx.register_obligation(Obligation::new(
self.interner(),
- ObligationCause::new(),
+ ObligationCause::new(call_expr),
self.table.param_env,
ClauseKind::WellFormed(ty.into()),
));
@@ -1826,38 +1994,39 @@ impl<'db> InferenceContext<'_, 'db> {
.unwrap_or_default();
// If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
- let (formal_input_tys, expected_input_tys) =
- if tuple_arguments == TupleArgumentsFlag::TupleArguments {
- let tuple_type = self.table.structurally_resolve_type(formal_input_tys[0]);
- match tuple_type.kind() {
- // We expected a tuple and got a tuple
- TyKind::Tuple(arg_types) => {
- // Argument length differs
- if arg_types.len() != provided_args.len() {
- // FIXME: Emit an error.
- }
- let expected_input_tys = match expected_input_tys {
- Some(expected_input_tys) => match expected_input_tys.first() {
- Some(ty) => match ty.kind() {
- TyKind::Tuple(tys) => Some(tys.iter().collect()),
- _ => None,
- },
- None => None,
- },
- None => None,
- };
- (arg_types.iter().collect(), expected_input_tys)
- }
- _ => {
- // Otherwise, there's a mismatch, so clear out what we're expecting, and set
- // our input types to err_args so we don't blow up the error messages
+ let (formal_input_tys, expected_input_tys) = if tuple_arguments
+ == TupleArgumentsFlag::TupleArguments
+ {
+ let tuple_type = self.structurally_resolve_type(call_expr.into(), formal_input_tys[0]);
+ match tuple_type.kind() {
+ // We expected a tuple and got a tuple
+ TyKind::Tuple(arg_types) => {
+ // Argument length differs
+ if arg_types.len() != provided_args.len() {
// FIXME: Emit an error.
- (vec![self.types.types.error; provided_args.len()], None)
}
+ let expected_input_tys = match expected_input_tys {
+ Some(expected_input_tys) => match expected_input_tys.first() {
+ Some(ty) => match ty.kind() {
+ TyKind::Tuple(tys) => Some(tys.iter().collect()),
+ _ => None,
+ },
+ None => None,
+ },
+ None => None,
+ };
+ (arg_types.to_vec(), expected_input_tys)
}
- } else {
- (formal_input_tys.to_vec(), expected_input_tys)
- };
+ _ => {
+ // Otherwise, there's a mismatch, so clear out what we're expecting, and set
+ // our input types to err_args so we don't blow up the error messages
+ // FIXME: Emit an error.
+ (vec![self.types.types.error; provided_args.len()], None)
+ }
+ }
+ } else {
+ (formal_input_tys, expected_input_tys)
+ };
// If there are no external expectations at the call site, just use the types from the function defn
let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys {
@@ -1916,13 +2085,7 @@ impl<'db> InferenceContext<'_, 'db> {
let coerced_ty = this.table.resolve_vars_with_obligations(coerced_ty);
let coerce_error = this
- .coerce(
- provided_arg.into(),
- checked_ty,
- coerced_ty,
- AllowTwoPhase::Yes,
- ExprIsRead::Yes,
- )
+ .coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, ExprIsRead::Yes)
.err();
if coerce_error.is_some() {
return Err((coerce_error, coerced_ty, checked_ty));
@@ -1933,7 +2096,7 @@ impl<'db> InferenceContext<'_, 'db> {
let formal_ty_error = this
.table
.infer_ctxt
- .at(&ObligationCause::new(), this.table.param_env)
+ .at(&ObligationCause::new(provided_arg), this.table.param_env)
.eq(formal_input_ty, coerced_ty);
// If neither check failed, the types are compatible
@@ -1974,7 +2137,7 @@ impl<'db> InferenceContext<'_, 'db> {
// closure wrapped in a block.
// See <https://github.com/rust-lang/rust/issues/112225>.
let is_closure = if let Expr::Closure { closure_kind, .. } = self.store[*arg] {
- !matches!(closure_kind, ClosureKind::Coroutine(_))
+ !matches!(closure_kind, ClosureKind::OldCoroutine(_))
} else {
false
};
@@ -1992,10 +2155,7 @@ impl<'db> InferenceContext<'_, 'db> {
&& args_count_matches
{
// Don't report type mismatches if there is a mismatch in args count.
- self.result.type_mismatches.get_or_insert_default().insert(
- (*arg).into(),
- TypeMismatch { expected: expected.store(), actual: found.store() },
- );
+ self.emit_type_mismatch((*arg).into(), expected, found);
}
}
}
@@ -2003,40 +2163,6 @@ impl<'db> InferenceContext<'_, 'db> {
if !args_count_matches {}
}
- fn register_obligations_for_call(&mut self, callable_ty: Ty<'db>) {
- let callable_ty = self.table.try_structurally_resolve_type(callable_ty);
- if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind() {
- let generic_predicates = GenericPredicates::query_all(
- self.db,
- GenericDefId::from_callable(self.db, fn_def.0),
- );
- let param_env = self.table.param_env;
- self.table.register_predicates(clauses_as_obligations(
- generic_predicates.iter_instantiated(self.interner(), parameters.as_slice()),
- ObligationCause::new(),
- param_env,
- ));
- // add obligation for trait implementation, if this is a trait method
- match fn_def.0 {
- CallableDefId::FunctionId(f) => {
- if let ItemContainerId::TraitId(trait_) = f.lookup(self.db).container {
- // construct a TraitRef
- let trait_params_len = generics(self.db, trait_.into()).len();
- let substs =
- GenericArgs::new_from_slice(&parameters.as_slice()[..trait_params_len]);
- self.table.register_predicate(Obligation::new(
- self.interner(),
- ObligationCause::new(),
- self.table.param_env,
- TraitRef::new_from_args(self.interner(), trait_.into(), substs),
- ));
- }
- }
- CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {}
- }
- }
- }
-
pub(super) fn with_breakable_ctx<T>(
&mut self,
kind: BreakableKind,
diff --git a/crates/hir-ty/src/infer/fallback.rs b/crates/hir-ty/src/infer/fallback.rs
index c7669b346f..3744a434d2 100644
--- a/crates/hir-ty/src/infer/fallback.rs
+++ b/crates/hir-ty/src/infer/fallback.rs
@@ -1,6 +1,5 @@
//! Fallback of infer vars to `!` and `i32`/`f64`.
-use intern::sym;
use petgraph::{
Graph,
visit::{Dfs, Walker},
@@ -76,11 +75,11 @@ impl<'db> InferenceContext<'_, 'db> {
}
fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior {
- if self.krate().data(self.db).edition.at_least_2024() {
+ if self.edition.at_least_2024() {
return DivergingFallbackBehavior::ToNever;
}
- if self.resolver.def_map().is_unstable_feature_enabled(&sym::never_type_fallback) {
+ if self.features.never_type_fallback {
return DivergingFallbackBehavior::ContextDependent;
}
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
index b2369f6a87..c3b532638f 100644
--- a/crates/hir-ty/src/infer/mutability.rs
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -32,6 +32,7 @@ impl<'db> InferenceContext<'_, 'db> {
};
if let Some(infer_ok) = Self::try_mutable_overloaded_place_op(
&self.table,
+ tgt_expr,
source_ty,
None,
PlaceOp::Deref,
diff --git a/crates/hir-ty/src/infer/op.rs b/crates/hir-ty/src/infer/op.rs
index 95d63ffb50..9119af9628 100644
--- a/crates/hir-ty/src/infer/op.rs
+++ b/crates/hir-ty/src/infer/op.rs
@@ -2,8 +2,7 @@
use std::collections::hash_map;
-use hir_def::{GenericParamId, TraitId, hir::ExprId};
-use intern::{Symbol, sym};
+use hir_def::{FunctionId, GenericParamId, TraitId, hir::ExprId};
use rustc_ast_ir::Mutability;
use rustc_type_ir::inherent::{IntoKind, Ty as _};
use syntax::ast::{ArithOp, BinaryOp, UnaryOp};
@@ -38,7 +37,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
&& !rhs_ty.is_ty_var()
&& is_builtin_binop(lhs_ty, rhs_ty, category)
{
- self.enforce_builtin_binop_types(lhs_ty, rhs_ty, category);
+ self.enforce_builtin_binop_types(expr, lhs_ty, rhs_ty, category);
self.types.types.unit
} else {
return_ty
@@ -107,7 +106,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
&& is_builtin_binop(lhs_ty, rhs_ty, category)
{
let builtin_return_ty =
- self.enforce_builtin_binop_types(lhs_ty, rhs_ty, category);
+ self.enforce_builtin_binop_types(expr, lhs_ty, rhs_ty, category);
_ = self.demand_eqtype(expr.into(), builtin_return_ty, return_ty);
builtin_return_ty
} else {
@@ -119,6 +118,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
fn enforce_builtin_binop_types(
&mut self,
+ expr: ExprId,
lhs_ty: Ty<'db>,
rhs_ty: Ty<'db>,
category: BinOpCategory,
@@ -131,8 +131,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
match category {
BinOpCategory::Shortcircuit => {
- self.demand_suptype(self.types.types.bool, lhs_ty);
- self.demand_suptype(self.types.types.bool, rhs_ty);
+ _ = self.demand_suptype(expr.into(), self.types.types.bool, lhs_ty);
+ _ = self.demand_suptype(expr.into(), self.types.types.bool, rhs_ty);
self.types.types.bool
}
@@ -143,13 +143,13 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
BinOpCategory::Math | BinOpCategory::Bitwise => {
// both LHS and RHS and result will have the same type
- self.demand_suptype(lhs_ty, rhs_ty);
+ _ = self.demand_suptype(expr.into(), lhs_ty, rhs_ty);
lhs_ty
}
BinOpCategory::Comparison => {
// both LHS and RHS and result will have the same type
- self.demand_suptype(lhs_ty, rhs_ty);
+ _ = self.demand_suptype(expr.into(), lhs_ty, rhs_ty);
self.types.types.bool
}
}
@@ -179,7 +179,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
// in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
let lhs_ty = self.infer_expr_no_expect(lhs_expr, ExprIsRead::Yes);
- let fresh_var = self.table.next_ty_var();
+ let fresh_var = self.table.next_ty_var(lhs_expr.into());
self.demand_coerce(lhs_expr, lhs_ty, fresh_var, AllowTwoPhase::No, ExprIsRead::Yes)
}
};
@@ -191,8 +191,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// using this variable as the expected type, which sometimes lets
// us do better coercions than we would be able to do otherwise,
// particularly for things like `String + &String`.
- let rhs_ty_var = self.table.next_ty_var();
+ let rhs_ty_var = self.table.next_ty_var(rhs_expr.into());
let result = self.lookup_op_method(
+ expr,
lhs_ty,
Some((rhs_expr, rhs_ty_var)),
self.lang_item_for_bin_op(op),
@@ -264,7 +265,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
operand_ty: Ty<'db>,
op: UnaryOp,
) -> Ty<'db> {
- match self.lookup_op_method(operand_ty, None, self.lang_item_for_unop(op)) {
+ match self.lookup_op_method(ex, operand_ty, None, self.lang_item_for_unop(op)) {
Ok(method) => {
self.write_method_resolution(ex, method.def_id, method.args);
method.sig.output()
@@ -278,31 +279,32 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
fn lookup_op_method(
&mut self,
+ expr: ExprId,
lhs_ty: Ty<'db>,
opt_rhs: Option<(ExprId, Ty<'db>)>,
- (opname, trait_did): (Symbol, Option<TraitId>),
+ (op_method, trait_did): (Option<FunctionId>, Option<TraitId>),
) -> Result<MethodCallee<'db>, Vec<NextSolverError<'db>>> {
- let Some(trait_did) = trait_did else {
+ let (Some(trait_did), Some(op_method)) = (trait_did, op_method) else {
// Bail if the operator trait is not defined.
return Err(vec![]);
};
debug!(
"lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})",
- lhs_ty, opname, trait_did
+ lhs_ty, op_method, trait_did
);
let opt_rhs_ty = opt_rhs.map(|it| it.1);
- let cause = ObligationCause::new();
+ let cause = ObligationCause::new(expr);
// We don't consider any other candidates if this lookup fails
// so we can freely treat opaque types as inference variables here
// to allow more code to compile.
let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
let method = self.table.lookup_method_for_operator(
- cause.clone(),
- opname,
+ cause,
trait_did,
+ op_method,
lhs_ty,
opt_rhs_ty,
treat_opaques,
@@ -357,20 +359,20 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
}
}
- fn lang_item_for_bin_op(&self, op: BinaryOp) -> (Symbol, Option<TraitId>) {
- let (method_name, trait_lang_item) =
+ fn lang_item_for_bin_op(&self, op: BinaryOp) -> (Option<FunctionId>, Option<TraitId>) {
+ let (method, trait_lang_item) =
crate::lang_items::lang_items_for_bin_op(self.lang_items, op)
.expect("invalid operator provided");
- (method_name, trait_lang_item)
+ (method, trait_lang_item)
}
- fn lang_item_for_unop(&self, op: UnaryOp) -> (Symbol, Option<TraitId>) {
- let (method_name, trait_lang_item) = match op {
- UnaryOp::Not => (sym::not, self.lang_items.Not),
- UnaryOp::Neg => (sym::neg, self.lang_items.Neg),
+ fn lang_item_for_unop(&self, op: UnaryOp) -> (Option<FunctionId>, Option<TraitId>) {
+ let (method, trait_lang_item) = match op {
+ UnaryOp::Not => (self.lang_items.Not_not, self.lang_items.Not),
+ UnaryOp::Neg => (self.lang_items.Neg_neg, self.lang_items.Neg),
UnaryOp::Deref => panic!("Deref is not overloadable"),
};
- (method_name, trait_lang_item)
+ (method, trait_lang_item)
}
}
diff --git a/crates/hir-ty/src/infer/opaques.rs b/crates/hir-ty/src/infer/opaques.rs
index a39288721b..63149deb82 100644
--- a/crates/hir-ty/src/infer/opaques.rs
+++ b/crates/hir-ty/src/infer/opaques.rs
@@ -4,6 +4,7 @@ use rustc_type_ir::{TypeVisitableExt, fold_regions};
use tracing::{debug, instrument};
use crate::{
+ Span,
infer::InferenceContext,
next_solver::{
EarlyBinder, OpaqueTypeKey, SolverDefId, TypingMode,
@@ -68,13 +69,13 @@ impl<'db> InferenceContext<'_, 'db> {
mut opaque_types: Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)>,
) {
for entry in opaque_types.iter_mut() {
- *entry = self.table.infer_ctxt.resolve_vars_if_possible(*entry);
+ *entry = self.resolve_vars_if_possible(*entry);
}
debug!(?opaque_types);
let interner = self.interner();
let TypingMode::Analysis { defining_opaque_types_and_generators } =
- self.table.infer_ctxt.typing_mode()
+ self.table.infer_ctxt.typing_mode_raw()
else {
unreachable!();
};
@@ -107,8 +108,9 @@ impl<'db> InferenceContext<'_, 'db> {
continue;
}
- let expected =
- EarlyBinder::bind(ty.ty).instantiate(interner, opaque_type_key.args);
+ let expected = EarlyBinder::bind(ty.ty)
+ .instantiate(interner, opaque_type_key.args)
+ .skip_norm_wip();
_ = self.demand_eqtype_fixme_no_diag(expected, hidden_type.ty);
}
@@ -135,7 +137,8 @@ impl<'db> InferenceContext<'_, 'db> {
return UsageKind::UnconstrainedHiddenType(hidden_type);
}
- let cause = ObligationCause::new();
+ // FIXME: This should not use a dummy span.
+ let cause = ObligationCause::new(Span::Dummy);
let at = self.table.infer_ctxt.at(&cause, self.table.param_env);
let hidden_type = match at.deeply_normalize(hidden_type) {
Ok(hidden_type) => hidden_type,
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 8033680dcc..f21438647c 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -1,647 +1,1545 @@
//! Type inference for patterns.
-use std::{cmp, iter};
+use std::{
+ cmp,
+ collections::hash_map::Entry::{Occupied, Vacant},
+ iter,
+};
use hir_def::{
- HasModule as _,
- expr_store::{ExpressionStore, path::Path},
- hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId},
+ AdtId, LocalFieldId, VariantId,
+ expr_store::path::Path,
+ hir::{
+ BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId,
+ RecordFieldPat,
+ },
+ resolver::ValueNs,
signatures::VariantFields,
};
-use hir_expand::name::Name;
use rustc_ast_ir::Mutability;
-use rustc_type_ir::inherent::{GenericArg as _, GenericArgs as _, IntoKind, Ty as _};
-use stdx::TupleExt;
+use rustc_hash::FxHashMap;
+use rustc_type_ir::{
+ TypeVisitableExt as _,
+ inherent::{IntoKind as _, Ty as _},
+};
+use span::Edition;
+use tracing::{debug, instrument, trace};
use crate::{
- DeclContext, DeclOrigin, InferenceDiagnostic,
- consteval::{self, try_const_usize, usize_const},
+ BindingMode, InferenceDiagnostic, Span,
infer::{
- AllowTwoPhase, BindingMode, Expectation, InferenceContext, TypeMismatch, expr::ExprIsRead,
+ AllowTwoPhase, ByRef, Expectation, InferenceContext, PatAdjust, PatAdjustment,
+ expr::ExprIsRead,
+ },
+ next_solver::{
+ Const, TraitRef, Ty, TyKind, Tys,
+ infer::{
+ InferOk,
+ traits::{Obligation, ObligationCause},
+ },
},
- lower::lower_mutability,
- next_solver::{GenericArgs, Ty, TyKind, Tys, infer::traits::ObligationCause},
+ utils::EnumerateAndAdjustIterator,
};
-impl<'db> InferenceContext<'_, 'db> {
- /// Infers type for tuple struct pattern or its corresponding assignee expression.
+impl ByRef {
+ #[must_use]
+ fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self {
+ if let ByRef::Yes(old_mutbl) = &mut self {
+ *old_mutbl = cmp::min(*old_mutbl, mutbl);
+ }
+ self
+ }
+}
+
+impl BindingMode {
+ fn from_annotation(annotation: BindingAnnotation) -> BindingMode {
+ match annotation {
+ BindingAnnotation::Unannotated => BindingMode(ByRef::No, Mutability::Not),
+ BindingAnnotation::Mutable => BindingMode(ByRef::No, Mutability::Mut),
+ BindingAnnotation::Ref => BindingMode(ByRef::Yes(Mutability::Not), Mutability::Not),
+ BindingAnnotation::RefMut => BindingMode(ByRef::Yes(Mutability::Mut), Mutability::Not),
+ }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub(super) enum PatOrigin {
+ LetExpr,
+ LetStmt { has_else: bool },
+ Param,
+ MatchArm,
+ DestructuringAssignment,
+}
+
+impl PatOrigin {
+ fn default_binding_modes(self) -> bool {
+ self != PatOrigin::DestructuringAssignment
+ }
+}
+
+#[derive(Copy, Clone)]
+struct PatInfo {
+ binding_mode: ByRef,
+ max_ref_mutbl: MutblCap,
+ pat_origin: PatOrigin,
+}
+
+/// Mode for adjusting the expected type and binding mode.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum AdjustMode {
+ /// Peel off all immediate reference types. If the `deref_patterns` feature is enabled, this
+ /// also peels smart pointer ADTs.
+ Peel { kind: PeelKind },
+ /// Pass on the input binding mode and expected type.
+ Pass,
+}
+
+/// Restrictions on what types to peel when adjusting the expected type and binding mode.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum PeelKind {
+ /// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference
+ /// any number of `&`/`&mut` references, plus a single smart pointer.
+ ExplicitDerefPat,
+ /// Implicitly peel references, and if `deref_patterns` is enabled, smart pointer ADTs.
+ Implicit {
+ /// The ADT the pattern is a constructor for, if applicable, so that we don't peel it. See
+ /// [`ResolvedPat`] for more information.
+ until_adt: Option<AdtId>,
+ /// The number of references at the head of the pattern's type, so we can leave that many
+ /// untouched. This is `1` for string literals, and `0` for most patterns.
+ pat_ref_layers: usize,
+ },
+}
+
+impl AdjustMode {
+ const fn peel_until_adt(opt_adt_def: Option<AdtId>) -> AdjustMode {
+ AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def, pat_ref_layers: 0 } }
+ }
+ const fn peel_all() -> AdjustMode {
+ AdjustMode::peel_until_adt(None)
+ }
+}
+
+/// `ref mut` bindings (explicit or match-ergonomics) are not allowed behind an `&` reference.
+/// Normally, the borrow checker enforces this, but for (currently experimental) match ergonomics,
+/// we track this when typing patterns for two purposes:
+///
+/// - For RFC 3627's Rule 3, when this would prevent us from binding with `ref mut`, we limit the
+/// default binding mode to be by shared `ref` when it would otherwise be `ref mut`.
+///
+/// - For RFC 3627's Rule 5, we allow `&` patterns to match against `&mut` references, treating them
+/// as if they were shared references. Since the scrutinee is mutable in this case, the borrow
+/// checker won't catch if we bind with `ref mut`, so we need to throw an error ourselves.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum MutblCap {
+ /// Mutability restricted to immutable.
+ Not,
+
+ /// Mutability restricted to immutable, but only because of the pattern
+ /// (not the scrutinee type).
///
- /// Ellipses found in the original pattern or expression must be filtered out.
- pub(super) fn infer_tuple_struct_pat_like(
+ /// The contained span, if present, points to an `&` pattern
+ /// that is the reason for the restriction,
+ /// and which will be reported in a diagnostic.
+ WeaklyNot,
+
+ /// No restriction on mutability
+ Mut,
+}
+
+impl MutblCap {
+ #[must_use]
+ fn cap_to_weakly_not(self) -> Self {
+ match self {
+ MutblCap::Not => MutblCap::Not,
+ _ => MutblCap::WeaklyNot,
+ }
+ }
+
+ #[must_use]
+ fn as_mutbl(self) -> Mutability {
+ match self {
+ MutblCap::Not | MutblCap::WeaklyNot => Mutability::Not,
+ MutblCap::Mut => Mutability::Mut,
+ }
+ }
+}
+
+/// Variations on RFC 3627's Rule 4: when do reference patterns match against inherited references?
+///
+/// "Inherited reference" designates the `&`/`&mut` types that arise from using match ergonomics, i.e.
+/// from matching a reference type with a non-reference pattern. E.g. when `Some(x)` matches on
+/// `&mut Option<&T>`, `x` gets type `&mut &T` and the outer `&mut` is considered "inherited".
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum InheritedRefMatchRule {
+ /// Reference patterns consume only the inherited reference if possible, regardless of whether
+ /// the underlying type being matched against is a reference type. If there is no inherited
+ /// reference, a reference will be consumed from the underlying type.
+ EatOuter,
+ /// Reference patterns consume only a reference from the underlying type if possible. If the
+ /// underlying type is not a reference type, the inherited reference will be consumed.
+ EatInner,
+ /// When the underlying type is a reference type, reference patterns consume both layers of
+ /// reference, i.e. they both reset the binding mode and consume the reference type.
+ EatBoth {
+ /// If `true`, an inherited reference will be considered when determining whether a reference
+ /// pattern matches a given type:
+ /// - If the underlying type is not a reference, a reference pattern may eat the inherited reference;
+ /// - If the underlying type is a reference, a reference pattern matches if it can eat either one
+ /// of the underlying and inherited references. E.g. a `&mut` pattern is allowed if either the
+ /// underlying type is `&mut` or the inherited reference is `&mut`.
+ ///
+ /// If `false`, a reference pattern is only matched against the underlying type.
+ /// This is `false` for stable Rust and `true` for both the `ref_pat_eat_one_layer_2024` and
+ /// `ref_pat_eat_one_layer_2024_structural` feature gates.
+ consider_inherited_ref: bool,
+ },
+}
+
+/// When checking patterns containing paths, we need to know the path's resolution to determine
+/// whether to apply match ergonomics and implicitly dereference the scrutinee. For instance, when
+/// the `deref_patterns` feature is enabled and we're matching against a scrutinee of type
+/// `Cow<'a, Option<u8>>`, we insert an implicit dereference to allow the pattern `Some(_)` to type,
+/// but we must not dereference it when checking the pattern `Cow::Borrowed(_)`.
+///
+/// `ResolvedPat` contains the information from resolution needed to determine match ergonomics
+/// adjustments, and to finish checking the pattern once we know its adjusted type.
+#[derive(Clone, Copy, Debug)]
+struct ResolvedPat<'db> {
+ /// The type of the pattern, to be checked against the type of the scrutinee after peeling. This
+ /// is also used to avoid peeling the scrutinee's constructors (see the `Cow` example above).
+ ty: Ty<'db>,
+ kind: ResolvedPatKind,
+}
+
+#[derive(Clone, Copy, Debug)]
+enum ResolvedPatKind {
+ Path { res: ValueNs },
+ Struct { variant: VariantId },
+ TupleStruct { variant: VariantId },
+}
+
+impl<'db> ResolvedPat<'db> {
+ fn adjust_mode(&self) -> AdjustMode {
+ if let ResolvedPatKind::Path { res, .. } = self.kind
+ && matches!(res, ValueNs::ConstId(_))
+ {
+ // These constants can be of a reference type, e.g. `const X: &u8 = &0;`.
+ // Peeling the reference types too early will cause type checking failures.
+ // Although it would be possible to *also* peel the types of the constants too.
+ AdjustMode::Pass
+ } else {
+ // The remaining possible resolutions for path, struct, and tuple struct patterns are
+ // ADT constructors. As such, we may peel references freely, but we must not peel the
+ // ADT itself from the scrutinee if it's a smart pointer.
+ AdjustMode::peel_until_adt(self.ty.as_adt().map(|(adt, _)| adt))
+ }
+ }
+}
+
+impl<'a, 'db> InferenceContext<'a, 'db> {
+ /// Experimental pattern feature: after matching against a shared reference, do we limit the
+ /// default binding mode in subpatterns to be `ref` when it would otherwise be `ref mut`?
+ /// This corresponds to Rule 3 of RFC 3627.
+ fn downgrade_mut_inside_shared(&self) -> bool {
+ // NB: RFC 3627 proposes stabilizing Rule 3 in all editions. If we adopt the same behavior
+ // across all editions, this may be removed.
+ self.features.ref_pat_eat_one_layer_2024_structural
+ }
+
+ /// Experimental pattern feature: when do reference patterns match against inherited references?
+ /// This corresponds to variations on Rule 4 of RFC 3627.
+ fn ref_pat_matches_inherited_ref(&self, edition: Edition) -> InheritedRefMatchRule {
+ // NB: The particular rule used here is likely to differ across editions, so calls to this
+ // may need to become edition checks after match ergonomics stabilize.
+ if edition.at_least_2024() {
+ if self.features.ref_pat_eat_one_layer_2024 {
+ InheritedRefMatchRule::EatOuter
+ } else if self.features.ref_pat_eat_one_layer_2024_structural {
+ InheritedRefMatchRule::EatInner
+ } else {
+ // Currently, matching against an inherited ref on edition 2024 is an error.
+ // Use `EatBoth` as a fallback to be similar to stable Rust.
+ InheritedRefMatchRule::EatBoth { consider_inherited_ref: false }
+ }
+ } else {
+ InheritedRefMatchRule::EatBoth {
+ consider_inherited_ref: self.features.ref_pat_eat_one_layer_2024
+ || self.features.ref_pat_eat_one_layer_2024_structural,
+ }
+ }
+ }
+
+ /// Experimental pattern feature: do `&` patterns match against `&mut` references, treating them
+ /// as if they were shared references? This corresponds to Rule 5 of RFC 3627.
+ fn ref_pat_matches_mut_ref(&self) -> bool {
+ // NB: RFC 3627 proposes stabilizing Rule 5 in all editions. If we adopt the same behavior
+ // across all editions, this may be removed.
+ self.features.ref_pat_eat_one_layer_2024
+ || self.features.ref_pat_eat_one_layer_2024_structural
+ }
+
+ /// Type check the given top level pattern against the `expected` type.
+ ///
+ /// If a `Some(span)` is provided and `origin_expr` holds,
+ /// then the `span` represents the scrutinee's span.
+ /// The scrutinee is found in e.g. `match scrutinee { ... }` and `let pat = scrutinee;`.
+ ///
+ /// Otherwise, `Some(span)` represents the span of a type expression
+ /// which originated the `expected` type.
+ pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: Ty<'db>, pat_origin: PatOrigin) {
+ let pat_info =
+ PatInfo { binding_mode: ByRef::No, max_ref_mutbl: MutblCap::Mut, pat_origin };
+ self.infer_pat(pat, expected, pat_info);
+ }
+
+ /// Type check the given `pat` against the `expected` type
+ /// with the provided `binding_mode` (default binding mode).
+ ///
+ /// Outside of this module, `check_pat_top` should always be used.
+ /// Conversely, inside this module, `check_pat_top` should never be used.
+ #[instrument(level = "debug", skip(self, pat_info))]
+ fn infer_pat(&mut self, pat_id: PatId, expected: Ty<'db>, pat_info: PatInfo) {
+ // For patterns containing paths, we need the path's resolution to determine whether to
+ // implicitly dereference the scrutinee before matching.
+ let pat = &self.store[pat_id];
+ let opt_path_res = match pat {
+ Pat::Path(path) => Some(self.resolve_pat_path(pat_id, path)),
+ Pat::Record { path, .. } => Some(self.resolve_record_pat(pat_id, path)),
+ Pat::TupleStruct { path, .. } => Some(self.resolve_tuple_struct_pat(pat_id, path)),
+ _ => None,
+ };
+ let adjust_mode = self.calc_adjust_mode(pat_id, pat, opt_path_res);
+ let ty = self.infer_pat_inner(pat_id, opt_path_res, adjust_mode, expected, pat_info);
+ let ty = self.insert_type_vars_shallow(ty);
+ self.write_pat_ty(pat_id, ty);
+
+ // If we implicitly inserted overloaded dereferences before matching check the pattern to
+ // see if the dereferenced types need `DerefMut` bounds.
+ if let Some(derefed_tys) = self.result.pat_adjustment(pat_id)
+ && derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref)
+ {
+ let infer_ok = self.register_deref_mut_bounds_if_needed(
+ pat_id,
+ pat_id,
+ derefed_tys.iter().filter_map(|adjust| match adjust.kind {
+ PatAdjust::OverloadedDeref => Some(adjust.source.as_ref()),
+ PatAdjust::BuiltinDeref => None,
+ }),
+ );
+ self.table.register_infer_ok(infer_ok);
+ }
+
+ // (note_1): In most of the cases where (note_1) is referenced
+ // (literals and constants being the exception), we relate types
+ // using strict equality, even though subtyping would be sufficient.
+ // There are a few reasons for this, some of which are fairly subtle
+ // and which cost me (nmatsakis) an hour or two debugging to remember,
+ // so I thought I'd write them down this time.
+ //
+ // 1. There is no loss of expressiveness here, though it does
+ // cause some inconvenience. What we are saying is that the type
+ // of `x` becomes *exactly* what is expected. This can cause unnecessary
+ // errors in some cases, such as this one:
+ //
+ // ```
+ // fn foo<'x>(x: &'x i32) {
+ // let a = 1;
+ // let mut z = x;
+ // z = &a;
+ // }
+ // ```
+ //
+ // The reason we might get an error is that `z` might be
+ // assigned a type like `&'x i32`, and then we would have
+ // a problem when we try to assign `&a` to `z`, because
+ // the lifetime of `&a` (i.e., the enclosing block) is
+ // shorter than `'x`.
+ //
+ // HOWEVER, this code works fine. The reason is that the
+ // expected type here is whatever type the user wrote, not
+ // the initializer's type. In this case the user wrote
+ // nothing, so we are going to create a type variable `Z`.
+ // Then we will assign the type of the initializer (`&'x i32`)
+ // as a subtype of `Z`: `&'x i32 <: Z`. And hence we
+ // will instantiate `Z` as a type `&'0 i32` where `'0` is
+ // a fresh region variable, with the constraint that `'x : '0`.
+ // So basically we're all set.
+ //
+ // Note that there are two tests to check that this remains true
+ // (`regions-reassign-{match,let}-bound-pointer.rs`).
+ //
+ // 2. An outdated issue related to the old HIR borrowck. See the test
+ // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`,
+ }
+
+ // Helper to avoid resolving the same path pattern several times.
+ fn infer_pat_inner(
&mut self,
- path: Option<&Path>,
+ pat: PatId,
+ opt_path_res: Option<Result<ResolvedPat<'db>, ()>>,
+ adjust_mode: AdjustMode,
expected: Ty<'db>,
- default_bm: BindingMode,
- id: PatId,
- ellipsis: Option<u32>,
- subs: &[PatId],
- decl: Option<DeclContext>,
+ pat_info: PatInfo,
) -> Ty<'db> {
- let (ty, def) = self.resolve_variant(id.into(), path, true);
- let var_data = def.map(|it| it.fields(self.db));
- if let Some(variant) = def {
- self.write_variant_resolution(id.into(), variant);
- }
- if let Some(var) = &var_data {
- let cmp = if ellipsis.is_some() { usize::gt } else { usize::ne };
-
- if cmp(&subs.len(), &var.fields().len()) {
- self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount {
- pat: id.into(),
- expected: var.fields().len(),
- found: subs.len(),
- });
- }
+ #[cfg(debug_assertions)]
+ if matches!(pat_info.binding_mode, ByRef::Yes(Mutability::Mut))
+ && pat_info.max_ref_mutbl != MutblCap::Mut
+ && self.downgrade_mut_inside_shared()
+ {
+ panic!("Pattern mutability cap violated!");
}
- self.unify(ty, expected);
+ // Resolve type if needed.
+ let expected = if let AdjustMode::Peel { .. } = adjust_mode
+ && pat_info.pat_origin.default_binding_modes()
+ {
+ self.table.try_structurally_resolve_type(pat.into(), expected)
+ } else {
+ expected
+ };
+
+ match self.store[pat] {
+ // Peel off a `&` or `&mut`from the scrutinee type. See the examples in
+ // `tests/ui/rfcs/rfc-2005-default-binding-mode`.
+ _ if let AdjustMode::Peel { kind: peel_kind } = adjust_mode
+ && pat_info.pat_origin.default_binding_modes()
+ && let TyKind::Ref(_, inner_ty, inner_mutability) = expected.kind()
+ && self.should_peel_ref(peel_kind, expected) =>
+ {
+ debug!("inspecting {:?}", expected);
+
+ debug!("current discriminant is Ref, inserting implicit deref");
+ // Preserve the reference type. We'll need it later during THIR lowering.
+ self.result.pat_adjustments.entry(pat).or_default().push(PatAdjustment {
+ kind: PatAdjust::BuiltinDeref,
+ source: expected.store(),
+ });
- match def {
- _ if subs.is_empty() => {}
- Some(def) => {
- let field_types = self.db.field_types(def);
- let variant_data = def.fields(self.db);
- let visibilities = VariantFields::field_visibilities(self.db, def);
+ // Use the old pat info to keep `current_depth` to its old value.
+ let new_pat_info = self.adjust_pat_info(inner_mutability, pat_info);
- let (pre, post) = match ellipsis {
- Some(idx) => subs.split_at(idx as usize),
- None => (subs, &[][..]),
+ // Recurse with the new expected type.
+ self.infer_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info)
+ }
+ // If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
+ // examples in `tests/ui/pattern/deref_patterns/`.
+ _ if self.features.deref_patterns
+ && let AdjustMode::Peel { kind: peel_kind } = adjust_mode
+ && pat_info.pat_origin.default_binding_modes()
+ && self.should_peel_smart_pointer(peel_kind, expected) =>
+ {
+ debug!("scrutinee ty {expected:?} is a smart pointer, inserting pin deref");
+
+ // The scrutinee is a smart pointer; implicitly dereference it. This adds a
+ // requirement that `expected: DerefPure`.
+ let inner_ty = self.deref_pat_target(pat, expected);
+ // Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
+ // `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`.
+
+ self.check_deref_pattern(
+ pat,
+ opt_path_res,
+ adjust_mode,
+ expected,
+ inner_ty,
+ PatAdjust::OverloadedDeref,
+ pat_info,
+ )
+ }
+ Pat::Missing => self.types.types.error,
+ Pat::Wild | Pat::Rest => expected,
+ // We allow any type here; we ensure that the type is uninhabited during match checking.
+ // Pat::Never => expected,
+ Pat::Path(_) => {
+ let ty = match opt_path_res.unwrap() {
+ Ok(ref pr) => self.infer_pat_path(pat, pr, expected),
+ Err(()) => self.types.types.error,
};
- let post_idx_offset = field_types.iter().count().saturating_sub(post.len());
-
- let pre_iter = pre.iter().enumerate();
- let post_iter = (post_idx_offset..).zip(post.iter());
-
- let substs = ty.as_adt().map(TupleExt::tail);
-
- for (i, &subpat) in pre_iter.chain(post_iter) {
- let expected_ty = {
- match variant_data.field(&Name::new_tuple_field(i)) {
- Some(local_id) => {
- if !visibilities[local_id]
- .is_visible_from(self.db, self.resolver.module())
- {
- // FIXME(DIAGNOSE): private tuple field
- }
- let f = field_types[local_id].get();
- let expected_ty = match substs {
- Some(substs) => f.instantiate(self.interner(), substs),
- None => f.instantiate(self.interner(), &[]),
- };
- self.process_remote_user_written_ty(expected_ty)
- }
- None => self.err_ty(),
+ self.write_pat_ty(pat, ty);
+ ty
+ }
+ Pat::Lit(expr) => self.infer_lit_pat(expr, expected),
+ Pat::Range { start: lhs, end: rhs, .. } => {
+ self.infer_range_pat(pat, lhs, rhs, expected)
+ }
+ Pat::Bind { id: var_id, subpat } => {
+ self.infer_bind_pat(pat, var_id, subpat, expected, pat_info)
+ }
+ Pat::TupleStruct { args: ref subpats, ellipsis: ddpos, .. } => match opt_path_res
+ .unwrap()
+ {
+ Ok(ResolvedPat { ty, kind: ResolvedPatKind::TupleStruct { variant } }) => self
+ .infer_tuple_struct_pat(pat, subpats, ddpos, ty, variant, expected, pat_info),
+ Err(()) => {
+ let ty_err = self.types.types.error;
+ for &subpat in subpats {
+ self.infer_pat(subpat, ty_err, pat_info);
+ }
+ ty_err
+ }
+ Ok(pr) => panic!("tuple struct pattern resolved to {pr:?}"),
+ },
+ Pat::Record { args: ref fields, ellipsis: has_rest_pat, .. } => {
+ match opt_path_res.unwrap() {
+ Ok(ResolvedPat { ty, kind: ResolvedPatKind::Struct { variant } }) => self
+ .infer_record_pat(
+ pat,
+ fields,
+ has_rest_pat,
+ ty,
+ variant,
+ expected,
+ pat_info,
+ ),
+ Err(()) => {
+ let ty_err = self.types.types.error;
+ for field in fields {
+ self.infer_pat(field.pat, ty_err, pat_info);
}
- };
-
- self.infer_pat(subpat, expected_ty, default_bm, decl);
+ ty_err
+ }
+ Ok(pr) => panic!("struct pattern resolved to {pr:?}"),
}
}
- None => {
- let err_ty = self.err_ty();
- for &inner in subs {
- self.infer_pat(inner, err_ty, default_bm, decl);
+ // Pat::Guard(pat, cond) => {
+ // self.infer_pat(pat, expected, pat_info);
+ // self.check_expr_has_type_or_error(cond, self.tcx.types.bool, |_| {});
+ // expected
+ // }
+ Pat::Or(ref pats) => {
+ for &pat in pats {
+ self.infer_pat(pat, expected, pat_info);
}
+ expected
+ }
+ Pat::Tuple { args: ref elements, ellipsis: ddpos } => {
+ self.infer_tuple_pat(pat, elements, ddpos, expected, pat_info)
+ }
+ Pat::Box { inner } => self.infer_box_pat(pat, inner, expected, pat_info),
+ Pat::Deref { inner } => self.infer_deref_pat(pat, inner, expected, pat_info),
+ // Pat::Deref(inner) => self.infer_deref_pat(pat.span, inner, expected, pat_info),
+ Pat::Ref { pat: inner, mutability: mutbl } => self.infer_ref_pat(
+ pat,
+ inner,
+ if mutbl.is_mut() { Mutability::Mut } else { Mutability::Not },
+ expected,
+ pat_info,
+ ),
+ Pat::Slice { prefix: ref before, slice, suffix: ref after } => {
+ self.infer_slice_pat(pat, before, slice, after, expected, pat_info)
+ }
+ Pat::Expr(expr) => self.infer_destructuring_assignment_expr(expr, expected),
+ Pat::ConstBlock(expr) => {
+ self.infer_expr(expr, &Expectation::has_type(expected), ExprIsRead::Yes)
}
}
+ }
- ty
+ fn adjust_pat_info(&self, inner_mutability: Mutability, pat_info: PatInfo) -> PatInfo {
+ let mut binding_mode = match pat_info.binding_mode {
+ // If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const`
+ // or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or
+ // `&pin mut`).
+ ByRef::No => ByRef::Yes(inner_mutability),
+ ByRef::Yes(mutability) => {
+ let mutability = match mutability {
+ // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
+ Mutability::Mut => inner_mutability,
+ // Once a `ref`, always a `ref`.
+ // This is because a `& &mut` cannot mutate the underlying value.
+ Mutability::Not => Mutability::Not,
+ };
+ ByRef::Yes(mutability)
+ }
+ };
+
+ let PatInfo { mut max_ref_mutbl, .. } = pat_info;
+ if self.downgrade_mut_inside_shared() {
+ binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
+ }
+ match binding_mode {
+ ByRef::Yes(Mutability::Not) => max_ref_mutbl = MutblCap::Not,
+ _ => {}
+ }
+ debug!("default binding mode is now {:?}", binding_mode);
+ PatInfo { binding_mode, max_ref_mutbl, ..pat_info }
}
- /// Infers type for record pattern or its corresponding assignee expression.
- pub(super) fn infer_record_pat_like(
+ fn check_deref_pattern(
&mut self,
- path: Option<&Path>,
+ pat: PatId,
+ opt_path_res: Option<Result<ResolvedPat<'db>, ()>>,
+ adjust_mode: AdjustMode,
expected: Ty<'db>,
- default_bm: BindingMode,
- id: PatId,
- subs: impl ExactSizeIterator<Item = (Name, PatId)>,
- decl: Option<DeclContext>,
+ mut inner_ty: Ty<'db>,
+ pat_adjust_kind: PatAdjust,
+ pat_info: PatInfo,
) -> Ty<'db> {
- let (ty, def) = self.resolve_variant(id.into(), path, false);
- if let Some(variant) = def {
- self.write_variant_resolution(id.into(), variant);
- }
-
- self.unify(ty, expected);
-
- match def {
- _ if subs.len() == 0 => {}
- Some(def) => {
- let field_types = self.db.field_types(def);
- let variant_data = def.fields(self.db);
- let visibilities = VariantFields::field_visibilities(self.db, def);
-
- let substs = ty.as_adt().map(TupleExt::tail);
-
- for (name, inner) in subs {
- let expected_ty = {
- match variant_data.field(&name) {
- Some(local_id) => {
- if !visibilities[local_id]
- .is_visible_from(self.db, self.resolver.module())
- {
- self.push_diagnostic(InferenceDiagnostic::NoSuchField {
- field: inner.into(),
- private: Some(local_id),
- variant: def,
- });
- }
- let f = field_types[local_id].get();
- let expected_ty = match substs {
- Some(substs) => f.instantiate(self.interner(), substs),
- None => f.instantiate(self.interner(), &[]),
- };
- self.process_remote_user_written_ty(expected_ty)
- }
- None => {
- self.push_diagnostic(InferenceDiagnostic::NoSuchField {
- field: inner.into(),
- private: None,
- variant: def,
- });
- self.err_ty()
- }
- }
- };
+ debug_assert!(
+ !matches!(pat_adjust_kind, PatAdjust::BuiltinDeref),
+ "unexpected deref pattern for builtin reference type {expected:?}",
+ );
+
+ let pat_adjustments = self.result.pat_adjustments.entry(pat).or_default();
+ // We may reach the recursion limit if a user matches on a type `T` satisfying
+ // `T: Deref<Target = T>`; error gracefully in this case.
+ // FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move
+ // this check out of this branch. Alternatively, this loop could be implemented with
+ // autoderef and this check removed. For now though, don't break code compiling on
+ // stable with lots of `&`s and a low recursion limit, if anyone's done that.
+ if pat_adjustments.len() < self.resolver.top_level_def_map().recursion_limit() as usize {
+ // Preserve the smart pointer type for THIR lowering and closure upvar analysis.
+ pat_adjustments.push(PatAdjustment { kind: pat_adjust_kind, source: expected.store() });
+ } else {
+ // FIXME: Emit an error.
+ inner_ty = self.types.types.error;
+ }
- self.infer_pat(inner, expected_ty, default_bm, decl);
+ // Recurse, using the old pat info to keep `current_depth` to its old value.
+ // Peeling smart pointers does not update the default binding mode.
+ self.infer_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, pat_info)
+ }
+
+ /// How should the binding mode and expected type be adjusted?
+ ///
+ /// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`.
+ fn calc_adjust_mode(
+ &mut self,
+ pat_id: PatId,
+ pat: &Pat,
+ opt_path_res: Option<Result<ResolvedPat<'db>, ()>>,
+ ) -> AdjustMode {
+ match pat {
+ // Type checking these product-like types successfully always require
+ // that the expected type be of those types and not reference types.
+ Pat::Tuple { .. } | Pat::Range { .. } | Pat::Slice { .. } => AdjustMode::peel_all(),
+ // When checking an explicit deref pattern, only peel reference types.
+ // FIXME(deref_patterns): If box patterns and deref patterns need to coexist, box
+ // patterns may want `PeelKind::Implicit`, stopping on encountering a box.
+ Pat::Box { .. } | Pat::Deref { .. } => {
+ AdjustMode::Peel { kind: PeelKind::ExplicitDerefPat }
+ }
+ // A never pattern behaves somewhat like a literal or unit variant.
+ // Pat::Never => AdjustMode::peel_all(),
+ // For patterns with paths, how we peel the scrutinee depends on the path's resolution.
+ Pat::Record { .. }
+ | Pat::TupleStruct { .. }
+ | Pat::Path(_) => {
+ // If there was an error resolving the path, default to peeling everything.
+ opt_path_res.unwrap().map_or(AdjustMode::peel_all(), |pr| pr.adjust_mode())
+ }
+
+ // String and byte-string literals result in types `&str` and `&[u8]` respectively.
+ // All other literals result in non-reference types.
+ // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}` unless
+ // `deref_patterns` is enabled.
+ &Pat::Lit(expr) | &Pat::ConstBlock(expr) => {
+ let lit_ty = self.infer_expr_pat_unadjusted(expr);
+ // Call `resolve_vars_if_possible` here for inline const blocks.
+ let lit_ty = self.infcx().resolve_vars_if_possible(lit_ty);
+ // If `deref_patterns` is enabled, allow `if let "foo" = &&"foo" {}`.
+ if self.features.deref_patterns {
+ let mut peeled_ty = lit_ty;
+ let mut pat_ref_layers = 0;
+ while let TyKind::Ref(_, inner_ty, mutbl) =
+ self.table.try_structurally_resolve_type(pat_id.into(), peeled_ty).kind()
+ {
+ // We rely on references at the head of constants being immutable.
+ debug_assert!(mutbl.is_not());
+ pat_ref_layers += 1;
+ peeled_ty = inner_ty;
+ }
+ AdjustMode::Peel {
+ kind: PeelKind::Implicit { until_adt: None, pat_ref_layers },
+ }
+ } else {
+ if lit_ty.is_ref() { AdjustMode::Pass } else { AdjustMode::peel_all() }
}
}
- None => {
- let err_ty = self.err_ty();
- for (_, inner) in subs {
- self.infer_pat(inner, err_ty, default_bm, decl);
+
+ // Ref patterns are complicated, we handle them in `check_pat_ref`.
+ Pat::Ref { .. }
+ // No need to do anything on a missing pattern.
+ | Pat::Missing
+ // A `_`/`..` pattern works with any expected type, so there's no need to do anything.
+ | Pat::Wild | Pat::Rest
+ // Bindings also work with whatever the expected type is,
+ // and moreover if we peel references off, that will give us the wrong binding type.
+ // Also, we can have a subpattern `binding @ pat`.
+ // Each side of the `@` should be treated independently (like with OR-patterns).
+ | Pat::Bind { .. }
+ // `Pat::Expr(_)` inside assignments becomes a binding in rustc, therefore should be
+ // the same as `Pat::Bind`.
+ | Pat::Expr(_)
+ // An OR-pattern just propagates to each individual alternative.
+ // This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`.
+ // In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`.
+ | Pat::Or(_)
+ // Like or-patterns, guard patterns just propagate to their subpatterns.
+ /* | Pat::Guard(..) */ => AdjustMode::Pass,
+ }
+ }
+
+ /// Assuming `expected` is a reference type, determine whether to peel it before matching.
+ fn should_peel_ref(&self, peel_kind: PeelKind, mut expected: Ty<'db>) -> bool {
+ debug_assert!(expected.is_ref());
+ let pat_ref_layers = match peel_kind {
+ PeelKind::ExplicitDerefPat => 0,
+ PeelKind::Implicit { pat_ref_layers, .. } => pat_ref_layers,
+ };
+
+ // Most patterns don't have reference types, so we'll want to peel all references from the
+ // scrutinee before matching. To optimize for the common case, return early.
+ if pat_ref_layers == 0 {
+ return true;
+ }
+ debug_assert!(
+ self.features.deref_patterns,
+ "Peeling for patterns with reference types is gated by `deref_patterns`."
+ );
+
+ // If the pattern has as many or more layers of reference as the expected type, we can match
+ // without peeling more, unless we find a smart pointer or `&mut` that we also need to peel.
+ // We don't treat `&` and `&mut` as interchangeable, but by peeling `&mut`s before matching,
+ // we can still, e.g., match on a `&mut str` with a string literal pattern. This is because
+ // string literal patterns may be used where `str` is expected.
+ let mut expected_ref_layers = 0;
+ while let TyKind::Ref(_, inner_ty, mutbl) = expected.kind() {
+ if mutbl.is_mut() {
+ // Mutable references can't be in the final value of constants, thus they can't be
+ // at the head of their types, thus we should always peel `&mut`.
+ return true;
+ }
+ expected_ref_layers += 1;
+ expected = inner_ty;
+ }
+ pat_ref_layers < expected_ref_layers || self.should_peel_smart_pointer(peel_kind, expected)
+ }
+
+ /// Determine whether `expected` is a smart pointer type that should be peeled before matching.
+ fn should_peel_smart_pointer(&self, peel_kind: PeelKind, expected: Ty<'db>) -> bool {
+ // Explicit `deref!(_)` patterns match against smart pointers; don't peel in that case.
+ if let PeelKind::Implicit { until_adt, .. } = peel_kind
+ // For simplicity, only apply overloaded derefs if `expected` is a known ADT.
+ // FIXME(deref_patterns): we'll get better diagnostics for users trying to
+ // implicitly deref generics if we allow them here, but primitives, tuples, and
+ // inference vars definitely should be stopped. Figure out what makes most sense.
+ && let TyKind::Adt(scrutinee_adt, _) = expected.kind()
+ // Don't peel if the pattern type already matches the scrutinee. E.g., stop here if
+ // matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
+ && until_adt != Some(scrutinee_adt.def_id())
+ // At this point, the pattern isn't able to match `expected` without peeling. Check
+ // that it implements `Deref` before assuming it's a smart pointer, to get a normal
+ // type error instead of a missing impl error if not. This only checks for `Deref`,
+ // not `DerefPure`: we require that too, but we want a trait error if it's missing.
+ && let Some(deref_trait) = self.lang_items.Deref
+ && self.infcx().type_implements_trait(deref_trait, [expected], self.table.param_env).may_apply()
+ {
+ true
+ } else {
+ false
+ }
+ }
+
+ fn infer_expr_pat_unadjusted(&mut self, expr: ExprId) -> Ty<'db> {
+ self.infer_expr_no_expect(expr, ExprIsRead::Yes)
+ }
+
+ fn infer_lit_pat(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> {
+ let literal = match &self.store[expr] {
+ Expr::Literal(literal) => literal,
+ _ => panic!("expected a literal"),
+ };
+
+ // We've already computed the type above (when checking for a non-ref pat),
+ // so avoid computing it again.
+ let ty = self.expr_ty(expr);
+
+ // Byte string patterns behave the same way as array patterns
+ // They can denote both statically and dynamically-sized byte arrays.
+ // Additionally, when `deref_patterns` is enabled, byte string literal patterns may have
+ // types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec<u8>`.
+ let mut pat_ty = ty;
+ if matches!(literal, Literal::ByteString(_)) {
+ let expected = self.structurally_resolve_type(expr.into(), expected);
+ match expected.kind() {
+ // Allow `b"...": &[u8]`
+ TyKind::Ref(_, inner_ty, _)
+ if self
+ .table
+ .try_structurally_resolve_type(expr.into(), inner_ty)
+ .is_slice() =>
+ {
+ trace!(?expr, "polymorphic byte string lit");
+ pat_ty = self.types.types.static_u8_slice;
}
+ // Allow `b"...": [u8; 3]` for `deref_patterns`
+ TyKind::Array(..) if self.features.deref_patterns => {
+ pat_ty = match ty.kind() {
+ TyKind::Ref(_, inner_ty, _) => inner_ty,
+ _ => panic!("found byte string literal with non-ref type {ty:?}"),
+ }
+ }
+ // Allow `b"...": [u8]` for `deref_patterns`
+ TyKind::Slice(..) if self.features.deref_patterns => {
+ pat_ty = self.types.types.u8_slice;
+ }
+ // Otherwise, `b"...": &[u8; 3]`
+ _ => {}
}
}
- ty
+ // When `deref_patterns` is enabled, in order to allow `deref!("..."): String`, we allow
+ // string literal patterns to have type `str`. This is accounted for when lowering to MIR.
+ if self.features.deref_patterns
+ && matches!(literal, Literal::String(_))
+ && self.table.try_structurally_resolve_type(expr.into(), expected).is_str()
+ {
+ pat_ty = self.types.types.str;
+ }
+
+ // Somewhat surprising: in this case, the subtyping relation goes the
+ // opposite way as the other cases. Actually what we really want is not
+ // a subtyping relation at all but rather that there exists a LUB
+ // (so that they can be compared). However, in practice, constants are
+ // always scalars or strings. For scalars subtyping is irrelevant,
+ // and for strings `ty` is type is `&'static str`, so if we say that
+ //
+ // &'static str <: expected
+ //
+ // then that's equivalent to there existing a LUB.
+ _ = self.demand_suptype(expr.into(), expected, pat_ty);
+
+ pat_ty
}
- /// Infers type for tuple pattern or its corresponding assignee expression.
- ///
- /// Ellipses found in the original pattern or expression must be filtered out.
- pub(super) fn infer_tuple_pat_like(
+ fn infer_range_pat(
&mut self,
pat: PatId,
+ lhs_expr: Option<ExprId>,
+ rhs_expr: Option<ExprId>,
expected: Ty<'db>,
- default_bm: BindingMode,
- ellipsis: Option<u32>,
- elements: &[PatId],
- decl: Option<DeclContext>,
) -> Ty<'db> {
- let mut expected_len = elements.len();
- if ellipsis.is_some() {
- // Require known type only when `..` is present.
- if let TyKind::Tuple(tys) = self.table.structurally_resolve_type(expected).kind() {
- expected_len = tys.len();
+ let mut calc_side = |opt_expr: Option<ExprId>| match opt_expr {
+ None => None,
+ Some(expr) => {
+ let ty = self.infer_expr_pat_unadjusted(expr);
+ // Check that the end-point is possibly of numeric or char type.
+ // The early check here is not for correctness, but rather better
+ // diagnostics (e.g. when `&str` is being matched, `expected` will
+ // be peeled to `str` while ty here is still `&str`, if we don't
+ // err early here, a rather confusing unification error will be
+ // emitted instead).
+ let ty = self.table.try_structurally_resolve_type(expr.into(), ty);
+ let fail =
+ !(ty.is_numeric() || ty.is_char() || ty.is_ty_var() || ty.references_error());
+ Some((fail, ty, expr))
}
+ };
+ let mut lhs = calc_side(lhs_expr);
+ let mut rhs = calc_side(rhs_expr);
+
+ if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) {
+ // There exists a side that didn't meet our criteria that the end-point
+ // be of a numeric or char type, as checked in `calc_side` above.
+ // FIXME: Emit an error.
+ return self.types.types.error;
}
- let max_len = cmp::max(expected_len, elements.len());
- let element_tys_iter = (0..max_len).map(|_| self.table.next_ty_var());
- let element_tys = Tys::new_from_iter(self.interner(), element_tys_iter);
- let pat_ty = Ty::new(self.interner(), TyKind::Tuple(element_tys));
- if self.demand_eqtype(pat.into(), expected, pat_ty).is_err()
- && let TyKind::Tuple(expected) = expected.kind()
- {
- // Equate expected type with the infer vars, for better diagnostics.
- for (expected, elem_ty) in iter::zip(expected, element_tys) {
- _ = self
- .table
- .at(&ObligationCause::dummy())
- .eq(expected, elem_ty)
- .map(|infer_ok| self.table.register_infer_ok(infer_ok));
- }
- }
- let (before_ellipsis, after_ellipsis) = match ellipsis {
- Some(ellipsis) => {
- let element_tys = element_tys.as_slice();
- // Don't check patterns twice.
- let from_end_start = cmp::max(
- element_tys.len().saturating_sub(elements.len() - ellipsis as usize),
- ellipsis as usize,
- );
- (
- element_tys.get(..ellipsis as usize).unwrap_or(element_tys),
- element_tys.get(from_end_start..).unwrap_or_default(),
- )
+ // Unify each side with `expected`.
+ // Subtyping doesn't matter here, as the value is some kind of scalar.
+ let mut demand_eqtype = |x: &mut _| {
+ if let Some((_, x_ty, x_expr)) = *x {
+ _ = self.demand_eqtype(ExprOrPatId::from(x_expr), expected, x_ty);
+ }
+ };
+ demand_eqtype(&mut lhs);
+ demand_eqtype(&mut rhs);
+
+ if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) {
+ return self.types.types.error;
+ }
+
+ // Find the unified type and check if it's of numeric or char type again.
+ // This check is needed if both sides are inference variables.
+ // We require types to be resolved here so that we emit inference failure
+ // rather than "_ is not a char or numeric".
+ let ty = self.structurally_resolve_type(
+ lhs_expr.or(rhs_expr).map(ExprOrPatId::ExprId).unwrap_or(pat.into()),
+ expected,
+ );
+ if !(ty.is_numeric() || ty.is_char() || ty.references_error()) {
+ // FIXME: Emit an error.
+ return self.types.types.error;
+ }
+ ty
+ }
+
+ fn infer_bind_pat(
+ &mut self,
+ pat: PatId,
+ var_id: BindingId,
+ sub: Option<PatId>,
+ expected: Ty<'db>,
+ pat_info: PatInfo,
+ ) -> Ty<'db> {
+ let PatInfo { binding_mode: def_br, .. } = pat_info;
+ let binding_data = &self.store[var_id];
+
+ // Determine the binding mode...
+ let user_bind_annot = BindingMode::from_annotation(binding_data.mode);
+ let bm = match user_bind_annot {
+ BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_) = def_br => {
+ // Only mention the experimental `mut_ref` feature if if we're in edition 2024 and
+ // using other experimental matching features compatible with it.
+ if self.edition.at_least_2024()
+ && (self.features.ref_pat_eat_one_layer_2024
+ || self.features.ref_pat_eat_one_layer_2024_structural)
+ {
+ if !self.features.mut_ref {
+ // FIXME: Emit an error: binding cannot be both mutable and by-reference.
+ }
+
+ BindingMode(def_br, Mutability::Mut)
+ } else {
+ // `mut` resets the binding mode on edition <= 2021
+ BindingMode(ByRef::No, Mutability::Mut)
+ }
}
- None => (element_tys.as_slice(), &[][..]),
+ BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
+ BindingMode(ByRef::Yes(_), _) => user_bind_annot,
};
- for (&elem, &elem_ty) in iter::zip(elements, before_ellipsis.iter().chain(after_ellipsis)) {
- self.infer_pat(elem, elem_ty, default_bm, decl);
+
+ if matches!(bm.0, ByRef::Yes(Mutability::Mut))
+ && let MutblCap::WeaklyNot = pat_info.max_ref_mutbl
+ {
+ // FIXME: Emit an error: cannot borrow as mutable inside an `&` pattern.
}
- if let Some(uncovered) = elements.get(element_tys.len()..) {
- for &elem in uncovered {
- self.infer_pat(elem, self.types.types.error, default_bm, decl);
+
+ // ...and store it in a side table:
+ self.result.binding_modes.insert(pat, bm);
+
+ debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat, bm);
+
+ let local_ty = match bm.0 {
+ ByRef::Yes(mutbl) => {
+ // If the binding is like `ref x | ref mut x`,
+ // then `x` is assigned a value of type `&M T` where M is the
+ // mutability and T is the expected type.
+ //
+ // Under pin ergonomics, if the binding is like `ref pin const|mut x`,
+ // then `x` is assigned a value of type `&pin M T` where M is the
+ // mutability and T is the expected type.
+ //
+ // `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)`
+ // is required. However, we use equality, which is stronger.
+ // See (note_1) for an explanation.
+ self.new_ref_ty(pat.into(), mutbl, expected)
}
+ // Otherwise, the type of x is the expected type `T`.
+ ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1).
+ };
+
+ // We have a concrete type for the local, so we do not need to taint it and hide follow up errors *using* the local.
+ if let Some(existing_local_ty) = self.result.type_of_binding.get(var_id) {
+ // If there are multiple arms, make sure they all agree on
+ // what the type of the binding `x` ought to be.
+ _ = self.demand_eqtype(pat.into(), existing_local_ty.as_ref(), local_ty);
+ } else {
+ self.write_binding_ty(var_id, local_ty);
+ }
+
+ if let Some(p) = sub {
+ self.infer_pat(p, expected, pat_info);
+ }
+
+ local_ty
+ }
+
+ fn check_dereferenceable(&self, expected: Ty<'db>, inner: PatId) -> Result<(), ()> {
+ if let Pat::Bind { .. } = self.store[inner]
+ && let Some(pointee_ty) = self.shallow_resolve(expected).builtin_deref(true)
+ && let TyKind::Dynamic(..) = pointee_ty.kind()
+ {
+ // This is "x = dyn SomeTrait" being reduced from
+ // "let &x = &dyn SomeTrait" or "let box x = Box<dyn SomeTrait>", an error.
+ // FIXME: Emit an error. rustc emits this message:
+ const _CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\
+This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \
+pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \
+this type has no compile-time size. Therefore, all accesses to trait types must be through \
+pointers. If you encounter this error you should try to avoid dereferencing the pointer.
+
+You can read more about trait objects in the Trait Objects section of the Reference: \
+https://doc.rust-lang.org/reference/types.html#trait-objects";
}
+ Ok(())
+ }
+
+ fn resolve_record_pat(&mut self, pat: PatId, path: &Path) -> Result<ResolvedPat<'db>, ()> {
+ // Resolve the path and check the definition for errors.
+ let (pat_ty, Some(variant)) = self.resolve_variant(pat.into(), path, false) else {
+ return Err(());
+ };
+ self.write_variant_resolution(pat.into(), variant);
+ Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Struct { variant } })
+ }
+
+ fn infer_record_pat(
+ &mut self,
+ pat: PatId,
+ fields: &[RecordFieldPat],
+ has_rest_pat: bool,
+ pat_ty: Ty<'db>,
+ variant: VariantId,
+ expected: Ty<'db>,
+ pat_info: PatInfo,
+ ) -> Ty<'db> {
+ // Type-check the path.
+ let _ = self.demand_eqtype(pat.into(), expected, pat_ty);
+
+ // Type-check subpatterns.
+ self.check_record_pat_fields(pat_ty, pat, variant, fields, has_rest_pat, pat_info);
pat_ty
}
- /// The resolver needs to be updated to the surrounding expression when inside assignment
- /// (because there, `Pat::Path` can refer to a variable).
- pub(super) fn infer_top_pat(
+ fn resolve_pat_path(&mut self, pat: PatId, path: &Path) -> Result<ResolvedPat<'db>, ()> {
+ let (res, pat_ty) = self.infer_path(path, pat.into()).ok_or(())?;
+ match res {
+ ValueNs::FunctionId(_)
+ | ValueNs::GenericParam(_)
+ | ValueNs::ImplSelf(_)
+ | ValueNs::LocalBinding(_)
+ | ValueNs::StaticId(_) => {
+ // FIXME: Emit an error.
+ return Err(());
+ }
+ ValueNs::ConstId(_) | ValueNs::EnumVariantId(_) | ValueNs::StructId(_) => {} // OK
+ }
+
+ Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Path { res } })
+ }
+
+ fn infer_pat_path(
&mut self,
pat: PatId,
+ resolved: &ResolvedPat<'db>,
expected: Ty<'db>,
- decl: Option<DeclContext>,
- ) {
- self.infer_pat(pat, expected, BindingMode::default(), decl);
+ ) -> Ty<'db> {
+ _ = self.demand_suptype(pat.into(), expected, resolved.ty);
+ resolved.ty
}
- fn infer_pat(
+ fn resolve_tuple_struct_pat(
&mut self,
pat: PatId,
+ path: &Path,
+ ) -> Result<ResolvedPat<'db>, ()> {
+ // Resolve the path and check the definition for errors.
+ let (pat_ty, Some(variant)) = self.resolve_variant(pat.into(), path, true) else {
+ return Err(());
+ };
+ self.write_variant_resolution(pat.into(), variant);
+ Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::TupleStruct { variant } })
+ }
+
+ fn infer_tuple_struct_pat(
+ &mut self,
+ pat: PatId,
+ subpats: &[PatId],
+ ddpos: Option<u32>,
+ pat_ty: Ty<'db>,
+ variant: VariantId,
expected: Ty<'db>,
- mut default_bm: BindingMode,
- decl: Option<DeclContext>,
+ pat_info: PatInfo,
) -> Ty<'db> {
- let mut expected = self.table.structurally_resolve_type(expected);
-
- if matches!(&self.store[pat], Pat::Ref { .. }) || self.inside_assignment {
- cov_mark::hit!(match_ergonomics_ref);
- // When you encounter a `&pat` pattern, reset to Move.
- // This is so that `w` is by value: `let (_, &w) = &(1, &2);`
- // Destructuring assignments also reset the binding mode and
- // don't do match ergonomics.
- default_bm = BindingMode::Move;
- } else if self.is_non_ref_pat(self.store, pat) {
- let mut pat_adjustments = Vec::new();
- while let TyKind::Ref(_lifetime, inner, mutability) = expected.kind() {
- pat_adjustments.push(expected.store());
- expected = self.table.try_structurally_resolve_type(inner);
- default_bm = match default_bm {
- BindingMode::Move => BindingMode::Ref(mutability),
- BindingMode::Ref(Mutability::Not) => BindingMode::Ref(Mutability::Not),
- BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability),
+ let interner = self.interner();
+
+ // Type-check the tuple struct pattern against the expected type.
+ let had_err = self.demand_eqtype(pat.into(), expected, pat_ty);
+
+ let variant_fields = variant.fields(self.db);
+ let variant_field_tys = self.db.field_types(variant);
+ let TyKind::Adt(_, args) = pat_ty.kind() else {
+ panic!("unexpected pattern type {:?}", pat_ty);
+ };
+ // Type-check subpatterns.
+ if subpats.len() == variant_fields.len()
+ || subpats.len() < variant_fields.len() && ddpos.is_some()
+ {
+ for (i, &subpat) in subpats.iter().enumerate_and_adjust(variant_fields.len(), ddpos) {
+ let field_id = LocalFieldId::from_raw(la_arena::RawIdx::from_u32(i as u32));
+ let field_ty =
+ variant_field_tys[field_id].get().instantiate(interner, args).skip_norm_wip();
+ self.infer_pat(subpat, field_ty, pat_info);
+ }
+ if let Err(()) = had_err {
+ for &pat in subpats {
+ self.infer_pat(pat, self.types.types.error, pat_info);
}
+ return self.types.types.error;
}
+ } else {
+ self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount {
+ pat,
+ expected: variant_fields.len(),
+ found: subpats.len(),
+ });
- if !pat_adjustments.is_empty() {
- pat_adjustments.shrink_to_fit();
- self.result.pat_adjustments.insert(pat, pat_adjustments);
+ for (i, &pat) in subpats.iter().enumerate() {
+ let field_id = LocalFieldId::from_raw(la_arena::RawIdx::from_u32(i as u32));
+ let expected = match variant_field_tys.get(field_id) {
+ Some(field_ty) => field_ty.get().instantiate(interner, args).skip_norm_wip(),
+ None => self.types.types.error,
+ };
+ self.infer_pat(pat, expected, pat_info);
}
}
+ pat_ty
+ }
- // Lose mutability.
- let default_bm = default_bm;
- let expected = expected;
-
- let ty = match &self.store[pat] {
- Pat::Tuple { args, ellipsis } => {
- self.infer_tuple_pat_like(pat, expected, default_bm, *ellipsis, args, decl)
+ fn infer_tuple_pat(
+ &mut self,
+ pat: PatId,
+ elements: &[PatId],
+ ddpos: Option<u32>,
+ expected: Ty<'db>,
+ pat_info: PatInfo,
+ ) -> Ty<'db> {
+ let interner = self.interner();
+ let mut expected_len = elements.len();
+ if ddpos.is_some() {
+ // Require known type only when `..` is present.
+ if let TyKind::Tuple(tys) = self.structurally_resolve_type(pat.into(), expected).kind()
+ {
+ expected_len = tys.len();
}
- Pat::Or(pats) => {
- for pat in pats.iter() {
- self.infer_pat(*pat, expected, default_bm, decl);
+ }
+ let max_len = cmp::max(expected_len, elements.len());
+
+ let element_tys_iter = (0..max_len).map(|i| {
+ self.table.next_ty_var(elements.get(i).copied().map(Span::PatId).unwrap_or(Span::Dummy))
+ });
+ let element_tys = Tys::new_from_iter(interner, element_tys_iter);
+ let pat_ty = Ty::new(interner, TyKind::Tuple(element_tys));
+ if self.demand_eqtype(pat.into(), expected, pat_ty).is_err() {
+ let expected = if let TyKind::Tuple(tys) =
+ self.table.try_structurally_resolve_type(Span::Dummy, expected).kind()
+ {
+ for (expected_var, found) in iter::zip(element_tys, tys) {
+ // Constrain the infer var so that the type mismatch error message, which contains it,
+ // will be better.
+ _ = self.demand_eqtype(pat.into(), expected_var, found);
}
- expected
- }
- &Pat::Ref { pat, mutability } => {
- self.infer_ref_pat(pat, lower_mutability(mutability), expected, default_bm, decl)
+ tys
+ } else {
+ self.types.empty.tys
+ };
+ let expected = expected.iter().chain(iter::repeat(self.types.types.error));
+ Ty::new_tup_from_iter(
+ interner,
+ iter::zip(expected, elements).map(|(expected, &elem)| {
+ self.infer_pat(elem, expected, pat_info);
+ self.result.type_of_pat_with_adjust(elem)
+ }),
+ )
+ } else {
+ for (i, &elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
+ self.infer_pat(elem, element_tys[i], pat_info);
}
- Pat::TupleStruct { path: p, args: subpats, ellipsis } => self
- .infer_tuple_struct_pat_like(
- p.as_deref(),
- expected,
- default_bm,
- pat,
- *ellipsis,
- subpats,
- decl,
- ),
- Pat::Record { path: p, args: fields, ellipsis: _ } => {
- let subs = fields.iter().map(|f| (f.name.clone(), f.pat));
- self.infer_record_pat_like(p.as_deref(), expected, default_bm, pat, subs, decl)
- }
- Pat::Path(path) => {
- let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty());
- let ty_inserted_vars = self.insert_type_vars_shallow(ty);
- match self.coerce(
- pat.into(),
- expected,
- ty_inserted_vars,
- AllowTwoPhase::No,
- ExprIsRead::No,
- ) {
- Ok(coerced_ty) => {
- self.write_pat_ty(pat, coerced_ty);
- return self.pat_ty_after_adjustment(pat);
- }
- Err(_) => {
- self.result.type_mismatches.get_or_insert_default().insert(
- pat.into(),
- TypeMismatch {
- expected: expected.store(),
- actual: ty_inserted_vars.store(),
- },
- );
- self.write_pat_ty(pat, ty);
- // We return `expected` to prevent cascading errors. I guess an alternative is to
- // not emit type mismatches for error types and emit an error type here.
- return expected;
- }
+ pat_ty
+ }
+ }
+
+ fn check_record_pat_fields(
+ &mut self,
+ adt_ty: Ty<'db>,
+ _pat: PatId,
+ variant: VariantId,
+ fields: &[RecordFieldPat],
+ has_rest_pat: bool,
+ pat_info: PatInfo,
+ ) {
+ let interner = self.interner();
+
+ let TyKind::Adt(_, args) = adt_ty.kind() else {
+ panic!("struct pattern is not an ADT");
+ };
+
+ // Index the struct fields' types.
+ let variant_fields = variant.fields(self.db);
+ let field_map = variant_fields
+ .fields()
+ .iter()
+ .map(|(i, field)| (field.name.clone(), i))
+ .collect::<FxHashMap<_, _>>();
+ let variant_field_tys = self.db.field_types(variant);
+ let variant_fields_vis = VariantFields::field_visibilities(self.db, variant);
+
+ // Keep track of which fields have already appeared in the pattern.
+ let mut used_fields = FxHashMap::default();
+
+ let mut inexistent_fields = vec![];
+ // Typecheck each field.
+ for (field_idx, field) in fields.iter().enumerate() {
+ match used_fields.entry(field.name.clone()) {
+ Occupied(_occupied) => {
+ self.push_diagnostic(InferenceDiagnostic::DuplicateField {
+ field: field.pat.into(),
+ variant,
+ });
}
- }
- Pat::Bind { id, subpat } => {
- return self.infer_bind_pat(pat, *id, default_bm, *subpat, expected, decl);
- }
- Pat::Slice { prefix, slice, suffix } => {
- self.infer_slice_pat(expected, prefix, *slice, suffix, default_bm, decl)
- }
- Pat::Wild => expected,
- Pat::Range { start, end, range_type: _ } => {
- if let Some(start) = *start {
- let start_ty = self.infer_expr(start, &Expectation::None, ExprIsRead::Yes);
- _ = self.demand_eqtype(start.into(), expected, start_ty);
+ Vacant(vacant) => {
+ vacant.insert(field_idx);
}
- if let Some(end) = *end {
- let end_ty = self.infer_expr(end, &Expectation::None, ExprIsRead::Yes);
- _ = self.demand_eqtype(end.into(), expected, end_ty);
+ };
+ let field_idx = field_map.get(&field.name).copied();
+ let field_ty = match field_idx {
+ Some(field_idx) => {
+ if !self.resolver.is_visible(self.db, variant_fields_vis[field_idx]) {
+ self.push_diagnostic(InferenceDiagnostic::NoSuchField {
+ field: field.pat.into(),
+ private: Some(field_idx),
+ variant,
+ });
+ }
+
+ variant_field_tys[field_idx].get().instantiate(interner, args).skip_norm_wip()
}
- expected
- }
- &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);
- return self.pat_ty_after_adjustment(pat);
- }
- Pat::Box { inner } => match self.resolve_boxed_box() {
- Some(box_adt) => {
- let (inner_ty, alloc_ty) = match expected.as_adt() {
- Some((adt, subst)) if adt == box_adt => {
- (subst.type_at(0), subst.as_slice().get(1).and_then(|a| a.as_type()))
- }
- _ => (self.types.types.error, None),
- };
-
- let inner_ty = self.infer_pat(*inner, inner_ty, default_bm, decl);
- Ty::new_adt(
- self.interner(),
- box_adt,
- GenericArgs::fill_with_defaults(
- self.interner(),
- box_adt.into(),
- iter::once(inner_ty.into()).chain(alloc_ty.map(Into::into)),
- |_, id, _| self.table.next_var_for_param(id),
- ),
- )
+ None => {
+ inexistent_fields.push(field);
+ self.types.types.error
}
- None => self.err_ty(),
- },
- Pat::ConstBlock(expr) => {
- let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false);
- let result =
- self.infer_expr(*expr, &Expectation::has_type(expected), ExprIsRead::Yes);
- self.inside_assignment = old_inside_assign;
- result
- }
- Pat::Expr(expr) => {
- let old_inside_assign = std::mem::replace(&mut self.inside_assignment, false);
- // LHS of assignment doesn't constitute reads.
- let expr_is_read = ExprIsRead::No;
- let result =
- self.infer_expr_coerce(*expr, &Expectation::has_type(expected), expr_is_read);
- // We are returning early to avoid the unifiability check below.
- let lhs_ty = self.insert_type_vars_shallow(result);
- let ty = match self.coerce(
- (*expr).into(),
- expected,
- lhs_ty,
- AllowTwoPhase::No,
- expr_is_read,
- ) {
- Ok(ty) => ty,
- Err(_) => {
- self.result.type_mismatches.get_or_insert_default().insert(
- pat.into(),
- TypeMismatch { expected: expected.store(), actual: lhs_ty.store() },
- );
- // `rhs_ty` is returned so no further type mismatches are
- // reported because of this mismatch.
- expected
- }
- };
- self.write_pat_ty(pat, ty);
- self.inside_assignment = old_inside_assign;
- return ty;
- }
- Pat::Missing => self.err_ty(),
- };
- // use a new type variable if we got error type here
- let ty = self.insert_type_vars_shallow(ty);
- // FIXME: This never check is odd, but required with out we do inference right now
- if !expected.is_never() && !self.unify(ty, expected) {
- self.result.type_mismatches.get_or_insert_default().insert(
- pat.into(),
- TypeMismatch { expected: expected.store(), actual: ty.store() },
- );
+ };
+
+ self.infer_pat(field.pat, field_ty, pat_info);
+ }
+
+ let unmentioned_fields = variant_fields
+ .fields()
+ .iter()
+ .filter(|(_, field)| !used_fields.contains_key(&field.name))
+ .collect::<Vec<_>>();
+
+ for inexistent_field in inexistent_fields {
+ self.push_diagnostic(InferenceDiagnostic::NoSuchField {
+ field: inexistent_field.pat.into(),
+ private: None,
+ variant,
+ });
}
- self.write_pat_ty(pat, ty);
- self.pat_ty_after_adjustment(pat)
- }
- fn pat_ty_after_adjustment(&self, pat: PatId) -> Ty<'db> {
- self.result
- .pat_adjustments
- .get(&pat)
- .and_then(|it| it.last())
- .unwrap_or_else(|| &self.result.type_of_pat[pat])
- .as_ref()
+ // Require `..` if struct has non_exhaustive attribute.
+ let non_exhaustive = self.has_applicable_non_exhaustive(variant.into());
+ if non_exhaustive && !has_rest_pat {
+ // FIXME: Emit an error.
+ }
+
+ // Report an error if an incorrect number of fields was specified.
+ if matches!(variant, VariantId::UnionId(_)) {
+ if fields.len() != 1 {
+ // FIXME: Emit an error, unions can't have more than one field.
+ }
+ if has_rest_pat {
+ // FIXME: Emit an error, unions can't have a rest pat.
+ }
+ } else if !unmentioned_fields.is_empty() && !has_rest_pat {
+ // FIXME: Emit an error.
+ }
}
- fn infer_ref_pat(
+ fn infer_box_pat(
&mut self,
- inner_pat: PatId,
- mutability: Mutability,
+ pat: PatId,
+ inner: PatId,
expected: Ty<'db>,
- default_bm: BindingMode,
- decl: Option<DeclContext>,
+ pat_info: PatInfo,
) -> Ty<'db> {
- let (expectation_type, expectation_lt) = match expected.kind() {
- TyKind::Ref(lifetime, inner_ty, _exp_mut) => (inner_ty, lifetime),
- _ => {
- let inner_ty = self.table.next_ty_var();
- let inner_lt = self.table.next_region_var();
- let ref_ty = Ty::new_ref(self.interner(), inner_lt, inner_ty, mutability);
- // Unification failure will be reported by the caller.
- self.unify(ref_ty, expected);
- (inner_ty, inner_lt)
- }
- };
- let subty = self.infer_pat(inner_pat, expectation_type, default_bm, decl);
- Ty::new_ref(self.interner(), expectation_lt, subty, mutability)
+ let interner = self.interner();
+ let (box_ty, inner_ty) = self
+ .check_dereferenceable(expected, inner)
+ .map(|()| {
+ // Here, `demand::subtype` is good enough, but I don't
+ // think any errors can be introduced by using `demand::eqtype`.
+ let inner_ty = self.table.next_ty_var(inner.into());
+ let box_ty = Ty::new_box(interner, inner_ty);
+ _ = self.demand_eqtype(pat.into(), expected, box_ty);
+ (box_ty, inner_ty)
+ })
+ .unwrap_or_else(|()| {
+ let err = self.types.types.error;
+ (err, err)
+ });
+ self.infer_pat(inner, inner_ty, pat_info);
+ box_ty
}
- fn infer_bind_pat(
+ fn infer_deref_pat(
&mut self,
pat: PatId,
- binding: BindingId,
- default_bm: BindingMode,
- subpat: Option<PatId>,
+ inner: PatId,
expected: Ty<'db>,
- decl: Option<DeclContext>,
+ pat_info: PatInfo,
) -> Ty<'db> {
- let Binding { mode, .. } = self.store[binding];
- let mode = if mode == BindingAnnotation::Unannotated {
- default_bm
- } else {
- BindingMode::convert(mode)
- };
- self.result.binding_modes.insert(pat, mode);
+ let target_ty = self.deref_pat_target(pat, expected);
+ self.infer_pat(inner, target_ty, pat_info);
+ let infer_ok = self.register_deref_mut_bounds_if_needed(pat, inner, [expected]);
+ self.table.register_infer_ok(infer_ok);
+ expected
+ }
- let inner_ty = match subpat {
- Some(subpat) => self.infer_pat(subpat, expected, default_bm, decl),
- None => expected,
+ fn deref_pat_target(&mut self, pat: PatId, source_ty: Ty<'db>) -> Ty<'db> {
+ let (Some(deref_pure), Some(deref_target)) =
+ (self.lang_items.DerefPure, self.lang_items.DerefTarget)
+ else {
+ return self.types.types.error;
};
- let inner_ty = self.insert_type_vars_shallow(inner_ty);
+ // Register a `DerefPure` bound, which is required by all `deref!()` pats.
+ let interner = self.interner();
+ self.table.register_bound(source_ty, deref_pure, ObligationCause::new(pat));
+ // The expected type for the deref pat's inner pattern is `<expected as Deref>::Target`.
+ let target_ty = Ty::new_projection(interner, deref_target.into(), [source_ty]);
+ self.table.try_structurally_resolve_type(pat.into(), target_ty)
+ }
- let bound_ty = match mode {
- BindingMode::Ref(mutability) => {
- let inner_lt = self.table.next_region_var();
- Ty::new_ref(self.interner(), inner_lt, expected, mutability)
+ /// Check if the interior of a deref pattern (either explicit or implicit) has any `ref mut`
+ /// bindings, which would require `DerefMut` to be emitted in MIR building instead of just
+ /// `Deref`. We do this *after* checking the inner pattern, since we want to make sure to
+ /// account for `ref mut` binding modes inherited from implicitly dereferencing `&mut` refs.
+ fn register_deref_mut_bounds_if_needed(
+ &self,
+ pat: PatId,
+ inner: PatId,
+ derefed_tys: impl IntoIterator<Item = Ty<'db>>,
+ ) -> InferOk<'db, ()> {
+ let mut infer_ok = InferOk { value: (), obligations: Vec::new() };
+ if self.pat_has_ref_mut_binding(inner) {
+ let Some(deref_mut) = self.lang_items.DerefMut else { return infer_ok };
+ let interner = self.interner();
+ for mutably_derefed_ty in derefed_tys {
+ infer_ok.obligations.push(Obligation::new(
+ interner,
+ ObligationCause::new(pat),
+ self.table.param_env,
+ TraitRef::new(interner, deref_mut.into(), [mutably_derefed_ty]),
+ ));
}
- BindingMode::Move => expected,
- };
- self.write_pat_ty(pat, inner_ty);
- self.write_binding_ty(binding, bound_ty);
- inner_ty
+ }
+ infer_ok
}
- fn infer_slice_pat(
+ /// Does the pattern recursively contain a `ref mut` binding in it?
+ ///
+ /// This is used to determined whether a `deref` pattern should emit a `Deref`
+ /// or `DerefMut` call for its pattern scrutinee.
+ ///
+ /// This is computed from the typeck results since we want to make
+ /// sure to apply any match-ergonomics adjustments, which we cannot
+ /// determine from the HIR alone.
+ pub(super) fn pat_has_ref_mut_binding(&self, pat: PatId) -> bool {
+ let mut has_ref_mut = false;
+ self.store.walk_pats(pat, &mut |pat| {
+ if let Some(BindingMode(ByRef::Yes(Mutability::Mut), _)) =
+ self.result.binding_modes.get(pat)
+ {
+ has_ref_mut = true;
+ }
+ });
+ has_ref_mut
+ }
+
+ // Precondition: Pat is Ref(inner)
+ fn infer_ref_pat(
&mut self,
- expected: Ty<'db>,
- prefix: &[PatId],
- slice: Option<PatId>,
- suffix: &[PatId],
- default_bm: BindingMode,
- decl: Option<DeclContext>,
+ pat: PatId,
+ inner: PatId,
+ pat_mutbl: Mutability,
+ mut expected: Ty<'db>,
+ mut pat_info: PatInfo,
) -> Ty<'db> {
- let expected = self.table.structurally_resolve_type(expected);
-
- // If `expected` is an infer ty, we try to equate it to an array if the given pattern
- // allows it. See issue #16609
- if self.pat_is_irrefutable(decl)
- && expected.is_ty_var()
- && let Some(resolved_array_ty) =
- self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice)
- {
- self.unify(expected, resolved_array_ty);
+ let ref_pat_matches_mut_ref = self.ref_pat_matches_mut_ref();
+ if ref_pat_matches_mut_ref && pat_mutbl == Mutability::Not {
+ // If `&` patterns can match against mutable reference types (RFC 3627, Rule 5), we need
+ // to prevent subpatterns from binding with `ref mut`. Subpatterns of a shared reference
+ // pattern should have read-only access to the scrutinee, and the borrow checker won't
+ // catch it in this case.
+ pat_info.max_ref_mutbl = pat_info.max_ref_mutbl.cap_to_weakly_not();
}
- let expected = self.table.try_structurally_resolve_type(expected);
- let elem_ty = match expected.kind() {
- TyKind::Array(st, _) | TyKind::Slice(st) => st,
- _ => self.err_ty(),
- };
+ expected = self.table.try_structurally_resolve_type(pat.into(), expected);
+ // Determine whether we're consuming an inherited reference and resetting the default
+ // binding mode, based on edition and enabled experimental features.
+ if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
+ match self.ref_pat_matches_inherited_ref(self.edition) {
+ InheritedRefMatchRule::EatOuter => {
+ // ref pattern attempts to consume inherited reference
+ if pat_mutbl > inh_mut {
+ // Tried to match inherited `ref` with `&mut`
+ // NB: This assumes that `&` patterns can match against mutable references
+ // (RFC 3627, Rule 5). If we implement a pattern typing ruleset with Rule 4E
+ // but not Rule 5, we'll need to check that here.
+ debug_assert!(ref_pat_matches_mut_ref);
+ // FIXME: Emit an error.
+ }
- for &pat_id in prefix.iter().chain(suffix.iter()) {
- self.infer_pat(pat_id, elem_ty, default_bm, decl);
- }
-
- if let Some(slice_pat_id) = slice {
- let rest_pat_ty = match expected.kind() {
- TyKind::Array(_, length) => {
- let len = try_const_usize(self.db, length);
- let len =
- len.and_then(|len| len.checked_sub((prefix.len() + suffix.len()) as u128));
- Ty::new_array_with_const_len(
- self.interner(),
- elem_ty,
- usize_const(self.db, len, self.resolver.krate()),
- )
+ pat_info.binding_mode = ByRef::No;
+ self.result.skipped_ref_pats.insert(pat);
+ self.infer_pat(inner, expected, pat_info);
+ return expected;
}
- _ => Ty::new_slice(self.interner(), elem_ty),
- };
- self.infer_pat(slice_pat_id, rest_pat_ty, default_bm, decl);
- }
+ InheritedRefMatchRule::EatInner => {
+ if let TyKind::Ref(_, _, r_mutbl) = expected.kind()
+ && pat_mutbl <= r_mutbl
+ {
+ // Match against the reference type; don't consume the inherited ref.
+ // NB: The check for compatible pattern and ref type mutability assumes that
+ // `&` patterns can match against mutable references (RFC 3627, Rule 5). If
+ // we implement a pattern typing ruleset with Rule 4 (including the fallback
+ // to matching the inherited ref when the inner ref can't match) but not
+ // Rule 5, we'll need to check that here.
+ debug_assert!(ref_pat_matches_mut_ref);
+ // NB: For RFC 3627's Rule 3, we limit the default binding mode's ref
+ // mutability to `pat_info.max_ref_mutbl`. If we implement a pattern typing
+ // ruleset with Rule 4 but not Rule 3, we'll need to check that here.
+ debug_assert!(self.downgrade_mut_inside_shared());
+ let mutbl_cap = cmp::min(r_mutbl, pat_info.max_ref_mutbl.as_mutbl());
+ pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(mutbl_cap);
+ } else {
+ // The reference pattern can't match against the expected type, so try
+ // matching against the inherited ref instead.
+ if pat_mutbl > inh_mut {
+ // We can't match an inherited shared reference with `&mut`.
+ // NB: This assumes that `&` patterns can match against mutable
+ // references (RFC 3627, Rule 5). If we implement a pattern typing
+ // ruleset with Rule 4 but not Rule 5, we'll need to check that here.
+ // FIXME(ref_pat_eat_one_layer_2024_structural): If we already tried
+ // matching the real reference, the error message should explain that
+ // falling back to the inherited reference didn't work. This should be
+ // the same error as the old-Edition version below.
+ debug_assert!(ref_pat_matches_mut_ref);
+ // FIXME: Emit an error.
+ }
+
+ pat_info.binding_mode = ByRef::No;
+ self.result.skipped_ref_pats.insert(pat);
+ self.infer_pat(inner, expected, pat_info);
+ return expected;
+ }
+ }
+ InheritedRefMatchRule::EatBoth { consider_inherited_ref: true } => {
+ // Reset binding mode on old editions
+ pat_info.binding_mode = ByRef::No;
+
+ if let TyKind::Ref(_, inner_ty, _) = expected.kind() {
+ // Consume both the inherited and inner references.
+ if pat_mutbl.is_mut() && inh_mut.is_mut() {
+ // As a special case, a `&mut` reference pattern will be able to match
+ // against a reference type of any mutability if the inherited ref is
+ // mutable. Since this allows us to match against a shared reference
+ // type, we refer to this as "falling back" to matching the inherited
+ // reference, though we consume the real reference as well. We handle
+ // this here to avoid adding this case to the common logic below.
+ self.infer_pat(inner, inner_ty, pat_info);
+ return expected;
+ } else {
+ // Otherwise, use the common logic below for matching the inner
+ // reference type.
+ // FIXME(ref_pat_eat_one_layer_2024_structural): If this results in a
+ // mutability mismatch, the error message should explain that falling
+ // back to the inherited reference didn't work. This should be the same
+ // error as the Edition 2024 version above.
+ }
+ } else {
+ // The expected type isn't a reference type, so only match against the
+ // inherited reference.
+ if pat_mutbl > inh_mut {
+ // We can't match a lone inherited shared reference with `&mut`.
+ // FIXME: Emit an error.
+ }
- match expected.kind() {
- TyKind::Array(_, const_) => {
- Ty::new_array_with_const_len(self.interner(), elem_ty, const_)
+ self.result.skipped_ref_pats.insert(pat);
+ self.infer_pat(inner, expected, pat_info);
+ return expected;
+ }
+ }
+ InheritedRefMatchRule::EatBoth { consider_inherited_ref: false } => {
+ // Reset binding mode on stable Rust. This will be a type error below if
+ // `expected` is not a reference type.
+ pat_info.binding_mode = ByRef::No;
+ }
}
- _ => Ty::new_slice(self.interner(), elem_ty),
}
- }
- fn infer_lit_pat(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> {
- // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`.
- if let Expr::Literal(Literal::ByteString(_)) = self.store[expr]
- && let TyKind::Ref(_, inner, _) = expected.kind()
- {
- let inner = self.table.try_structurally_resolve_type(inner);
- if matches!(inner.kind(), TyKind::Slice(_)) {
- let elem_ty = self.types.types.u8;
- let slice_ty = Ty::new_slice(self.interner(), elem_ty);
- let ty = Ty::new_ref(
- self.interner(),
- self.types.regions.statik,
- slice_ty,
- Mutability::Not,
- );
- self.write_expr_ty(expr, ty);
- return ty;
+ let (ref_ty, inner_ty) = match self.check_dereferenceable(expected, inner) {
+ Ok(()) => {
+ // `demand::subtype` would be good enough, but using `eqtype` turns
+ // out to be equally general. See (note_1) for details.
+
+ // Take region, inner-type from expected type if we can,
+ // to avoid creating needless variables. This also helps with
+ // the bad interactions of the given hack detailed in (note_1).
+ debug!("check_pat_ref: expected={:?}", expected);
+ match expected.as_reference() {
+ Some((r_ty, _, r_mutbl))
+ if ((ref_pat_matches_mut_ref && r_mutbl >= pat_mutbl)
+ || r_mutbl == pat_mutbl) =>
+ {
+ if r_mutbl == Mutability::Not {
+ pat_info.max_ref_mutbl = MutblCap::Not;
+ }
+
+ (expected, r_ty)
+ }
+ _ => {
+ let inner_ty = self.table.next_ty_var(inner.into());
+ let ref_ty = self.new_ref_ty(inner.into(), pat_mutbl, inner_ty);
+ debug!("check_pat_ref: demanding {:?} = {:?}", expected, ref_ty);
+ _ = self.demand_eqtype(pat.into(), expected, ref_ty);
+
+ (ref_ty, inner_ty)
+ }
+ }
}
- }
+ Err(()) => {
+ let err = self.types.types.error;
+ (err, err)
+ }
+ };
- self.infer_expr(expr, &Expectation::has_type(expected), ExprIsRead::Yes)
+ self.infer_pat(inner, inner_ty, pat_info);
+ ref_ty
}
- fn is_non_ref_pat(&mut self, store: &hir_def::expr_store::ExpressionStore, pat: PatId) -> bool {
- match &store[pat] {
- Pat::Tuple { .. }
- | Pat::TupleStruct { .. }
- | Pat::Record { .. }
- | Pat::Range { .. }
- | Pat::Slice { .. } => true,
- Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(store, *p)),
- Pat::Path(path) => {
- // A const is a reference pattern, but other value ns things aren't (see #16131).
- let resolved = self.resolve_value_path_inner(path, pat.into(), true);
- resolved.is_some_and(|it| !matches!(it.0, hir_def::resolver::ValueNs::ConstId(_)))
- }
- Pat::ConstBlock(..) => false,
- Pat::Lit(expr) => !matches!(
- store[*expr],
- Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..))
- ),
- Pat::Wild
- | Pat::Bind { .. }
- | Pat::Ref { .. }
- | Pat::Box { .. }
- | Pat::Missing
- | Pat::Expr(_) => false,
- }
+ /// Create a reference or pinned reference type with a fresh region variable.
+ fn new_ref_ty(&self, span: Span, mutbl: Mutability, ty: Ty<'db>) -> Ty<'db> {
+ let region = self.table.next_region_var(span);
+ Ty::new_ref(self.interner(), region, ty, mutbl)
}
fn try_resolve_slice_ty_to_array_ty(
- &mut self,
+ &self,
before: &[PatId],
- suffix: &[PatId],
slice: Option<PatId>,
+ pat: PatId,
) -> Option<Ty<'db>> {
if slice.is_some() {
return None;
}
- let len = before.len() + suffix.len();
- let size = consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db));
+ let interner = self.interner();
+ let len = before.len();
+ let inner_ty = self.table.next_ty_var(pat.into());
- let elem_ty = self.table.next_ty_var();
- let array_ty = Ty::new_array_with_const_len(self.interner(), elem_ty, size);
- Some(array_ty)
+ Some(Ty::new_array(interner, inner_ty, len.try_into().unwrap()))
}
- /// Used to determine whether we can infer the expected type in the slice pattern to be of type array.
+ /// Used to determines whether we can infer the expected type in the slice pattern to be of type array.
/// This is only possible if we're in an irrefutable pattern. If we were to allow this in refutable
/// patterns we wouldn't e.g. report ambiguity in the following situation:
///
/// ```ignore(rust)
- /// struct Zeroes;
+ /// struct Zeroes;
/// const ARR: [usize; 2] = [0; 2];
/// const ARR2: [usize; 2] = [2; 2];
///
@@ -666,15 +1564,160 @@ impl<'db> InferenceContext<'_, 'db> {
///
/// If we're in an irrefutable pattern we prefer the array impl candidate given that
/// the slice impl candidate would be rejected anyway (if no ambiguity existed).
- fn pat_is_irrefutable(&self, decl_ctxt: Option<DeclContext>) -> bool {
- matches!(decl_ctxt, Some(DeclContext { origin: DeclOrigin::LocalDecl { has_else: false } }))
+ fn pat_is_irrefutable(&self, pat_origin: PatOrigin) -> bool {
+ match pat_origin {
+ PatOrigin::LetExpr | PatOrigin::MatchArm => false,
+ PatOrigin::LetStmt { has_else } => !has_else,
+ PatOrigin::DestructuringAssignment | PatOrigin::Param => true,
+ }
}
-}
-pub(super) fn contains_explicit_ref_binding(store: &ExpressionStore, pat_id: PatId) -> bool {
- let mut res = false;
- store.walk_pats(pat_id, &mut |pat| {
- res |= matches!(store[pat], Pat::Bind { id, .. } if matches!(store[id].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut));
- });
- res
+ /// Type check a slice pattern.
+ ///
+ /// Syntactically, these look like `[pat_0, ..., pat_n]`.
+ /// Semantically, we are type checking a pattern with structure:
+ /// ```ignore (not-rust)
+ /// [before_0, ..., before_n, (slice, after_0, ... after_n)?]
+ /// ```
+ /// The type of `slice`, if it is present, depends on the `expected` type.
+ /// If `slice` is missing, then so is `after_i`.
+ /// If `slice` is present, it can still represent 0 elements.
+ fn infer_slice_pat(
+ &mut self,
+ pat: PatId,
+ before: &[PatId],
+ slice: Option<PatId>,
+ after: &[PatId],
+ expected: Ty<'db>,
+ pat_info: PatInfo,
+ ) -> Ty<'db> {
+ let expected = self.table.try_structurally_resolve_type(pat.into(), expected);
+
+ // If the pattern is irrefutable and `expected` is an infer ty, we try to equate it
+ // to an array if the given pattern allows it. See issue #76342
+ if self.pat_is_irrefutable(pat_info.pat_origin)
+ && expected.is_ty_var()
+ && let Some(resolved_arr_ty) = self.try_resolve_slice_ty_to_array_ty(before, slice, pat)
+ {
+ debug!(?resolved_arr_ty);
+ let _ = self.demand_eqtype(pat.into(), expected, resolved_arr_ty);
+ }
+
+ let expected = self.structurally_resolve_type(pat.into(), expected);
+ debug!(?expected);
+
+ let (element_ty, opt_slice_ty, inferred) = match expected.kind() {
+ // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`.
+ TyKind::Array(element_ty, len) => {
+ let min = before.len() as u64 + after.len() as u64;
+ let (opt_slice_ty, expected) =
+ self.check_array_pat_len(pat, element_ty, expected, slice, len, min.into());
+ // `opt_slice_ty.is_none()` => `slice.is_none()`.
+ // Note, though, that opt_slice_ty could be `Some(error_ty)`.
+ assert!(opt_slice_ty.is_some() || slice.is_none());
+ (element_ty, opt_slice_ty, expected)
+ }
+ TyKind::Slice(element_ty) => (element_ty, Some(expected), expected),
+ // The expected type must be an array or slice, but was neither, so error.
+ _ => {
+ self.push_diagnostic(InferenceDiagnostic::ExpectedArrayOrSlicePat {
+ pat,
+ found: expected.store(),
+ });
+ let err = self.types.types.error;
+ (err, Some(err), err)
+ }
+ };
+
+ // Type check all the patterns before `slice`.
+ for &elt in before {
+ self.infer_pat(elt, element_ty, pat_info);
+ }
+ // Type check the `slice`, if present, against its expected type.
+ if let Some(slice) = slice {
+ self.infer_pat(slice, opt_slice_ty.unwrap(), pat_info);
+ }
+ // Type check the elements after `slice`, if present.
+ for &elt in after {
+ self.infer_pat(elt, element_ty, pat_info);
+ }
+ inferred
+ }
+
+ /// Type check the length of an array pattern.
+ ///
+ /// Returns both the type of the variable length pattern (or `None`), and the potentially
+ /// inferred array type. We only return `None` for the slice type if `slice.is_none()`.
+ fn check_array_pat_len(
+ &mut self,
+ pat: PatId,
+ element_ty: Ty<'db>,
+ arr_ty: Ty<'db>,
+ slice: Option<PatId>,
+ len: Const<'db>,
+ min_len: u128,
+ ) -> (Option<Ty<'db>>, Ty<'db>) {
+ let len = crate::consteval::try_const_usize(self.db, len);
+
+ if let Some(len) = len {
+ // Now we know the length...
+ if slice.is_none() {
+ // ...and since there is no variable-length pattern,
+ // we require an exact match between the number of elements
+ // in the array pattern and as provided by the matched type.
+ if min_len == len {
+ return (None, arr_ty);
+ }
+
+ self.push_diagnostic(InferenceDiagnostic::MismatchedArrayPatLen {
+ pat,
+ expected: len,
+ found: min_len,
+ has_rest: false,
+ });
+ } else if let Some(pat_len) = len.checked_sub(min_len) {
+ // The variable-length pattern was there,
+ // so it has an array type with the remaining elements left as its size...
+ return (Some(Ty::new_array(self.interner(), element_ty, pat_len)), arr_ty);
+ } else {
+ // ...however, in this case, there were no remaining elements.
+ // That is, the slice pattern requires more than the array type offers.
+ self.push_diagnostic(InferenceDiagnostic::MismatchedArrayPatLen {
+ pat,
+ expected: len,
+ found: min_len,
+ has_rest: true,
+ });
+ }
+ } else if slice.is_none() {
+ // We have a pattern with a fixed length,
+ // which we can use to infer the length of the array.
+ let updated_arr_ty = Ty::new_array(self.interner(), element_ty, min_len);
+ _ = self.demand_eqtype(pat.into(), updated_arr_ty, arr_ty);
+ return (None, updated_arr_ty);
+ } else {
+ // We have a variable-length pattern and don't know the array length.
+ // This happens if we have e.g.,
+ // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`.
+ // FIXME: Emit an error: cannot pattern-match on an array without a fixed length.
+ };
+
+ // If we get here, we must have emitted an error.
+ (Some(self.types.types.error), arr_ty)
+ }
+
+ fn infer_destructuring_assignment_expr(&mut self, expr: ExprId, expected: Ty<'db>) -> Ty<'db> {
+ // LHS of assignment doesn't constitute reads.
+ let expr_is_read = ExprIsRead::No;
+ let lhs_ty = self.infer_expr_inner(expr, &Expectation::has_type(expected), expr_is_read);
+ match self.coerce(expr, expected, lhs_ty, AllowTwoPhase::No, expr_is_read) {
+ Ok(ty) => ty,
+ Err(_) => {
+ self.emit_type_mismatch(expr.into(), expected, lhs_ty);
+ // `rhs_ty` is returned so no further type mismatches are
+ // reported because of this mismatch.
+ expected
+ }
+ }
+ }
}
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 3cadc8e933..704f15cc86 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -11,14 +11,14 @@ use rustc_type_ir::inherent::{SliceLike, Ty as _};
use stdx::never;
use crate::{
- InferenceDiagnostic, ValueTyDefId,
- generics::generics,
- infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext,
+ InferenceDiagnostic, Span, ValueTyDefId,
+ infer::{
+ InferenceTyLoweringVarsCtx, diagnostics::InferenceTyLoweringContext as TyLoweringContext,
+ },
lower::{GenericPredicates, LifetimeElisionKind},
method_resolution::{self, CandidateId, MethodError},
next_solver::{
- GenericArg, GenericArgs, TraitRef, Ty,
- infer::traits::{Obligation, ObligationCause},
+ GenericArg, GenericArgs, TraitRef, Ty, Unnormalized, infer::traits::ObligationCause,
util::clauses_as_obligations,
},
};
@@ -26,29 +26,36 @@ use crate::{
use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource};
impl<'db> InferenceContext<'_, 'db> {
- pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty<'db>> {
- 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),
- };
+ pub(super) fn infer_path(
+ &mut self,
+ path: &Path,
+ id: ExprOrPatId,
+ ) -> Option<(ValueNs, Ty<'db>)> {
+ let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?;
+
+ let (value_def, generic_def, substs) =
+ match self.resolve_value_path(path, id, value, self_subst)? {
+ ValuePathResolution::GenericDef(value_def, generic_def, substs) => {
+ (value_def, generic_def, substs)
+ }
+ ValuePathResolution::NonGeneric(ty) => return Some((value, ty)),
+ };
let args = self.insert_type_vars(substs);
- self.add_required_obligations_for_value_path(generic_def, args);
+ self.add_required_obligations_for_value_path(id, generic_def, args);
- let ty = self.db.value_ty(value_def)?.instantiate(self.interner(), args);
+ let ty = self.db.value_ty(value_def)?.instantiate(self.interner(), args).skip_norm_wip();
let ty = self.process_remote_user_written_ty(ty);
- Some(ty)
+ Some((value, ty))
}
fn resolve_value_path(
&mut self,
path: &Path,
id: ExprOrPatId,
+ value: ValueNs,
+ self_subst: Option<GenericArgs<'db>>,
) -> Option<ValuePathResolution<'db>> {
- let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?;
-
let value_def: ValueTyDefId = match value {
ValueNs::FunctionId(it) => it.into(),
ValueNs::ConstId(it) => it.into(),
@@ -73,7 +80,7 @@ impl<'db> InferenceContext<'_, 'db> {
};
}
ValueNs::ImplSelf(impl_id) => {
- let ty = self.db.impl_self_ty(impl_id).instantiate_identity();
+ let ty = self.db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip();
return if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() {
Some(ValuePathResolution::GenericDef(
struct_id.into(),
@@ -86,7 +93,7 @@ impl<'db> InferenceContext<'_, 'db> {
};
}
ValueNs::GenericParam(it) => {
- return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty_ns(it)));
+ return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty(it)));
}
};
@@ -112,7 +119,7 @@ impl<'db> InferenceContext<'_, 'db> {
if let Some(last_segment) = last_segment {
path_ctx.set_current_segment(last_segment)
}
- path_ctx.substs_from_path(value_def, true, false)
+ path_ctx.substs_from_path(value_def, true, false, id.into())
})
};
@@ -134,14 +141,23 @@ impl<'db> InferenceContext<'_, 'db> {
no_diagnostics: bool,
) -> Option<(ValueNs, Option<GenericArgs<'db>>)> {
// Don't use `self.make_ty()` here as we need `orig_ns`.
+ let mut vars_ctx = InferenceTyLoweringVarsCtx {
+ table: &mut self.table,
+ type_of_type_placeholder: &mut self.result.type_of_type_placeholder,
+ };
let mut ctx = TyLoweringContext::new(
self.db,
&self.resolver,
self.store,
&self.diagnostics,
InferenceTyDiagnosticSource::Body,
+ self.store_owner,
self.generic_def,
+ &self.generics,
LifetimeElisionKind::Infer,
+ self.allow_using_generic_params,
+ Some(&mut vars_ctx),
+ &self.defined_anon_consts,
);
let mut path_ctx = if no_diagnostics {
ctx.at_path_forget_diagnostics(path)
@@ -152,10 +168,10 @@ impl<'db> InferenceContext<'_, 'db> {
let last = path.segments().last()?;
let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref);
- let ty = self.table.process_user_written_ty(ty);
+ let ty = path_ctx.expect_table().process_user_written_ty(ty);
path_ctx.ignore_last_segment();
- let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns, true);
+ let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns, true, id.into());
drop_ctx(ctx, no_diagnostics);
let ty = self.table.process_user_written_ty(ty);
self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
@@ -182,9 +198,13 @@ impl<'db> InferenceContext<'_, 'db> {
let (resolution, substs) = match (def, is_before_last) {
(TypeNs::TraitId(trait_), true) => {
- let self_ty = self.table.next_ty_var();
- let trait_ref =
- path_ctx.lower_trait_ref_from_resolved_path(trait_, self_ty, true);
+ let self_ty = path_ctx.expect_table().next_ty_var(id.into());
+ let trait_ref = path_ctx.lower_trait_ref_from_resolved_path(
+ trait_,
+ self_ty,
+ true,
+ id.into(),
+ );
drop_ctx(ctx, no_diagnostics);
self.resolve_trait_assoc_item(trait_ref, last_segment, id)
}
@@ -194,7 +214,7 @@ impl<'db> InferenceContext<'_, 'db> {
// should resolve to an associated type of that trait (e.g. `<T
// as Iterator>::Item::default`)
path_ctx.ignore_last_segment();
- let (ty, _) = path_ctx.lower_partly_resolved_path(def, true);
+ let (ty, _) = path_ctx.lower_partly_resolved_path(def, true, id.into());
drop_ctx(ctx, no_diagnostics);
if ty.is_ty_error() {
return None;
@@ -219,8 +239,9 @@ impl<'db> InferenceContext<'_, 'db> {
}
}
- fn add_required_obligations_for_value_path(
+ pub(super) fn add_required_obligations_for_value_path(
&mut self,
+ node: ExprOrPatId,
def: GenericDefId,
subst: GenericArgs<'db>,
) {
@@ -228,29 +249,12 @@ impl<'db> InferenceContext<'_, 'db> {
let predicates = GenericPredicates::query_all(self.db, def);
let param_env = self.table.param_env;
self.table.register_predicates(clauses_as_obligations(
- predicates.iter_instantiated(interner, subst.as_slice()),
- ObligationCause::new(),
+ predicates
+ .iter_instantiated(interner, subst.as_slice())
+ .map(Unnormalized::skip_norm_wip),
+ ObligationCause::new(node),
param_env,
));
-
- // 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).container,
- GenericDefId::ConstId(id) => id.lookup(self.db).container,
- _ => return,
- };
-
- if let ItemContainerId::TraitId(trait_) = container {
- let parent_len = generics(self.db, def).parent_generics().map_or(0, |g| g.len_self());
- let parent_subst = GenericArgs::new_from_slice(&subst.as_slice()[..parent_len]);
- let trait_ref = TraitRef::new_from_args(interner, trait_.into(), parent_subst);
- self.table.register_predicate(Obligation::new(
- interner,
- ObligationCause::new(),
- param_env,
- trait_ref,
- ));
- }
}
fn resolve_trait_assoc_item(
@@ -304,7 +308,7 @@ impl<'db> InferenceContext<'_, 'db> {
return Some(result);
}
- let res = self.with_method_resolution(|ctx| {
+ let res = self.with_method_resolution(Span::Dummy, Span::Dummy, |ctx| {
ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty)
});
let (item, visible) = match res {
@@ -324,28 +328,23 @@ impl<'db> InferenceContext<'_, 'db> {
};
let substs = match container {
ItemContainerId::ImplId(impl_id) => {
- let impl_substs = self.table.fresh_args_for_item(impl_id.into());
- let impl_self_ty =
- self.db.impl_self_ty(impl_id).instantiate(self.interner(), impl_substs);
- self.unify(impl_self_ty, ty);
+ let impl_substs = self.table.fresh_args_for_item(id.into(), impl_id.into());
+ let impl_self_ty = self
+ .db
+ .impl_self_ty(impl_id)
+ .instantiate(self.interner(), impl_substs)
+ .skip_norm_wip();
+ _ = self.demand_eqtype(id, impl_self_ty, ty);
impl_substs
}
ItemContainerId::TraitId(trait_) => {
// we're picking this method
- let args = GenericArgs::fill_rest(
+ GenericArgs::fill_rest(
self.interner(),
trait_.into(),
[ty.into()],
- |_, id, _| self.table.next_var_for_param(id),
- );
- let trait_ref = TraitRef::new_from_args(self.interner(), trait_.into(), args);
- self.table.register_predicate(Obligation::new(
- self.interner(),
- ObligationCause::new(),
- self.table.param_env,
- trait_ref,
- ));
- args
+ |_, param, _| self.table.var_for_def(param, id.into()),
+ )
}
ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
never!("assoc item contained in module/extern block");
@@ -370,7 +369,7 @@ impl<'db> InferenceContext<'_, 'db> {
name: &Name,
id: ExprOrPatId,
) -> Option<(ValueNs, GenericArgs<'db>)> {
- let ty = self.table.try_structurally_resolve_type(ty);
+ let ty = self.table.try_structurally_resolve_type(id.into(), ty);
let (enum_id, subst) = match ty.as_adt() {
Some((AdtId::EnumId(e), subst)) => (e, subst),
_ => return None,
diff --git a/crates/hir-ty/src/infer/place_op.rs b/crates/hir-ty/src/infer/place_op.rs
index 1298b38097..bbf047b8ba 100644
--- a/crates/hir-ty/src/infer/place_op.rs
+++ b/crates/hir-ty/src/infer/place_op.rs
@@ -1,7 +1,6 @@
//! Inference of *place operators*: deref and indexing (operators that create places, as opposed to values).
use hir_def::hir::ExprId;
-use intern::sym;
use rustc_ast_ir::Mutability;
use rustc_type_ir::inherent::{IntoKind, Ty as _};
use tracing::debug;
@@ -29,9 +28,10 @@ pub(super) enum PlaceOp {
impl<'a, 'db> InferenceContext<'a, 'db> {
pub(super) fn try_overloaded_deref(
&self,
+ expr: ExprId,
base_ty: Ty<'db>,
) -> Option<InferOk<'db, MethodCallee<'db>>> {
- self.try_overloaded_place_op(base_ty, None, PlaceOp::Deref)
+ self.try_overloaded_place_op(expr, base_ty, None, PlaceOp::Deref)
}
/// For the overloaded place expressions (`*x`, `x[3]`), the trait
@@ -57,7 +57,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
return Some(ty);
}
- let ok = self.try_overloaded_deref(oprnd_ty)?;
+ let ok = self.try_overloaded_deref(expr, oprnd_ty)?;
let method = self.table.register_infer_ok(ok);
if let TyKind::Ref(_, _, Mutability::Not) = method.sig.inputs_and_output.inputs()[0].kind()
{
@@ -81,6 +81,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
&mut self,
expr: ExprId,
base_expr: ExprId,
+ index_expr: ExprId,
base_ty: Ty<'db>,
idx_ty: Ty<'db>,
) -> Option<(/*index type*/ Ty<'db>, /*element type*/ Ty<'db>)> {
@@ -88,10 +89,11 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// autoderef that normal method probing does. They could likely be
// consolidated.
- let mut autoderef = InferenceContextAutoderef::new_from_inference_context(self, base_ty);
+ let mut autoderef =
+ InferenceContextAutoderef::new_from_inference_context(self, base_ty, base_expr.into());
let mut result = None;
while result.is_none() && autoderef.next().is_some() {
- result = Self::try_index_step(expr, base_expr, &mut autoderef, idx_ty);
+ result = Self::try_index_step(expr, base_expr, index_expr, &mut autoderef, idx_ty);
}
result
}
@@ -104,11 +106,12 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
fn try_index_step(
expr: ExprId,
base_expr: ExprId,
+ index_expr: ExprId,
autoderef: &mut InferenceContextAutoderef<'_, 'a, 'db>,
index_ty: Ty<'db>,
) -> Option<(/*index type*/ Ty<'db>, /*element type*/ Ty<'db>)> {
let ty = autoderef.final_ty();
- let adjusted_ty = autoderef.ctx().table.structurally_resolve_type(ty);
+ let adjusted_ty = autoderef.ctx().structurally_resolve_type(base_expr.into(), ty);
debug!(
"try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
index_ty={:?})",
@@ -123,7 +126,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
let ctx = autoderef.ctx();
ctx.table.register_predicate(Obligation::new(
ctx.interner(),
- ObligationCause::new(),
+ ObligationCause::new(base_expr),
ctx.table.param_env,
ClauseKind::ConstArgHasType(ct, ctx.types.types.usize),
));
@@ -136,9 +139,13 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// If some lookup succeeds, write callee into table and extract index/element
// type from the method signature.
// If some lookup succeeded, install method in table
- let input_ty = autoderef.ctx().table.next_ty_var();
- let method =
- autoderef.ctx().try_overloaded_place_op(self_ty, Some(input_ty), PlaceOp::Index);
+ let input_ty = autoderef.ctx().table.next_ty_var(index_expr.into());
+ let method = autoderef.ctx().try_overloaded_place_op(
+ expr,
+ self_ty,
+ Some(input_ty),
+ PlaceOp::Index,
+ );
if let Some(result) = method {
debug!("try_index_step: success, using overloaded indexing");
@@ -180,15 +187,16 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
/// `convert_place_derefs_to_mutable`.
pub(super) fn try_overloaded_place_op(
&self,
+ expr: ExprId,
base_ty: Ty<'db>,
opt_rhs_ty: Option<Ty<'db>>,
op: PlaceOp,
) -> Option<InferOk<'db, MethodCallee<'db>>> {
debug!("try_overloaded_place_op({:?},{:?})", base_ty, op);
- let (Some(imm_tr), imm_op) = (match op {
- PlaceOp::Deref => (self.lang_items.Deref, sym::deref),
- PlaceOp::Index => (self.lang_items.Index, sym::index),
+ let (Some(imm_tr), Some(imm_op)) = (match op {
+ PlaceOp::Deref => (self.lang_items.Deref, self.lang_items.Deref_deref),
+ PlaceOp::Index => (self.lang_items.Index, self.lang_items.Index_index),
}) else {
// Bail if `Deref` or `Index` isn't defined.
return None;
@@ -198,9 +206,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// opaque types as rigid here to support `impl Deref<Target = impl Index<usize>>`.
let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
self.table.lookup_method_for_operator(
- ObligationCause::new(),
- imm_op,
+ ObligationCause::new(expr),
imm_tr,
+ imm_op,
base_ty,
opt_rhs_ty,
treat_opaques,
@@ -209,6 +217,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
pub(super) fn try_mutable_overloaded_place_op(
table: &InferenceTable<'db>,
+ expr: ExprId,
base_ty: Ty<'db>,
opt_rhs_ty: Option<Ty<'db>>,
op: PlaceOp,
@@ -216,9 +225,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
debug!("try_mutable_overloaded_place_op({:?},{:?})", base_ty, op);
let lang_items = table.interner().lang_items();
- let (Some(mut_tr), mut_op) = (match op {
- PlaceOp::Deref => (lang_items.DerefMut, sym::deref_mut),
- PlaceOp::Index => (lang_items.IndexMut, sym::index_mut),
+ let (Some(mut_tr), Some(mut_op)) = (match op {
+ PlaceOp::Deref => (lang_items.DerefMut, lang_items.DerefMut_deref_mut),
+ PlaceOp::Index => (lang_items.IndexMut, lang_items.IndexMut_index_mut),
}) else {
// Bail if `DerefMut` or `IndexMut` isn't defined.
return None;
@@ -230,9 +239,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// of the opaque.
let treat_opaques = TreatNotYetDefinedOpaques::AsInfer;
table.lookup_method_for_operator(
- ObligationCause::new(),
- mut_op,
+ ObligationCause::new(expr),
mut_tr,
+ mut_op,
base_ty,
opt_rhs_ty,
treat_opaques,
@@ -276,7 +285,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
))
}
};
- let method = Self::try_mutable_overloaded_place_op(&self.table, base_ty, arg_ty, op);
+ let method = Self::try_mutable_overloaded_place_op(&self.table, expr, base_ty, arg_ty, op);
let method = match method {
Some(ok) => self.table.register_infer_ok(ok),
// Couldn't find the mutable variant of the place op, keep the
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index b0f916b8c0..f9ad76b0c1 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -3,35 +3,35 @@
use std::fmt;
use base_db::Crate;
-use hir_def::{AdtId, ExpressionStoreOwnerId, GenericParamId, TraitId};
+use hir_def::{ExpressionStoreOwnerId, GenericParamId, TraitId};
use rustc_hash::FxHashSet;
use rustc_type_ir::{
- TyVid, TypeFoldable, TypeVisitableExt, UpcastFrom,
+ TyVid, TypeFoldable, TypeVisitableExt,
inherent::{Const as _, GenericArg as _, IntoKind, Ty as _},
solve::Certainty,
};
use smallvec::SmallVec;
+use thin_vec::ThinVec;
use crate::{
+ InferenceDiagnostic, Span,
db::HirDatabase,
next_solver::{
- Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Goal,
- ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, TyKind,
- TypingMode,
+ Canonical, ClauseKind, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArg,
+ GenericArgs, ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty,
+ TyKind, TypingMode,
fulfill::{FulfillmentCtxt, NextSolverError},
infer::{
- DbInternerInferExt, InferCtxt, InferOk, InferResult,
- at::{At, ToTrace},
+ DbInternerInferExt, InferCtxt, InferOk,
+ at::At,
snapshot::CombinedSnapshot,
traits::{Obligation, ObligationCause, PredicateObligation},
},
inspect::{InspectConfig, InspectGoal, ProofTreeVisitor},
obligation_ctxt::ObligationCtxt,
},
- traits::{
- NextTraitSolveResult, ParamEnvAndCrate, next_trait_solve_canonical_in_ctxt,
- next_trait_solve_in_ctxt,
- },
+ solver_errors::SolverDiagnostic,
+ traits::ParamEnvAndCrate,
};
struct NestedObligationsForSelfTy<'a, 'db> {
@@ -44,6 +44,10 @@ struct NestedObligationsForSelfTy<'a, 'db> {
impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> {
type Result = ();
+ fn span(&self) -> Span {
+ self.root_cause.span()
+ }
+
fn config(&self) -> InspectConfig {
// Using an intentionally low depth to minimize the chance of future
// breaking changes in case we adapt the approach later on. This also
@@ -63,7 +67,7 @@ impl<'a, 'db> ProofTreeVisitor<'db> for NestedObligationsForSelfTy<'a, 'db> {
if self.ctx.predicate_has_self_ty(goal.predicate, self.self_ty) {
self.obligations_for_self_ty.push(Obligation::new(
db,
- self.root_cause.clone(),
+ *self.root_cause,
goal.param_env,
goal.predicate,
));
@@ -115,7 +119,7 @@ fn could_unify_impl<'db>(
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
let cause = ObligationCause::dummy();
let at = infcx.at(&cause, env.param_env);
- let ((ty1_with_vars, ty2_with_vars), _) = infcx.instantiate_canonical(tys);
+ let ((ty1_with_vars, ty2_with_vars), _) = infcx.instantiate_canonical(Span::Dummy, tys);
let mut ctxt = ObligationCtxt::new(&infcx);
let can_unify = at
.eq(ty1_with_vars, ty2_with_vars)
@@ -124,18 +128,13 @@ fn could_unify_impl<'db>(
can_unify && select(&mut ctxt).is_empty()
}
-#[derive(Clone)]
pub(crate) struct InferenceTable<'db> {
pub(crate) db: &'db dyn HirDatabase,
pub(crate) param_env: ParamEnv<'db>,
pub(crate) infer_ctxt: InferCtxt<'db>,
pub(super) fulfillment_cx: FulfillmentCtxt<'db>,
pub(super) diverging_type_vars: FxHashSet<Ty<'db>>,
-}
-
-pub(crate) struct InferenceTableSnapshot<'db> {
- ctxt_snapshot: CombinedSnapshot,
- obligations: FulfillmentCtxt<'db>,
+ pub(super) trait_errors: Vec<NextSolverError<'db>>,
}
impl<'db> InferenceTable<'db> {
@@ -145,14 +144,10 @@ impl<'db> InferenceTable<'db> {
db: &'db dyn HirDatabase,
trait_env: ParamEnv<'db>,
krate: Crate,
- owner: Option<ExpressionStoreOwnerId>,
+ owner: ExpressionStoreOwnerId,
) -> Self {
let interner = DbInterner::new_with(db, krate);
- let typing_mode = match owner {
- Some(owner) => TypingMode::typeck_for_body(interner, owner.into()),
- // IDE things wants to reveal opaque types.
- None => TypingMode::PostAnalysis,
- };
+ let typing_mode = TypingMode::typeck_for_body(interner, owner.into());
let infer_ctxt = interner.infer_ctxt().build(typing_mode);
InferenceTable {
db,
@@ -160,6 +155,7 @@ impl<'db> InferenceTable<'db> {
fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt),
infer_ctxt,
diverging_type_vars: FxHashSet::default(),
+ trait_errors: Vec::new(),
}
}
@@ -172,6 +168,10 @@ impl<'db> InferenceTable<'db> {
self.infer_ctxt.type_is_copy_modulo_regions(self.param_env, ty)
}
+ pub(crate) fn type_is_sized_modulo_regions(&self, ty: Ty<'db>) -> bool {
+ self.infer_ctxt.type_is_sized_modulo_regions(self.param_env, ty)
+ }
+
pub(crate) fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'db>) -> bool {
self.infer_ctxt.type_is_use_cloned_modulo_regions(self.param_env, ty)
}
@@ -253,29 +253,12 @@ impl<'db> InferenceTable<'db> {
self.diverging_type_vars.insert(ty);
}
- pub(crate) fn canonicalize<T>(&mut self, t: T) -> rustc_type_ir::Canonical<DbInterner<'db>, T>
- where
- T: TypeFoldable<DbInterner<'db>>,
- {
- // try to resolve obligations before canonicalizing, since this might
- // result in new knowledge about variables
- self.select_obligations_where_possible();
- self.infer_ctxt.canonicalize_response(t)
- }
-
- pub(crate) fn normalize_alias_ty(&mut self, alias: Ty<'db>) -> Ty<'db> {
- self.infer_ctxt
- .at(&ObligationCause::new(), self.param_env)
- .structurally_normalize_ty(alias, &mut self.fulfillment_cx)
- .unwrap_or(alias)
- }
-
- pub(crate) fn next_ty_var(&self) -> Ty<'db> {
- self.infer_ctxt.next_ty_var()
+ pub(crate) fn next_ty_var(&self, span: Span) -> Ty<'db> {
+ self.infer_ctxt.next_ty_var(span)
}
- pub(crate) fn next_const_var(&self) -> Const<'db> {
- self.infer_ctxt.next_const_var()
+ pub(crate) fn next_const_var(&self, span: Span) -> Const<'db> {
+ self.infer_ctxt.next_const_var(span)
}
pub(crate) fn next_int_var(&self) -> Ty<'db> {
@@ -286,42 +269,18 @@ impl<'db> InferenceTable<'db> {
self.infer_ctxt.next_float_var()
}
- pub(crate) fn new_maybe_never_var(&mut self) -> Ty<'db> {
- let var = self.next_ty_var();
+ pub(crate) fn new_maybe_never_var(&mut self, span: Span) -> Ty<'db> {
+ let var = self.next_ty_var(span);
self.set_diverging(var);
var
}
- pub(crate) fn next_region_var(&self) -> Region<'db> {
- self.infer_ctxt.next_region_var()
- }
-
- pub(crate) fn next_var_for_param(&self, id: GenericParamId) -> GenericArg<'db> {
- self.infer_ctxt.next_var_for_param(id)
- }
-
- pub(crate) fn resolve_completely<T>(&mut self, value: T) -> T
- where
- T: TypeFoldable<DbInterner<'db>>,
- {
- let value = self.infer_ctxt.resolve_vars_if_possible(value);
-
- let mut goals = vec![];
-
- // FIXME(next-solver): Handle `goals`.
-
- value.fold_with(&mut resolve_completely::Resolver::new(self, true, &mut goals))
+ pub(crate) fn next_region_var(&self, span: Span) -> Region<'db> {
+ self.infer_ctxt.next_region_var(span)
}
- /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that.
- pub(crate) fn unify<T: ToTrace<'db>>(&mut self, ty1: T, ty2: T) -> bool {
- self.try_unify(ty1, ty2).map(|infer_ok| self.register_infer_ok(infer_ok)).is_ok()
- }
-
- /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the
- /// caller needs to deal with them.
- pub(crate) fn try_unify<T: ToTrace<'db>>(&mut self, t1: T, t2: T) -> InferResult<'db, ()> {
- self.at(&ObligationCause::new()).eq(t1, t2)
+ pub(crate) fn var_for_def(&self, id: GenericParamId, span: Span) -> GenericArg<'db> {
+ self.infer_ctxt.var_for_def(id, span)
}
pub(crate) fn at<'a>(&'a self, cause: &'a ObligationCause) -> At<'a, 'db> {
@@ -332,6 +291,10 @@ impl<'db> InferenceTable<'db> {
self.infer_ctxt.shallow_resolve(ty)
}
+ pub(crate) fn resolve_vars_if_possible<T: TypeFoldable<DbInterner<'db>>>(&self, t: T) -> T {
+ self.infer_ctxt.resolve_vars_if_possible(t)
+ }
+
pub(crate) fn resolve_vars_with_obligations<T>(&mut self, t: T) -> T
where
T: rustc_type_ir::TypeFoldable<DbInterner<'db>>,
@@ -351,8 +314,8 @@ impl<'db> InferenceTable<'db> {
}
/// Create a `GenericArgs` full of infer vars for `def`.
- pub(crate) fn fresh_args_for_item(&self, def: SolverDefId) -> GenericArgs<'db> {
- self.infer_ctxt.fresh_args_for_item(def)
+ pub(crate) fn fresh_args_for_item(&self, span: Span, def: SolverDefId) -> GenericArgs<'db> {
+ self.infer_ctxt.fresh_args_for_item(span, def)
}
/// Try to resolve `ty` to a structural type, normalizing aliases.
@@ -360,36 +323,55 @@ impl<'db> InferenceTable<'db> {
/// In case there is still ambiguity, the returned type may be an inference
/// variable. This is different from `structurally_resolve_type` which errors
/// in this case.
- pub(crate) fn try_structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> {
+ pub(crate) fn try_structurally_resolve_type(&mut self, span: Span, ty: Ty<'db>) -> Ty<'db> {
if let TyKind::Alias(..) = ty.kind() {
let result = self
.infer_ctxt
- .at(&ObligationCause::misc(), self.param_env)
+ .at(&ObligationCause::new(span), self.param_env)
.structurally_normalize_ty(ty, &mut self.fulfillment_cx);
match result {
Ok(normalized_ty) => normalized_ty,
- Err(_errors) => Ty::new_error(self.interner(), ErrorGuaranteed),
+ Err(errors) => {
+ self.trait_errors.extend(errors);
+ Ty::new_error(self.interner(), ErrorGuaranteed)
+ }
}
} else {
self.resolve_vars_with_obligations(ty)
}
}
- pub(crate) fn structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> {
- self.try_structurally_resolve_type(ty)
- // FIXME: Err if it still contain infer vars.
+ pub(crate) fn try_structurally_resolve_const(
+ &mut self,
+ sp: Span,
+ ct: Const<'db>,
+ ) -> Const<'db> {
+ let ct = self.resolve_vars_with_obligations(ct);
+
+ if let ConstKind::Unevaluated(..) = ct.kind() {
+ let result = self
+ .infer_ctxt
+ .at(&ObligationCause::new(sp), self.param_env)
+ .structurally_normalize_const(ct, &mut self.fulfillment_cx);
+ match result {
+ Ok(normalized_ct) => normalized_ct,
+ Err(errors) => {
+ self.trait_errors.extend(errors);
+ Const::new_error(self.interner(), ErrorGuaranteed)
+ }
+ }
+ } else {
+ ct
+ }
}
- pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'db> {
- let ctxt_snapshot = self.infer_ctxt.start_snapshot();
- let obligations = self.fulfillment_cx.clone();
- InferenceTableSnapshot { ctxt_snapshot, obligations }
+ pub(crate) fn snapshot(&mut self) -> CombinedSnapshot {
+ self.infer_ctxt.start_snapshot()
}
#[tracing::instrument(skip_all)]
- pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'db>) {
- self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot);
- self.fulfillment_cx = snapshot.obligations;
+ pub(crate) fn rollback_to(&mut self, snapshot: CombinedSnapshot) {
+ self.infer_ctxt.rollback_to(snapshot);
}
pub(crate) fn commit_if_ok<T, E>(
@@ -399,51 +381,12 @@ impl<'db> InferenceTable<'db> {
let snapshot = self.snapshot();
let result = f(self);
match result {
- Ok(_) => {}
- Err(_) => {
- self.rollback_to(snapshot);
- }
+ Ok(_) => self.infer_ctxt.commit_from(snapshot),
+ Err(_) => self.rollback_to(snapshot),
}
result
}
- /// Checks an obligation without registering it. Useful mostly to check
- /// whether a trait *might* be implemented before deciding to 'lock in' the
- /// choice (during e.g. method resolution or deref).
- #[tracing::instrument(level = "debug", skip(self))]
- pub(crate) fn try_obligation(&mut self, predicate: Predicate<'db>) -> NextTraitSolveResult {
- let goal = Goal { param_env: self.param_env, predicate };
- let canonicalized = self.canonicalize(goal);
-
- next_trait_solve_canonical_in_ctxt(&self.infer_ctxt, canonicalized)
- }
-
- pub(crate) fn register_obligation(&mut self, predicate: Predicate<'db>) {
- let goal = Goal { param_env: self.param_env, predicate };
- self.register_obligation_in_env(goal)
- }
-
- #[tracing::instrument(level = "debug", skip(self))]
- fn register_obligation_in_env(&mut self, goal: Goal<'db, Predicate<'db>>) {
- let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal);
- tracing::debug!(?result);
- match result {
- Ok((_, Certainty::Yes)) => {}
- Err(rustc_type_ir::solve::NoSolution) => {}
- Ok((_, Certainty::Maybe { .. })) => {
- self.fulfillment_cx.register_predicate_obligation(
- &self.infer_ctxt,
- Obligation::new(
- self.interner(),
- ObligationCause::new(),
- goal.param_env,
- goal.predicate,
- ),
- );
- }
- }
- }
-
pub(crate) fn register_bound(&mut self, ty: Ty<'db>, def_id: TraitId, cause: ObligationCause) {
if !ty.references_non_lt_error() {
let trait_ref = TraitRef::new(self.interner(), def_id.into(), [ty]);
@@ -463,7 +406,8 @@ impl<'db> InferenceTable<'db> {
}
pub(crate) fn select_obligations_where_possible(&mut self) {
- self.fulfillment_cx.try_evaluate_obligations(&self.infer_ctxt);
+ let errors = self.fulfillment_cx.try_evaluate_obligations(&self.infer_ctxt);
+ self.trait_errors.extend(errors);
}
pub(super) fn register_predicate(&mut self, obligation: PredicateObligation<'db>) {
@@ -478,9 +422,7 @@ impl<'db> InferenceTable<'db> {
where
I: IntoIterator<Item = PredicateObligation<'db>>,
{
- obligations.into_iter().for_each(|obligation| {
- self.register_predicate(obligation);
- });
+ self.fulfillment_cx.register_predicate_obligations(&self.infer_ctxt, obligations);
}
/// checking later, during regionck, that `arg` is well-formed.
@@ -494,9 +436,9 @@ impl<'db> InferenceTable<'db> {
}
/// Registers obligations that all `args` are well-formed.
- pub(crate) fn add_wf_bounds(&mut self, args: GenericArgs<'db>) {
+ pub(crate) fn add_wf_bounds(&mut self, span: Span, args: GenericArgs<'db>) {
for term in args.iter().filter_map(|it| it.as_term()) {
- self.register_wf_obligation(term, ObligationCause::new());
+ self.register_wf_obligation(term, ObligationCause::new(span));
}
}
@@ -507,15 +449,9 @@ impl<'db> InferenceTable<'db> {
self.infer_ctxt.insert_type_vars(ty)
}
- /// 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<'db>) -> Ty<'db> {
- if ty.is_ty_error() { self.next_ty_var() } else { ty }
- }
-
/// Whenever you lower a user-written type, you should call this.
pub(crate) fn process_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
self.process_remote_user_written_ty(ty)
- // FIXME: Register a well-formed obligation.
}
/// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation,
@@ -525,77 +461,17 @@ impl<'db> InferenceTable<'db> {
// See https://github.com/rust-lang/rust/blob/cdb45c87e2cd43495379f7e867e3cc15dcee9f93/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs#L487-L495:
// Even though the new solver only lazily normalizes usually, here we eagerly normalize so that not everything needs
// to normalize before inspecting the `TyKind`.
- // FIXME(next-solver): We should not deeply normalize here, only shallowly.
- self.try_structurally_resolve_type(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<'db>) -> Const<'db> {
- if c.is_ct_error() { self.next_const_var() } else { c }
- }
-
- /// Check if given type is `Sized` or not
- pub(crate) fn is_sized(&mut self, ty: Ty<'db>) -> bool {
- fn short_circuit_trivial_tys(ty: Ty<'_>) -> Option<bool> {
- match ty.kind() {
- TyKind::Bool
- | TyKind::Char
- | TyKind::Int(_)
- | TyKind::Uint(_)
- | TyKind::Float(_)
- | TyKind::Ref(..)
- | TyKind::RawPtr(..)
- | TyKind::Never
- | TyKind::FnDef(..)
- | TyKind::Array(..)
- | TyKind::FnPtr(..) => Some(true),
- TyKind::Slice(..) | TyKind::Str | TyKind::Dynamic(..) => Some(false),
- _ => None,
- }
- }
-
- let mut ty = ty;
- ty = self.try_structurally_resolve_type(ty);
- if let Some(sized) = short_circuit_trivial_tys(ty) {
- return sized;
- }
-
- {
- let mut structs = SmallVec::<[_; 8]>::new();
- // Must use a loop here and not recursion because otherwise users will conduct completely
- // artificial examples of structs that have themselves as the tail field and complain r-a crashes.
- while let Some((AdtId::StructId(id), subst)) = ty.as_adt() {
- let struct_data = id.fields(self.db);
- if let Some((last_field, _)) = struct_data.fields().iter().next_back() {
- let last_field_ty = self.db.field_types(id.into())[last_field]
- .get()
- .instantiate(self.interner(), subst);
- if structs.contains(&ty) {
- // A struct recursively contains itself as a tail field somewhere.
- return true; // Don't overload the users with too many errors.
- }
- structs.push(ty);
- // Structs can have DST as its last field and such cases are not handled
- // as unsized by the chalk, so we do this manually.
- ty = last_field_ty;
- ty = self.try_structurally_resolve_type(ty);
- if let Some(sized) = short_circuit_trivial_tys(ty) {
- return sized;
- }
- } else {
- break;
- };
- }
- }
+ self.try_structurally_resolve_type(Span::Dummy, ty)
+ }
- let Some(sized) = self.interner().lang_items().Sized else {
- return false;
- };
- let sized_pred = Predicate::upcast_from(
- TraitRef::new(self.interner(), sized.into(), [ty]),
- self.interner(),
- );
- self.try_obligation(sized_pred).certain()
+ fn emit_trait_errors(&mut self, diagnostics: &mut ThinVec<InferenceDiagnostic>) {
+ diagnostics.extend(std::mem::take(&mut self.trait_errors).into_iter().filter_map(
+ |error| {
+ let error = error.into_fulfillment_error(&self.infer_ctxt);
+ SolverDiagnostic::from_fulfillment_error(&error)
+ .map(InferenceDiagnostic::SolverDiagnostic)
+ },
+ ));
}
}
@@ -608,20 +484,229 @@ impl fmt::Debug for InferenceTable<'_> {
}
}
-mod resolve_completely {
- use rustc_type_ir::{DebruijnIndex, Flags, TypeFolder, TypeSuperFoldable};
+pub(super) mod resolve_completely {
+ use rustc_hash::FxHashSet;
+ use rustc_type_ir::{
+ DebruijnIndex, Flags, InferConst, InferTy, TypeFlags, TypeFoldable, TypeFolder,
+ TypeSuperFoldable, TypeVisitableExt, inherent::IntoKind,
+ };
+ use stdx::never;
+ use thin_vec::ThinVec;
use crate::{
+ InferenceDiagnostic, Span,
infer::unify::InferenceTable,
next_solver::{
- Const, DbInterner, Goal, Predicate, Region, Term, Ty,
+ Const, ConstKind, DbInterner, DefaultAny, GenericArg, Goal, Predicate, Region, Term,
+ TermKind, Ty, TyKind,
infer::{resolve::ReplaceInferWithError, traits::ObligationCause},
normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals,
},
};
+ pub(crate) struct WriteBackCtxt<'db> {
+ table: InferenceTable<'db>,
+ diagnostics: ThinVec<InferenceDiagnostic>,
+ has_errors: bool,
+ spans_emitted_type_must_be_known_for: FxHashSet<Span>,
+ types: &'db DefaultAny<'db>,
+ }
+
+ impl<'db> WriteBackCtxt<'db> {
+ pub(crate) fn new(
+ table: InferenceTable<'db>,
+ diagnostics: ThinVec<InferenceDiagnostic>,
+ vars_emitted_type_must_be_known_for: FxHashSet<Term<'db>>,
+ ) -> Self {
+ let spans_emitted_type_must_be_known_for = vars_emitted_type_must_be_known_for
+ .into_iter()
+ .filter_map(|term| match term.kind() {
+ TermKind::Ty(ty) => match ty.kind() {
+ TyKind::Infer(InferTy::TyVar(vid)) => {
+ Some(table.infer_ctxt.type_var_span(vid))
+ }
+ _ => None,
+ },
+ TermKind::Const(ct) => match ct.kind() {
+ ConstKind::Infer(InferConst::Var(vid)) => {
+ table.infer_ctxt.const_var_span(vid)
+ }
+ _ => None,
+ },
+ })
+ .collect();
+
+ Self {
+ types: table.interner().default_types(),
+ table,
+ diagnostics,
+ has_errors: false,
+ spans_emitted_type_must_be_known_for,
+ }
+ }
+
+ pub(crate) fn resolve_completely<T>(&mut self, value_ref: &mut T)
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ self.resolve_completely_with_default(value_ref, value_ref.clone());
+ }
+
+ pub(crate) fn resolve_completely_with_default<T>(&mut self, value_ref: &mut T, default: T)
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ let value = std::mem::replace(value_ref, default);
+
+ let value = self.table.resolve_vars_if_possible(value);
+
+ let mut goals = vec![];
+
+ // FIXME(next-solver): Handle `goals`.
+
+ *value_ref = value.fold_with(&mut Resolver::new(self, true, &mut goals));
+ }
+
+ pub(crate) fn resolve_diagnostics(mut self) -> (ThinVec<InferenceDiagnostic>, bool) {
+ let has_errors = self.has_errors;
+
+ self.table.emit_trait_errors(&mut self.diagnostics);
+
+ // Ignore diagnostics made from resolving diagnostics.
+ let mut diagnostics = std::mem::take(&mut self.diagnostics);
+ diagnostics.retain_mut(|diagnostic| {
+ self.resolve_completely(diagnostic);
+
+ if let InferenceDiagnostic::ExpectedFunction { found: ty, .. }
+ | InferenceDiagnostic::ExpectedArrayOrSlicePat { found: ty, .. }
+ | InferenceDiagnostic::UnresolvedField { receiver: ty, .. }
+ | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic
+ && ty.as_ref().references_non_lt_error()
+ {
+ false
+ } else {
+ true
+ }
+ });
+ diagnostics.shrink_to_fit();
+
+ (diagnostics, has_errors)
+ }
+ }
+
+ struct DiagnoseInferVars<'a, 'db> {
+ ctx: &'a mut WriteBackCtxt<'db>,
+ top_term: Term<'db>,
+ }
+
+ impl<'db> DiagnoseInferVars<'_, 'db> {
+ const TYPE_FLAGS: TypeFlags = TypeFlags::HAS_INFER.union(TypeFlags::HAS_NON_REGION_ERROR);
+
+ fn err_on_span(&mut self, span: Span) {
+ if !self.ctx.spans_emitted_type_must_be_known_for.insert(span) {
+ // Suppress duplicate diagnostics.
+ return;
+ }
+
+ if span.is_dummy() {
+ return;
+ }
+
+ // We have to be careful not to insert infer vars here, as we won't resolve this new diagnostic.
+ let top_term = self.top_term.fold_with(&mut ReplaceInferWithError::new(self.cx()));
+ self.ctx.diagnostics.push(InferenceDiagnostic::TypeMustBeKnown {
+ at_point: span,
+ top_term: Some(GenericArg::from(top_term).store()),
+ });
+ }
+ }
+
+ impl<'db> TypeFolder<DbInterner<'db>> for DiagnoseInferVars<'_, 'db> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.ctx.table.interner()
+ }
+
+ fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
+ if !t.has_type_flags(Self::TYPE_FLAGS) {
+ return t;
+ }
+
+ match t.kind() {
+ TyKind::Error(_) => {
+ self.ctx.has_errors = true;
+ t
+ }
+ TyKind::Infer(infer_ty) => match infer_ty {
+ InferTy::TyVar(vid) => {
+ self.err_on_span(self.ctx.table.infer_ctxt.type_var_span(vid));
+ self.ctx.has_errors = true;
+ self.ctx.types.types.error
+ }
+ InferTy::IntVar(_) => {
+ never!("fallback should have resolved all int vars");
+ self.ctx.types.types.i32
+ }
+ InferTy::FloatVar(_) => {
+ never!("fallback should have resolved all float vars");
+ self.ctx.types.types.f64
+ }
+ InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => {
+ never!("should not have fresh infer vars outside of caching");
+ self.ctx.has_errors = true;
+ self.ctx.types.types.error
+ }
+ },
+ _ => t.super_fold_with(self),
+ }
+ }
+
+ fn fold_const(&mut self, c: Const<'db>) -> Const<'db> {
+ if !c.has_type_flags(Self::TYPE_FLAGS) {
+ return c;
+ }
+
+ match c.kind() {
+ ConstKind::Error(_) => {
+ self.ctx.has_errors = true;
+ c
+ }
+ ConstKind::Infer(infer_ct) => match infer_ct {
+ InferConst::Var(vid) => {
+ if let Some(span) = self.ctx.table.infer_ctxt.const_var_span(vid) {
+ self.err_on_span(span);
+ }
+ self.ctx.has_errors = true;
+ self.ctx.types.consts.error
+ }
+ InferConst::Fresh(_) => {
+ never!("should not have fresh infer vars outside of caching");
+ self.ctx.has_errors = true;
+ self.ctx.types.consts.error
+ }
+ },
+ _ => c.super_fold_with(self),
+ }
+ }
+
+ fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> {
+ if !p.has_type_flags(Self::TYPE_FLAGS) {
+ return p;
+ }
+ p.super_fold_with(self)
+ }
+
+ fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
+ if r.is_var() {
+ // For now, we don't error on regions.
+ self.ctx.types.regions.error
+ } else {
+ r
+ }
+ }
+ }
+
pub(super) struct Resolver<'a, 'db> {
- ctx: &'a mut InferenceTable<'db>,
+ ctx: &'a mut WriteBackCtxt<'db>,
/// Whether we should normalize, disabled when resolving predicates.
should_normalize: bool,
nested_goals: &'a mut Vec<Goal<'db, Predicate<'db>>>,
@@ -629,7 +714,7 @@ mod resolve_completely {
impl<'a, 'db> Resolver<'a, 'db> {
pub(super) fn new(
- ctx: &'a mut InferenceTable<'db>,
+ ctx: &'a mut WriteBackCtxt<'db>,
should_normalize: bool,
nested_goals: &'a mut Vec<Goal<'db, Predicate<'db>>>,
) -> Resolver<'a, 'db> {
@@ -645,8 +730,9 @@ mod resolve_completely {
T: Into<Term<'db>> + TypeSuperFoldable<DbInterner<'db>> + Copy,
{
let value = if self.should_normalize {
- let cause = ObligationCause::new();
- let at = self.ctx.at(&cause);
+ // FIXME: This should not use a dummy span.
+ let cause = ObligationCause::new(Span::Dummy);
+ let at = self.ctx.table.at(&cause);
let universes = vec![None; outer_exclusive_binder(value).as_usize()];
match deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
at, value, universes,
@@ -655,8 +741,8 @@ mod resolve_completely {
self.nested_goals.extend(goals);
value
}
- Err(_errors) => {
- // FIXME: Report the error.
+ Err(errors) => {
+ self.ctx.table.trait_errors.extend(errors);
value
}
}
@@ -664,17 +750,17 @@ mod resolve_completely {
value
};
- value.fold_with(&mut ReplaceInferWithError::new(self.ctx.interner()))
+ value.fold_with(&mut DiagnoseInferVars { ctx: self.ctx, top_term: value.into() })
}
}
- impl<'cx, 'db> TypeFolder<DbInterner<'db>> for Resolver<'cx, 'db> {
+ impl<'db> TypeFolder<DbInterner<'db>> for Resolver<'_, 'db> {
fn cx(&self) -> DbInterner<'db> {
- self.ctx.interner()
+ self.ctx.table.interner()
}
fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
- if r.is_var() { Region::error(self.ctx.interner()) } else { r }
+ if r.is_var() { self.ctx.types.regions.error } else { r }
}
fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs
index 74d66123ea..0070d14f37 100644
--- a/crates/hir-ty/src/inhabitedness.rs
+++ b/crates/hir-ty/src/inhabitedness.rs
@@ -5,10 +5,7 @@ use hir_def::{
AdtId, EnumVariantId, ModuleId, VariantId, signatures::VariantFields, visibility::Visibility,
};
use rustc_hash::FxHashSet;
-use rustc_type_ir::{
- TypeSuperVisitable, TypeVisitable, TypeVisitor,
- inherent::{AdtDef, IntoKind},
-};
+use rustc_type_ir::{TypeSuperVisitable, TypeVisitable, TypeVisitor, inherent::IntoKind};
use crate::{
consteval::try_const_usize,
@@ -85,7 +82,7 @@ impl<'db> TypeVisitor<DbInterner<'db>> for UninhabitedFrom<'_, 'db> {
}
let r = match ty.kind() {
- TyKind::Adt(adt, subst) => self.visit_adt(adt.def_id().0, subst),
+ TyKind::Adt(adt, subst) => self.visit_adt(adt.def_id(), subst),
TyKind::Never => BREAK_VISIBLY_UNINHABITED,
TyKind::Tuple(..) => ty.super_visit_with(self),
TyKind::Array(item_ty, len) => match try_const_usize(self.infcx.interner.db, len) {
@@ -172,7 +169,7 @@ impl<'a, 'db> UninhabitedFrom<'a, 'db> {
subst: GenericArgs<'db>,
) -> ControlFlow<VisiblyUninhabited> {
if vis.is_none_or(|it| it.is_visible_from(self.db(), self.target_mod)) {
- let ty = ty.instantiate(self.interner(), subst);
+ let ty = ty.instantiate(self.interner(), subst).skip_norm_wip();
ty.visit_with(self)
} else {
CONTINUE_OPAQUELY_INHABITED
diff --git a/crates/hir-ty/src/lang_items.rs b/crates/hir-ty/src/lang_items.rs
index ae53276f56..19d2d29c9e 100644
--- a/crates/hir-ty/src/lang_items.rs
+++ b/crates/hir-ty/src/lang_items.rs
@@ -1,64 +1,52 @@
//! Functions to detect special lang items
-use hir_def::{
- AdtId, TraitId,
- lang_item::LangItems,
- signatures::{StructFlags, StructSignature},
-};
-use intern::{Symbol, sym};
-
-use crate::db::HirDatabase;
-
-pub fn is_box(db: &dyn HirDatabase, adt: AdtId) -> bool {
- let AdtId::StructId(id) = adt else { return false };
- StructSignature::of(db, id).flags.contains(StructFlags::IS_BOX)
-}
+use hir_def::{FunctionId, TraitId, lang_item::LangItems};
pub fn lang_items_for_bin_op(
lang_items: &LangItems,
op: syntax::ast::BinaryOp,
-) -> Option<(Symbol, Option<TraitId>)> {
+) -> Option<(Option<FunctionId>, Option<TraitId>)> {
use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering};
Some(match op {
BinaryOp::LogicOp(_) => return None,
BinaryOp::ArithOp(aop) => match aop {
- ArithOp::Add => (sym::add, lang_items.Add),
- ArithOp::Mul => (sym::mul, lang_items.Mul),
- ArithOp::Sub => (sym::sub, lang_items.Sub),
- ArithOp::Div => (sym::div, lang_items.Div),
- ArithOp::Rem => (sym::rem, lang_items.Rem),
- ArithOp::Shl => (sym::shl, lang_items.Shl),
- ArithOp::Shr => (sym::shr, lang_items.Shr),
- ArithOp::BitXor => (sym::bitxor, lang_items.BitXor),
- ArithOp::BitOr => (sym::bitor, lang_items.BitOr),
- ArithOp::BitAnd => (sym::bitand, lang_items.BitAnd),
+ ArithOp::Add => (lang_items.Add_add, lang_items.Add),
+ ArithOp::Mul => (lang_items.Mul_mul, lang_items.Mul),
+ ArithOp::Sub => (lang_items.Sub_sub, lang_items.Sub),
+ ArithOp::Div => (lang_items.Div_div, lang_items.Div),
+ ArithOp::Rem => (lang_items.Rem_rem, lang_items.Rem),
+ ArithOp::Shl => (lang_items.Shl_shl, lang_items.Shl),
+ ArithOp::Shr => (lang_items.Shr_shr, lang_items.Shr),
+ ArithOp::BitXor => (lang_items.BitXor_bitxor, lang_items.BitXor),
+ ArithOp::BitOr => (lang_items.BitOr_bitor, lang_items.BitOr),
+ ArithOp::BitAnd => (lang_items.BitAnd_bitand, lang_items.BitAnd),
},
BinaryOp::Assignment { op: Some(aop) } => match aop {
- ArithOp::Add => (sym::add_assign, lang_items.AddAssign),
- ArithOp::Mul => (sym::mul_assign, lang_items.MulAssign),
- ArithOp::Sub => (sym::sub_assign, lang_items.SubAssign),
- ArithOp::Div => (sym::div_assign, lang_items.DivAssign),
- ArithOp::Rem => (sym::rem_assign, lang_items.RemAssign),
- ArithOp::Shl => (sym::shl_assign, lang_items.ShlAssign),
- ArithOp::Shr => (sym::shr_assign, lang_items.ShrAssign),
- ArithOp::BitXor => (sym::bitxor_assign, lang_items.BitXorAssign),
- ArithOp::BitOr => (sym::bitor_assign, lang_items.BitOrAssign),
- ArithOp::BitAnd => (sym::bitand_assign, lang_items.BitAndAssign),
+ ArithOp::Add => (lang_items.AddAssign_add_assign, lang_items.AddAssign),
+ ArithOp::Mul => (lang_items.MulAssign_mul_assign, lang_items.MulAssign),
+ ArithOp::Sub => (lang_items.SubAssign_sub_assign, lang_items.SubAssign),
+ ArithOp::Div => (lang_items.DivAssign_div_assign, lang_items.DivAssign),
+ ArithOp::Rem => (lang_items.RemAssign_rem_assign, lang_items.RemAssign),
+ ArithOp::Shl => (lang_items.ShlAssign_shl_assign, lang_items.ShlAssign),
+ ArithOp::Shr => (lang_items.ShrAssign_shr_assign, lang_items.ShrAssign),
+ ArithOp::BitXor => (lang_items.BitXorAssign_bitxor_assign, lang_items.BitXorAssign),
+ ArithOp::BitOr => (lang_items.BitOrAssign_bitor_assign, lang_items.BitOrAssign),
+ ArithOp::BitAnd => (lang_items.BitAndAssign_bitand_assign, lang_items.BitAndAssign),
},
BinaryOp::CmpOp(cop) => match cop {
- CmpOp::Eq { negated: false } => (sym::eq, lang_items.PartialEq),
- CmpOp::Eq { negated: true } => (sym::ne, lang_items.PartialEq),
+ CmpOp::Eq { negated: false } => (lang_items.PartialEq_eq, lang_items.PartialEq),
+ CmpOp::Eq { negated: true } => (lang_items.PartialEq_ne, lang_items.PartialEq),
CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
- (sym::le, lang_items.PartialOrd)
+ (lang_items.PartialOrd_le, lang_items.PartialOrd)
}
CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
- (sym::lt, lang_items.PartialOrd)
+ (lang_items.PartialOrd_lt, lang_items.PartialOrd)
}
CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
- (sym::ge, lang_items.PartialOrd)
+ (lang_items.PartialOrd_ge, lang_items.PartialOrd)
}
CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
- (sym::gt, lang_items.PartialOrd)
+ (lang_items.PartialOrd_gt, lang_items.PartialOrd)
}
},
BinaryOp::Assignment { op: None } => return None,
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
index 798c62c192..3e569076ad 100644
--- a/crates/hir-ty/src/layout.rs
+++ b/crates/hir-ty/src/layout.rs
@@ -142,10 +142,10 @@ fn layout_of_simd_ty<'db>(
// where T is a primitive scalar (integer/float/pointer).
let fields = db.field_types(id.into());
let mut fields = fields.iter();
- let Some(TyKind::Array(e_ty, e_len)) = fields
- .next()
- .filter(|_| fields.next().is_none())
- .map(|f| (*f.1).get().instantiate(DbInterner::new_no_crate(db), args).kind())
+ let Some(TyKind::Array(e_ty, e_len)) =
+ fields.next().filter(|_| fields.next().is_none()).map(|f| {
+ (*f.1).get().instantiate(DbInterner::new_no_crate(db), args).skip_norm_wip().kind()
+ })
else {
return Err(LayoutError::InvalidSimdType);
};
@@ -167,7 +167,7 @@ pub fn layout_of_ty_query(
let Ok(target) = db.target_data_layout(krate) else {
return Err(LayoutError::TargetLayoutNotAvailable);
};
- let dl = &*target;
+ let dl = target;
let cx = LayoutCx::new(dl);
let infer_ctxt = interner.infer_ctxt().build(TypingMode::PostAnalysis);
let cause = ObligationCause::dummy();
@@ -177,7 +177,7 @@ pub fn layout_of_ty_query(
.unwrap_or(ty.as_ref());
let result = match ty.kind() {
TyKind::Adt(def, args) => {
- match def.inner().id {
+ match def.def_id() {
hir_def::AdtId::StructId(s) => {
let repr = AttrFlags::repr(db, s.into()).unwrap_or_default();
if repr.simd() {
@@ -187,13 +187,13 @@ pub fn layout_of_ty_query(
repr.packed(),
&args,
trait_env.as_ref(),
- &target,
+ target,
);
}
}
_ => {}
}
- return db.layout_of_adt(def.inner().id, args.store(), trait_env);
+ return db.layout_of_adt(def.def_id(), args.store(), trait_env);
}
TyKind::Bool => Layout::scalar(
dl,
@@ -374,7 +374,7 @@ pub(crate) fn layout_of_ty_cycle_result(
fn struct_tail_erasing_lifetimes<'a>(db: &'a dyn HirDatabase, pointee: Ty<'a>) -> Ty<'a> {
match pointee.kind() {
TyKind::Adt(def, args) => {
- let struct_id = match def.inner().id {
+ let struct_id = match def.def_id() {
AdtId::StructId(id) => id,
_ => return pointee,
};
@@ -405,7 +405,7 @@ fn field_ty<'a>(
fd: LocalFieldId,
args: GenericArgs<'a>,
) -> Ty<'a> {
- db.field_types(def)[fd].get().instantiate(DbInterner::new_no_crate(db), args)
+ db.field_types(def)[fd].get().instantiate(DbInterner::new_no_crate(db), args).skip_norm_wip()
}
fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar {
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs
index 6090ddfd45..b7e1697059 100644
--- a/crates/hir-ty/src/layout/adt.rs
+++ b/crates/hir-ty/src/layout/adt.rs
@@ -1,6 +1,6 @@
//! Compute the binary representation of structs, unions and enums
-use std::{cmp, ops::Bound};
+use std::cmp;
use hir_def::{
AdtId, VariantId,
@@ -29,7 +29,7 @@ pub fn layout_of_adt_query(
let Ok(target) = db.target_data_layout(krate) else {
return Err(LayoutError::TargetLayoutNotAvailable);
};
- let dl = &*target;
+ let dl = target;
let cx = LayoutCx::new(dl);
let handle_variant = |def: VariantId, var: &VariantFields| {
var.fields()
@@ -79,7 +79,6 @@ pub fn layout_of_adt_query(
&variants,
matches!(def, AdtId::EnumId(..)),
is_special_no_niche,
- layout_scalar_valid_range(db, def),
|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 };
@@ -107,15 +106,6 @@ pub(crate) fn layout_of_adt_cycle_result(
Err(LayoutError::RecursiveTypeWithoutIndirection)
}
-fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) {
- let range = AttrFlags::rustc_layout_scalar_valid_range(db, def);
- let get = |value| match value {
- Some(it) => Bound::Included(it),
- None => Bound::Unbounded,
- };
- (get(range.start), get(range.end))
-}
-
/// 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
diff --git a/crates/hir-ty/src/layout/target.rs b/crates/hir-ty/src/layout/target.rs
index 1752b56b0f..26fa73e76b 100644
--- a/crates/hir-ty/src/layout/target.rs
+++ b/crates/hir-ty/src/layout/target.rs
@@ -3,17 +3,17 @@
use base_db::{Crate, target::TargetLoadError};
use hir_def::layout::TargetDataLayout;
use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutError};
-use triomphe::Arc;
use crate::db::HirDatabase;
+#[salsa_macros::tracked(returns(ref))]
pub fn target_data_layout_query(
db: &dyn HirDatabase,
krate: Crate,
-) -> Result<Arc<TargetDataLayout>, TargetLoadError> {
+) -> Result<TargetDataLayout, TargetLoadError> {
match &krate.workspace_data(db).target {
Ok(target) => match TargetDataLayout::parse_from_llvm_datalayout_string(&target.data_layout, AddressSpace::ZERO) {
- Ok(it) => Ok(Arc::new(it)),
+ Ok(it) => Ok(it),
Err(e) => {
Err(match e {
TargetDataLayoutError::InvalidAddressSpace { addr_space, cause, err } => {
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
index 484ecebba5..bc18f05790 100644
--- a/crates/hir-ty/src/layout/tests.rs
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -90,7 +90,7 @@ fn eval_goal(
adt_id,
GenericArgs::identity_for_item(interner, adt_id.into()),
),
- Either::Right(ty_id) => db.ty(ty_id.into()).instantiate_identity(),
+ Either::Right(ty_id) => db.ty(ty_id.into()).instantiate_identity().skip_norm_wip(),
};
let param_env = db.trait_environment(
match adt_or_type_alias_id {
@@ -529,6 +529,7 @@ fn tuple_ptr_with_dst_tail() {
}
#[test]
+#[ignore = "FIXME: We need to have proper pattern types"]
fn non_zero_and_non_null() {
size_and_align! {
minicore: non_zero, non_null, option;
@@ -565,8 +566,6 @@ fn const_eval_simple() {
}
#[test]
-// FIXME
-#[should_panic]
fn const_eval_complex() {
size_and_align! {
struct Goal([i32; 2 + 2]);
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index d004b5e3ef..91e3b85aa1 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -26,6 +26,7 @@ extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver;
extern crate self as hir_ty;
pub mod builtin_derive;
+mod generics;
mod infer;
mod inhabitedness;
mod lower;
@@ -44,12 +45,12 @@ pub mod diagnostics;
pub mod display;
pub mod drop;
pub mod dyn_compatibility;
-pub mod generics;
pub mod lang_items;
pub mod layout;
pub mod method_resolution;
pub mod mir;
pub mod primitive;
+pub mod solver_errors;
pub mod traits;
pub mod upvars;
@@ -61,42 +62,55 @@ mod tests;
use std::{hash::Hash, ops::ControlFlow};
use hir_def::{
- CallableDefId, ExpressionStoreOwnerId, GenericDefId, TypeAliasId, TypeOrConstParamId,
- TypeParamId, resolver::TypeNs, type_ref::Rawness,
+ CallableDefId, ConstId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, FunctionId,
+ GenericDefId, HasModule, LifetimeParamId, ModuleId, StaticId, TypeAliasId, TypeOrConstParamId,
+ TypeParamId,
+ db::DefDatabase,
+ expr_store::{Body, ExpressionStore},
+ hir::{BindingId, ExprId, ExprOrPatId, PatId},
+ resolver::{HasResolver, Resolver, TypeNs},
+ type_ref::{Rawness, TypeRefId},
};
use hir_expand::name::Name;
use indexmap::{IndexMap, map::Entry};
-use intern::{Symbol, sym};
use macros::GenericTypeVisitable;
use mir::{MirEvalError, VTableMap};
+use rustc_abi::ExternAbi;
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use rustc_type_ir::{
- BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt, UpcastFrom,
+ BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt,
inherent::{IntoKind, Ty as _},
};
+use stdx::impl_from;
use syntax::ast::{ConstArg, make};
use traits::FnTrait;
use crate::{
- db::HirDatabase,
- display::{DisplayTarget, HirDisplay},
- infer::unify::InferenceTable,
+ db::{AnonConstId, HirDatabase},
+ display::HirDisplay,
lower::SupertraitsInfo,
next_solver::{
AliasTy, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, Canonical,
- CanonicalVarKind, CanonicalVarKinds, ClauseKind, Const, ConstKind, DbInterner, FnSig,
- GenericArgs, PolyFnSig, Predicate, Region, RegionKind, TraitRef, Ty, TyKind, Tys, abi,
+ CanonicalVarKind, CanonicalVarKinds, ClauseKind, Const, ConstKind, DbInterner, GenericArgs,
+ PolyFnSig, Region, RegionKind, TraitRef, Ty, TyKind, TypingMode,
+ abi::Safety,
+ infer::{
+ DbInternerInferExt,
+ traits::{Obligation, ObligationCause},
+ },
+ obligation_ctxt::ObligationCtxt,
},
};
pub use autoderef::autoderef;
pub use infer::{
- Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult,
+ Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, InferenceDiagnostic, InferenceResult,
InferenceTyDiagnosticSource, OverloadedDeref, PointerCast, cast::CastError, could_coerce,
could_unify, could_unify_deeply, infer_query_with_inspect,
};
pub use lower::{
- GenericPredicates, ImplTraits, LifetimeElisionKind, TyDefId, TyLoweringContext, ValueTyDefId,
+ GenericDefaults, GenericDefaultsRef, GenericPredicates, ImplTraits, LifetimeElisionKind,
+ TyDefId, TyLoweringContext, TyLoweringInferVarsCtx, TyLoweringResult, ValueTyDefId,
diagnostics::*,
};
pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db};
@@ -201,139 +215,12 @@ impl<'db> MemoryMap<'db> {
}
/// Return an index of a parameter in the generic type parameter list by it's id.
-pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize> {
+pub fn type_or_const_param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> u32 {
generics::generics(db, id.parent).type_or_const_param_idx(id)
}
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub enum FnAbi {
- Aapcs,
- AapcsUnwind,
- AvrInterrupt,
- AvrNonBlockingInterrupt,
- C,
- CCmseNonsecureCall,
- CCmseNonsecureEntry,
- CDecl,
- CDeclUnwind,
- CUnwind,
- Efiapi,
- Fastcall,
- FastcallUnwind,
- Msp430Interrupt,
- PtxKernel,
- RiscvInterruptM,
- RiscvInterruptS,
- Rust,
- RustCall,
- RustCold,
- RustIntrinsic,
- Stdcall,
- StdcallUnwind,
- System,
- SystemUnwind,
- Sysv64,
- Sysv64Unwind,
- Thiscall,
- ThiscallUnwind,
- Unadjusted,
- Vectorcall,
- VectorcallUnwind,
- Wasm,
- Win64,
- Win64Unwind,
- X86Interrupt,
- RustPreserveNone,
- Unknown,
-}
-
-impl FnAbi {
- #[rustfmt::skip]
- pub fn from_symbol(s: &Symbol) -> FnAbi {
- match s {
- s if *s == sym::aapcs_dash_unwind => FnAbi::AapcsUnwind,
- s if *s == sym::aapcs => FnAbi::Aapcs,
- s if *s == sym::avr_dash_interrupt => FnAbi::AvrInterrupt,
- s if *s == sym::avr_dash_non_dash_blocking_dash_interrupt => FnAbi::AvrNonBlockingInterrupt,
- s if *s == sym::C_dash_cmse_dash_nonsecure_dash_call => FnAbi::CCmseNonsecureCall,
- s if *s == sym::C_dash_cmse_dash_nonsecure_dash_entry => FnAbi::CCmseNonsecureEntry,
- s if *s == sym::C_dash_unwind => FnAbi::CUnwind,
- s if *s == sym::C => FnAbi::C,
- s if *s == sym::cdecl_dash_unwind => FnAbi::CDeclUnwind,
- s if *s == sym::cdecl => FnAbi::CDecl,
- s if *s == sym::efiapi => FnAbi::Efiapi,
- s if *s == sym::fastcall_dash_unwind => FnAbi::FastcallUnwind,
- s if *s == sym::fastcall => FnAbi::Fastcall,
- s if *s == sym::msp430_dash_interrupt => FnAbi::Msp430Interrupt,
- s if *s == sym::ptx_dash_kernel => FnAbi::PtxKernel,
- s if *s == sym::riscv_dash_interrupt_dash_m => FnAbi::RiscvInterruptM,
- s if *s == sym::riscv_dash_interrupt_dash_s => FnAbi::RiscvInterruptS,
- s if *s == sym::rust_dash_call => FnAbi::RustCall,
- s if *s == sym::rust_dash_cold => FnAbi::RustCold,
- s if *s == sym::rust_dash_preserve_dash_none => FnAbi::RustPreserveNone,
- s if *s == sym::rust_dash_intrinsic => FnAbi::RustIntrinsic,
- s if *s == sym::Rust => FnAbi::Rust,
- s if *s == sym::stdcall_dash_unwind => FnAbi::StdcallUnwind,
- s if *s == sym::stdcall => FnAbi::Stdcall,
- s if *s == sym::system_dash_unwind => FnAbi::SystemUnwind,
- s if *s == sym::system => FnAbi::System,
- s if *s == sym::sysv64_dash_unwind => FnAbi::Sysv64Unwind,
- s if *s == sym::sysv64 => FnAbi::Sysv64,
- s if *s == sym::thiscall_dash_unwind => FnAbi::ThiscallUnwind,
- s if *s == sym::thiscall => FnAbi::Thiscall,
- s if *s == sym::unadjusted => FnAbi::Unadjusted,
- s if *s == sym::vectorcall_dash_unwind => FnAbi::VectorcallUnwind,
- s if *s == sym::vectorcall => FnAbi::Vectorcall,
- s if *s == sym::wasm => FnAbi::Wasm,
- s if *s == sym::win64_dash_unwind => FnAbi::Win64Unwind,
- s if *s == sym::win64 => FnAbi::Win64,
- s if *s == sym::x86_dash_interrupt => FnAbi::X86Interrupt,
- _ => FnAbi::Unknown,
- }
- }
-
- pub fn as_str(self) -> &'static str {
- match self {
- FnAbi::Aapcs => "aapcs",
- FnAbi::AapcsUnwind => "aapcs-unwind",
- FnAbi::AvrInterrupt => "avr-interrupt",
- FnAbi::AvrNonBlockingInterrupt => "avr-non-blocking-interrupt",
- FnAbi::C => "C",
- FnAbi::CCmseNonsecureCall => "C-cmse-nonsecure-call",
- FnAbi::CCmseNonsecureEntry => "C-cmse-nonsecure-entry",
- FnAbi::CDecl => "C-decl",
- FnAbi::CDeclUnwind => "cdecl-unwind",
- FnAbi::CUnwind => "C-unwind",
- FnAbi::Efiapi => "efiapi",
- FnAbi::Fastcall => "fastcall",
- FnAbi::FastcallUnwind => "fastcall-unwind",
- FnAbi::Msp430Interrupt => "msp430-interrupt",
- FnAbi::PtxKernel => "ptx-kernel",
- FnAbi::RiscvInterruptM => "riscv-interrupt-m",
- FnAbi::RiscvInterruptS => "riscv-interrupt-s",
- FnAbi::Rust => "Rust",
- FnAbi::RustCall => "rust-call",
- FnAbi::RustCold => "rust-cold",
- FnAbi::RustPreserveNone => "rust-preserve-none",
- FnAbi::RustIntrinsic => "rust-intrinsic",
- FnAbi::Stdcall => "stdcall",
- FnAbi::StdcallUnwind => "stdcall-unwind",
- FnAbi::System => "system",
- FnAbi::SystemUnwind => "system-unwind",
- FnAbi::Sysv64 => "sysv64",
- FnAbi::Sysv64Unwind => "sysv64-unwind",
- FnAbi::Thiscall => "thiscall",
- FnAbi::ThiscallUnwind => "thiscall-unwind",
- FnAbi::Unadjusted => "unadjusted",
- FnAbi::Vectorcall => "vectorcall",
- FnAbi::VectorcallUnwind => "vectorcall-unwind",
- FnAbi::Wasm => "wasm",
- FnAbi::Win64 => "win64",
- FnAbi::Win64Unwind => "win64-unwind",
- FnAbi::X86Interrupt => "x86-interrupt",
- FnAbi::Unknown => "unknown-abi",
- }
- }
+pub fn lifetime_param_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> u32 {
+ generics::generics(db, id.parent).lifetime_param_idx(id)
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
@@ -495,7 +382,7 @@ pub fn associated_type_shorthand_candidates(
};
let mut dedup_map = FxHashSet::default();
- let param_ty = Ty::new_param(interner, param, param_idx(db, param.into()).unwrap() as u32);
+ let param_ty = Ty::new_param(interner, param, type_or_const_param_idx(db, param.into()));
// We use the ParamEnv and not the predicates because the ParamEnv elaborates bounds.
let param_env = db.trait_environment(ExpressionStoreOwnerId::from(def));
for clause in param_env.clauses {
@@ -525,68 +412,61 @@ pub fn associated_type_shorthand_candidates(
/// To be used from `hir` only.
pub fn callable_sig_from_fn_trait<'db>(
self_ty: Ty<'db>,
- trait_env: ParamEnvAndCrate<'db>,
+ param_env: ParamEnvAndCrate<'db>,
db: &'db dyn HirDatabase,
) -> Option<(FnTrait, PolyFnSig<'db>)> {
- let mut table = InferenceTable::new(db, trait_env.param_env, trait_env.krate, None);
- let lang_items = table.interner().lang_items();
-
- let fn_once_trait = FnTrait::FnOnce.get_id(lang_items)?;
- let output_assoc_type = fn_once_trait
- .trait_items(db)
- .associated_type_by_name(&Name::new_symbol_root(sym::Output))?;
-
- // Register two obligations:
- // - Self: FnOnce<?args_ty>
- // - <Self as FnOnce<?args_ty>>::Output == ?ret_ty
- let args_ty = table.next_ty_var();
- let args = GenericArgs::new_from_slice(&[self_ty.into(), args_ty.into()]);
- let trait_ref = TraitRef::new_from_args(table.interner(), fn_once_trait.into(), args);
- let projection = Ty::new_alias(
- table.interner(),
- AliasTy::new_from_args(
- table.interner(),
- rustc_type_ir::Projection { def_id: output_assoc_type.into() },
- args,
- ),
- );
+ let ParamEnvAndCrate { param_env, krate } = param_env;
+ let interner = DbInterner::new_with(db, krate);
+ let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
+ let lang_items = interner.lang_items();
+ let cause = ObligationCause::dummy();
+
+ let impls_trait = |trait_: FnTrait| {
+ let mut ocx = ObligationCtxt::new(&infcx);
+ let tupled_args = infcx.next_ty_var(Span::Dummy);
+ let args = GenericArgs::new_from_slice(&[self_ty.into(), tupled_args.into()]);
+ let trait_id = trait_.get_id(lang_items)?;
+ let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args);
+ let obligation = Obligation::new(interner, cause, param_env, trait_ref);
+ ocx.register_obligation(obligation);
+ if !ocx.try_evaluate_obligations().is_empty() {
+ return None;
+ }
+ let tupled_args =
+ infcx.resolve_vars_if_possible(tupled_args).replace_infer_with_error(interner);
+ if tupled_args.is_tuple() { Some(tupled_args) } else { None }
+ };
- let pred = Predicate::upcast_from(trait_ref, table.interner());
- if !table.try_obligation(pred).no_solution() {
- table.register_obligation(pred);
- let return_ty = table.normalize_alias_ty(projection);
- for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
- let fn_x_trait = fn_x.get_id(lang_items)?;
- let trait_ref = TraitRef::new_from_args(table.interner(), fn_x_trait.into(), args);
- if !table
- .try_obligation(Predicate::upcast_from(trait_ref, table.interner()))
- .no_solution()
- {
- let ret_ty = table.resolve_completely(return_ty);
- let args_ty = table.resolve_completely(args_ty);
- let TyKind::Tuple(params) = args_ty.kind() else {
- return None;
- };
- let inputs_and_output = Tys::new_from_iter(
- table.interner(),
- params.iter().chain(std::iter::once(ret_ty)),
- );
-
- return Some((
- fn_x,
- Binder::dummy(FnSig {
- inputs_and_output,
- c_variadic: false,
- safety: abi::Safety::Safe,
- abi: FnAbi::RustCall,
- }),
- ));
+ let (trait_, args) = 'find_trait: {
+ for trait_ in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] {
+ if let Some(args) = impls_trait(trait_) {
+ break 'find_trait (trait_, args);
}
}
- unreachable!("It should at least implement FnOnce at this point");
- } else {
- None
- }
+ return None;
+ };
+
+ let output_assoc_type = lang_items.FnOnceOutput?;
+ let output_projection = Ty::new_alias(
+ interner,
+ AliasTy::new(
+ interner,
+ rustc_type_ir::Projection { def_id: output_assoc_type.into() },
+ [self_ty, args],
+ ),
+ );
+ let mut ocx = ObligationCtxt::new(&infcx);
+ let ret = ocx.structurally_normalize_ty(&cause, param_env, output_projection).ok()?;
+ let ret = ret.replace_infer_with_error(interner);
+
+ let sig = Binder::dummy(interner.mk_fn_sig(
+ args.tuple_fields(),
+ ret,
+ false,
+ Safety::Safe,
+ ExternAbi::Rust,
+ ));
+ Some((trait_, sig))
}
struct ParamCollector {
@@ -623,58 +503,128 @@ where
Vec::from_iter(collector.params)
}
-struct TypeInferenceVarCollector<'db> {
- type_inference_vars: Vec<Ty<'db>>,
+pub fn known_const_to_ast<'db>(
+ konst: Const<'db>,
+ db: &'db dyn HirDatabase,
+ target_module: ModuleId,
+) -> Option<ConstArg> {
+ Some(make::expr_const_value(
+ &konst.display_source_code(db, target_module, true).unwrap_or_else(|_| "_".to_owned()),
+ ))
}
-impl<'db> rustc_type_ir::TypeVisitor<DbInterner<'db>> for TypeInferenceVarCollector<'db> {
- type Result = ();
+/// A `Span` represents some location in lowered code - a type, expression or pattern.
+///
+/// It has no meaning outside its body therefore it should not exit the pass it was created in
+/// (e.g. inference). It is usually associated with a solver obligation or an infer var, which
+/// should also not cross the pass they were created in.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Span {
+ ExprId(ExprId),
+ PatId(PatId),
+ BindingId(BindingId),
+ TypeRefId(TypeRefId),
+ /// An unimportant location. Errors on this will be suppressed.
+ Dummy,
+}
+impl_from!(ExprId, PatId, BindingId, TypeRefId for Span);
- fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result {
- use crate::rustc_type_ir::Flags;
- if ty.is_ty_var() {
- self.type_inference_vars.push(ty);
- } else if ty.flags().intersects(rustc_type_ir::TypeFlags::HAS_TY_INFER) {
- ty.super_visit_with(self);
- } else {
- // Fast path: don't visit inner types (e.g. generic arguments) when `flags` indicate
- // that there are no placeholders.
+impl From<ExprOrPatId> for Span {
+ fn from(value: ExprOrPatId) -> Self {
+ match value {
+ ExprOrPatId::ExprId(idx) => idx.into(),
+ ExprOrPatId::PatId(idx) => idx.into(),
}
}
}
-pub fn collect_type_inference_vars<'db, T>(value: &T) -> Vec<Ty<'db>>
-where
- T: ?Sized + rustc_type_ir::TypeVisitable<DbInterner<'db>>,
-{
- let mut collector = TypeInferenceVarCollector { type_inference_vars: vec![] };
- value.visit_with(&mut collector);
- collector.type_inference_vars
+impl Span {
+ pub(crate) fn pick_best(a: Span, b: Span) -> Span {
+ // We prefer dummy spans to minimize the risk of false errors.
+ if b.is_dummy() { b } else { a }
+ }
+
+ #[inline]
+ pub fn is_dummy(&self) -> bool {
+ matches!(self, Self::Dummy)
+ }
}
-pub fn known_const_to_ast<'db>(
- konst: Const<'db>,
- db: &'db dyn HirDatabase,
- display_target: DisplayTarget,
-) -> Option<ConstArg> {
- Some(make::expr_const_value(konst.display(db, display_target).to_string().as_str()))
+/// A [`DefWithBodyId`], or an anon const.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Supertype)]
+pub enum InferBodyId {
+ DefWithBodyId(DefWithBodyId),
+ AnonConstId(AnonConstId),
+}
+impl_from!(DefWithBodyId(FunctionId, ConstId, StaticId), AnonConstId for InferBodyId);
+impl From<EnumVariantId> for InferBodyId {
+ fn from(id: EnumVariantId) -> Self {
+ InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(id))
+ }
}
-#[derive(Debug, Copy, Clone)]
-pub(crate) enum DeclOrigin {
- LetExpr,
- /// from `let x = ..`
- LocalDecl {
- has_else: bool,
- },
+impl HasModule for InferBodyId {
+ fn module(&self, db: &dyn DefDatabase) -> ModuleId {
+ match self {
+ InferBodyId::DefWithBodyId(id) => id.module(db),
+ InferBodyId::AnonConstId(id) => id.module(db),
+ }
+ }
}
-/// Provides context for checking patterns in declarations. More specifically this
-/// allows us to infer array types if the pattern is irrefutable and allows us to infer
-/// the size of the array. See issue rust-lang/rust#76342.
-#[derive(Debug, Copy, Clone)]
-pub(crate) struct DeclContext {
- pub(crate) origin: DeclOrigin,
+impl HasResolver for InferBodyId {
+ fn resolver(self, db: &dyn DefDatabase) -> Resolver<'_> {
+ match self {
+ InferBodyId::DefWithBodyId(id) => id.resolver(db),
+ InferBodyId::AnonConstId(id) => id.resolver(db),
+ }
+ }
+}
+
+impl InferBodyId {
+ pub fn expression_store_owner(self, db: &dyn HirDatabase) -> ExpressionStoreOwnerId {
+ match self {
+ InferBodyId::DefWithBodyId(id) => id.into(),
+ InferBodyId::AnonConstId(id) => id.loc(db).owner,
+ }
+ }
+
+ pub fn generic_def(self, db: &dyn HirDatabase) -> GenericDefId {
+ match self {
+ InferBodyId::DefWithBodyId(id) => id.generic_def(db),
+ InferBodyId::AnonConstId(id) => id.loc(db).owner.generic_def(db),
+ }
+ }
+
+ #[inline]
+ pub fn as_function(self) -> Option<FunctionId> {
+ match self {
+ InferBodyId::DefWithBodyId(DefWithBodyId::FunctionId(it)) => Some(it),
+ _ => None,
+ }
+ }
+
+ #[inline]
+ pub fn as_variant(self) -> Option<EnumVariantId> {
+ match self {
+ InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(it)) => Some(it),
+ _ => None,
+ }
+ }
+
+ pub fn store_and_root_expr(self, db: &dyn HirDatabase) -> (&ExpressionStore, ExprId) {
+ match self {
+ InferBodyId::DefWithBodyId(id) => {
+ let body = Body::of(db, id);
+ (body, body.root_expr())
+ }
+ InferBodyId::AnonConstId(id) => {
+ let loc = id.loc(db);
+ let store = ExpressionStore::of(db, loc.owner);
+ (store, loc.expr)
+ }
+ }
+ }
}
pub fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 335aff2c1d..5b0bcd2be8 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -8,24 +8,23 @@
pub(crate) mod diagnostics;
pub(crate) mod path;
-use std::{cell::OnceCell, iter, mem};
+use std::{cell::OnceCell, iter, mem, sync::OnceLock};
-use arrayvec::ArrayVec;
use either::Either;
use hir_def::{
AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, EnumId, EnumVariantId,
- ExpressionStoreOwnerId, FunctionId, GeneralConstId, GenericDefId, GenericParamId, HasModule,
- ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
+ ExpressionStoreOwnerId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId,
+ ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, VariantId,
builtin_type::BuiltinType,
- expr_store::{ExpressionStore, HygieneId, path::Path},
+ expr_store::{ExpressionStore, path::Path},
hir::generics::{
- GenericParamDataRef, GenericParams, TypeOrConstParamData, TypeParamProvenance,
- WherePredicate,
+ GenericParamDataRef, GenericParams, LocalTypeOrConstParamId, TypeOrConstParamData,
+ TypeParamProvenance, WherePredicate,
},
item_tree::FieldsShape,
lang_item::LangItems,
- resolver::{HasResolver, LifetimeNs, Resolver, TypeNs, ValueNs},
+ resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
signatures::{
ConstSignature, FunctionSignature, ImplSignature, StaticSignature, StructSignature,
TraitFlags, TraitSignature, TypeAliasFlags, TypeAliasSignature,
@@ -38,30 +37,32 @@ use hir_def::{
use hir_expand::name::Name;
use la_arena::{Arena, ArenaMap, Idx};
use path::{PathDiagnosticCallback, PathLoweringContext};
+use rustc_abi::ExternAbi;
use rustc_ast_ir::Mutability;
use rustc_hash::FxHashSet;
use rustc_type_ir::{
- AliasTyKind, BoundVarIndexKind, ConstKind, DebruijnIndex, ExistentialPredicate,
- ExistentialProjection, ExistentialTraitRef, FnSig, Interner, OutlivesPredicate, TermKind,
- TyKind, TypeFoldable, TypeVisitableExt, Upcast, UpcastFrom, elaborate,
+ AliasTyKind, BoundVarIndexKind, DebruijnIndex, ExistentialPredicate, ExistentialProjection,
+ ExistentialTraitRef, FnSig, Interner, OutlivesPredicate, TermKind, TyKind, TypeFoldable,
+ TypeVisitableExt, Upcast, UpcastFrom, elaborate,
inherent::{Clause as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _},
};
use smallvec::SmallVec;
use stdx::{impl_from, never};
+use thin_vec::ThinVec;
use tracing::debug;
-use triomphe::{Arc, ThinArc};
use crate::{
- FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
- consteval::intern_const_ref,
- db::{HirDatabase, InternedOpaqueTyId},
- generics::{Generics, generics},
+ ImplTraitId, Span, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
+ consteval::{create_anon_const, path_to_const},
+ db::{AnonConstId, GeneralConstId, HirDatabase, InternedOpaqueTyId},
+ generics::{Generics, SingleGenerics, generics},
+ infer::unify::InferenceTable,
next_solver::{
- AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const,
- DbInterner, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FxIndexMap, GenericArg,
- GenericArgs, ParamConst, ParamEnv, PolyFnSig, Predicate, Region, SolverDefId,
+ AliasTy, Binder, BoundExistentialPredicates, Clause, ClauseKind, Clauses, Const, ConstKind,
+ DbInterner, DefaultAny, EarlyBinder, EarlyParamRegion, ErrorGuaranteed, FnSigKind,
+ FxIndexMap, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Predicate, Region,
StoredClauses, StoredEarlyBinder, StoredGenericArg, StoredGenericArgs, StoredPolyFnSig,
- StoredTy, TraitPredicate, TraitRef, Ty, Tys, UnevaluatedConst, abi::Safety,
+ StoredTraitRef, StoredTy, TraitPredicate, TraitRef, Ty, Tys, Unnormalized, abi::Safety,
util::BottomUpFolder,
},
};
@@ -172,7 +173,29 @@ pub(crate) enum GenericPredicateSource {
AssocTyBound,
}
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(crate) enum ForbidParamsAfterReason {
+ /// When lowering generic param defaults, you cannot refer to any param after
+ /// the currently lowered param, including the current param.
+ LoweringParamDefault,
+ /// Most anon const (except array repeat expressions) cannot refer to any generic
+ /// param.
+ AnonConst,
+ /// The type of a const param cannot refer to a type param.
+ ConstParamTy,
+}
+
+pub trait TyLoweringInferVarsCtx<'db> {
+ fn next_ty_var(&mut self, span: Span) -> Ty<'db>;
+ fn next_const_var(&mut self, span: Span) -> Const<'db>;
+ fn next_region_var(&mut self, span: Span) -> Region<'db>;
+
+ #[expect(private_interfaces)]
+ fn as_table(&mut self) -> Option<&mut InferenceTable<'db>> {
+ None
+ }
+}
+
pub struct TyLoweringContext<'db, 'a> {
pub db: &'db dyn HirDatabase,
interner: DbInterner<'db>,
@@ -180,17 +203,19 @@ pub struct TyLoweringContext<'db, 'a> {
lang_items: &'db LangItems,
resolver: &'a Resolver<'db>,
store: &'a ExpressionStore,
- def: GenericDefId,
- generics: OnceCell<Generics<'db>>,
+ def: ExpressionStoreOwnerId,
+ generic_def: GenericDefId,
+ generics: &'a OnceCell<Generics<'db>>,
in_binders: DebruijnIndex,
impl_trait_mode: ImplTraitLoweringState,
/// Tracks types with explicit `?Sized` bounds.
pub(crate) unsized_types: FxHashSet<Ty<'db>>,
- pub(crate) diagnostics: Vec<TyLoweringDiagnostic>,
+ pub(crate) diagnostics: ThinVec<TyLoweringDiagnostic>,
lifetime_elision: LifetimeElisionKind<'db>,
- /// When lowering the defaults for generic params, this contains the index of the currently lowered param.
- /// We disallow referring to later params, or to ADT's `Self`.
- lowering_param_default: Option<u32>,
+ forbid_params_after: Option<u32>,
+ forbid_params_after_reason: ForbidParamsAfterReason,
+ pub(crate) defined_anon_consts: ThinVec<AnonConstId>,
+ infer_vars: Option<&'a mut dyn TyLoweringInferVarsCtx<'db>>,
}
impl<'db, 'a> TyLoweringContext<'db, 'a> {
@@ -198,7 +223,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
db: &'db dyn HirDatabase,
resolver: &'a Resolver<'db>,
store: &'a ExpressionStore,
- def: GenericDefId,
+ def: ExpressionStoreOwnerId,
+ generic_def: GenericDefId,
+ generics: &'a OnceCell<Generics<'db>>,
lifetime_elision: LifetimeElisionKind<'db>,
) -> Self {
let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed);
@@ -212,14 +239,18 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
lang_items: interner.lang_items(),
resolver,
def,
- generics: Default::default(),
+ generic_def,
+ generics,
store,
in_binders,
impl_trait_mode,
unsized_types: FxHashSet::default(),
- diagnostics: Vec::new(),
+ diagnostics: ThinVec::new(),
lifetime_elision,
- lowering_param_default: None,
+ forbid_params_after: None,
+ forbid_params_after_reason: ForbidParamsAfterReason::AnonConst,
+ defined_anon_consts: ThinVec::new(),
+ infer_vars: None,
}
}
@@ -255,13 +286,57 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
self
}
- pub(crate) fn lowering_param_default(&mut self, index: u32) {
- self.lowering_param_default = Some(index);
+ pub(crate) fn forbid_params_after(&mut self, index: u32, reason: ForbidParamsAfterReason) {
+ self.forbid_params_after = Some(index);
+ self.forbid_params_after_reason = reason;
+ }
+
+ pub fn with_infer_vars_behavior(
+ mut self,
+ behavior: Option<&'a mut dyn TyLoweringInferVarsCtx<'db>>,
+ ) -> Self {
+ self.infer_vars = behavior;
+ self
}
pub(crate) fn push_diagnostic(&mut self, type_ref: TypeRefId, kind: TyLoweringDiagnosticKind) {
self.diagnostics.push(TyLoweringDiagnostic { source: type_ref, kind });
}
+
+ #[track_caller]
+ pub(crate) fn expect_table(&mut self) -> &mut InferenceTable<'db> {
+ self.infer_vars.as_mut().unwrap().as_table().unwrap()
+ }
+
+ fn next_ty_var(&mut self, span: Span) -> Ty<'db> {
+ match &mut self.infer_vars {
+ Some(infer_vars) => infer_vars.next_ty_var(span),
+ None => {
+ // FIXME: Emit an error: no infer vars allowed here.
+ self.types.types.error
+ }
+ }
+ }
+
+ fn next_const_var(&mut self, span: Span) -> Const<'db> {
+ match &mut self.infer_vars {
+ Some(infer_vars) => infer_vars.next_const_var(span),
+ None => {
+ // FIXME: Emit an error: no infer vars allowed here.
+ self.types.consts.error
+ }
+ }
+ }
+
+ fn next_region_var(&mut self, span: Span) -> Region<'db> {
+ match &mut self.infer_vars {
+ Some(infer_vars) => infer_vars.next_region_var(span),
+ None => {
+ // FIXME: Emit an error: no infer vars allowed here.
+ self.types.regions.error
+ }
+ }
+ }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
@@ -282,162 +357,67 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
}
pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -> Const<'db> {
- let expr_id = const_ref.expr;
- let expr = &self.store[expr_id];
- match expr {
- hir_def::hir::Expr::Path(path) => self
- .path_to_const(path)
- .unwrap_or_else(|| Const::new(self.interner, ConstKind::Error(ErrorGuaranteed))),
- hir_def::hir::Expr::Literal(literal) => {
- intern_const_ref(self.db, literal, const_type, self.resolver.krate())
- }
- hir_def::hir::Expr::UnaryOp { expr: inner_expr, op: hir_def::hir::UnaryOp::Neg } => {
- if let hir_def::hir::Expr::Literal(literal) = &self.store[*inner_expr] {
- // Only handle negation for signed integers and floats
- match literal {
- hir_def::hir::Literal::Int(_, _) | hir_def::hir::Literal::Float(_, _) => {
- if let Some(negated_literal) = literal.clone().negate() {
- intern_const_ref(
- self.db,
- &negated_literal,
- const_type,
- self.resolver.krate(),
- )
- } else {
- Const::new(self.interner, ConstKind::Error(ErrorGuaranteed))
- }
- }
- // For unsigned integers, chars, bools, etc., negation is not meaningful
- _ => Const::new(self.interner, ConstKind::Error(ErrorGuaranteed)),
- }
- } else {
- // Complex negation expression (e.g. `-N` where N is a const param)
- self.lower_const_as_unevaluated(expr_id, const_type)
- }
- }
- hir_def::hir::Expr::Underscore => {
- Const::new(self.interner, ConstKind::Error(ErrorGuaranteed))
- }
- // Any other complex expression becomes an unevaluated anonymous const.
- _ => self.lower_const_as_unevaluated(expr_id, const_type),
- }
- }
+ #[expect(clippy::manual_map, reason = "a `map()` here generates a borrowck error")]
+ let create_var = match &mut self.infer_vars {
+ Some(infer_vars) => Some(
+ (&mut |span| infer_vars.next_const_var(span)) as &mut dyn FnMut(Span) -> Const<'db>,
+ ),
+ None => None,
+ };
+ let konst = create_anon_const(
+ self.interner,
+ self.def,
+ self.store,
+ const_ref.expr,
+ self.resolver,
+ const_type,
+ &|| self.generics.get_or_init(|| generics(self.db, self.generic_def)),
+ create_var,
+ self.forbid_params_after,
+ );
- /// Lower a complex const expression to an `UnevaluatedConst` backed by an `AnonConstId`.
- ///
- /// The `expected_ty_ref` is `None` for array lengths (implicitly `usize`) or
- /// `Some(type_ref_id)` for const generic arguments where the expected type comes
- /// from the const parameter declaration.
- fn lower_const_as_unevaluated(
- &mut self,
- _expr: hir_def::hir::ExprId,
- _expected_ty: Ty<'db>,
- ) -> Const<'db> {
- // /// Build the identity generic args for the current generic context.
- // ///
- // /// This maps each generic parameter to itself (as a `ParamTy`, `ParamConst`,
- // /// or `EarlyParamRegion`), which is the correct substitution when creating
- // /// an `UnevaluatedConst` during type lowering — the anon const inherits the
- // /// parent's generics and they haven't been substituted yet.
- // fn current_generic_args(&self) -> GenericArgs<'db> {
- // let generics = self.generics();
- // let interner = self.interner;
- // GenericArgs::new_from_iter(
- // interner,
- // generics.iter_id().enumerate().map(|(index, id)| match id {
- // GenericParamId::TypeParamId(id) => {
- // GenericArg::from(Ty::new_param(interner, id, index as u32))
- // }
- // GenericParamId::ConstParamId(id) => GenericArg::from(Const::new_param(
- // interner,
- // ParamConst { id, index: index as u32 },
- // )),
- // GenericParamId::LifetimeParamId(id) => GenericArg::from(Region::new_early_param(
- // interner,
- // EarlyParamRegion { id, index: index as u32 },
- // )),
- // }),
- // )
- // }
- // let loc = AnonConstLoc { owner: self.def, expr };
- // let id = loc.intern(self.db);
- // let args = self.current_generic_args();
- // Const::new(
- // self.interner,
- // ConstKind::Unevaluated(UnevaluatedConst::new(
- // GeneralConstId::AnonConstId(id).into(),
- // args,
- // )),
- // )
- Const::new(self.interner, ConstKind::Error(ErrorGuaranteed))
- }
-
- pub(crate) fn path_to_const(&mut self, path: &Path) -> Option<Const<'db>> {
- match self.resolver.resolve_path_in_value_ns_fully(self.db, path, HygieneId::ROOT) {
- Some(ValueNs::GenericParam(p)) => {
- let args = self.generics();
- match args.type_or_const_param_idx(p.into()) {
- Some(idx) => Some(self.const_param(p, idx as u32)),
- None => {
- never!(
- "Generic list doesn't contain this param: {:?}, {:?}, {:?}",
- args,
- path,
- p
- );
- None
- }
- }
- }
- Some(ValueNs::ConstId(c)) => {
- let args = GenericArgs::empty(self.interner);
- Some(Const::new(
- self.interner,
- rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new(
- GeneralConstId::ConstId(c).into(),
- args,
- )),
- ))
- }
- _ => None,
+ if let Ok(konst) = konst
+ && let ConstKind::Unevaluated(konst) = konst.kind()
+ && let GeneralConstId::AnonConstId(konst) = konst.def.0
+ {
+ self.defined_anon_consts.push(konst);
}
+
+ konst.unwrap_or({
+ // FIXME: Report an error.
+ self.types.consts.error
+ })
}
- pub(crate) fn lower_path_as_const(&mut self, path: &Path, const_type: Ty<'db>) -> Const<'db> {
- self.path_to_const(path).unwrap_or_else(|| unknown_const(const_type))
+ pub(crate) fn lower_path_as_const(&mut self, path: &Path, _const_type: Ty<'db>) -> Const<'db> {
+ path_to_const(self.db, self.resolver, &|| self.generics(), self.forbid_params_after, path)
+ .unwrap_or({
+ // FIXME: Report an error.
+ self.types.consts.error
+ })
}
fn generics(&self) -> &Generics<'db> {
- self.generics.get_or_init(|| generics(self.db, self.def))
+ self.generics.get_or_init(|| generics(self.db, self.generic_def))
}
fn param_index_is_disallowed(&self, index: u32) -> bool {
- self.lowering_param_default
- .is_some_and(|disallow_params_after| index >= disallow_params_after)
+ self.forbid_params_after.is_some_and(|disallow_params_after| index >= disallow_params_after)
}
fn type_param(&mut self, id: TypeParamId, index: u32) -> Ty<'db> {
if self.param_index_is_disallowed(index) {
// FIXME: Report an error.
- Ty::new_error(self.interner, ErrorGuaranteed)
+ self.types.types.error
} else {
Ty::new_param(self.interner, id, index)
}
}
- fn const_param(&mut self, id: ConstParamId, index: u32) -> Const<'db> {
- if self.param_index_is_disallowed(index) {
- // FIXME: Report an error.
- Const::error(self.interner)
- } else {
- Const::new_param(self.interner, ParamConst { id, index })
- }
- }
-
fn region_param(&mut self, id: LifetimeParamId, index: u32) -> Region<'db> {
if self.param_index_is_disallowed(index) {
// FIXME: Report an error.
- Region::error(self.interner)
+ self.types.regions.error
} else {
Region::new_early_param(self.interner, EarlyParamRegion { id, index })
}
@@ -465,9 +445,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
res = Some(TypeNs::GenericParam(type_param_id));
let generics = self.generics();
- let (idx, _data) =
- generics.type_or_const_param(type_param_id.into()).expect("matching generics");
- self.type_param(type_param_id, idx as u32)
+ let idx = generics.type_or_const_param_idx(type_param_id.into());
+ self.type_param(type_param_id, idx)
}
&TypeRef::RawPtr(inner, mutability) => {
let inner_ty = self.lower_ty(inner);
@@ -475,7 +454,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
}
TypeRef::Array(array) => {
let inner_ty = self.lower_ty(array.ty);
- let const_len = self.lower_const(array.len, Ty::new_usize(interner));
+ let const_len = self.lower_const(array.len, self.types.types.usize);
Ty::new_array_with_const_len(interner, inner_ty, const_len)
}
&TypeRef::Slice(inner) => {
@@ -485,12 +464,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
TypeRef::Reference(ref_) => {
let inner_ty = self.lower_ty(ref_.ty);
// FIXME: It should infer the eldided lifetimes instead of stubbing with error
- let lifetime = ref_
- .lifetime
- .map_or_else(|| Region::error(interner), |lr| self.lower_lifetime(lr));
+ let lifetime =
+ ref_.lifetime.map_or(self.types.regions.error, |lr| self.lower_lifetime(lr));
Ty::new_ref(interner, lifetime, inner_ty, lower_mutability(ref_.mutability))
}
- TypeRef::Placeholder => Ty::new_error(interner, ErrorGuaranteed),
+ TypeRef::Placeholder => self.next_ty_var(type_ref_id.into()),
TypeRef::Fn(fn_) => self.lower_fn_ptr(fn_),
TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds),
TypeRef::ImplTrait(bounds) => {
@@ -516,8 +494,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
|f| ImplTraitId::ReturnTypeImplTrait(f, idx),
|a| ImplTraitId::TypeAliasImplTrait(a, idx),
);
- let opaque_ty_id: SolverDefId =
- self.db.intern_impl_trait_id(impl_trait_id).into();
+ let opaque_ty_id = InternedOpaqueTyId::new(self.db, impl_trait_id);
// We don't want to lower the bounds inside the binders
// we're currently in, because they don't end up inside
@@ -534,23 +511,24 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
});
self.impl_trait_mode.opaque_type_data[idx] = actual_opaque_type_data;
- let args = GenericArgs::identity_for_item(self.interner, opaque_ty_id);
+ let args =
+ GenericArgs::identity_for_item(self.interner, opaque_ty_id.into());
Ty::new_alias(
self.interner,
AliasTy::new_from_args(
self.interner,
- AliasTyKind::Opaque { def_id: opaque_ty_id },
+ AliasTyKind::Opaque { def_id: opaque_ty_id.into() },
args,
),
)
}
ImplTraitLoweringMode::Disallowed => {
// FIXME: report error
- Ty::new_error(self.interner, ErrorGuaranteed)
+ self.types.types.error
}
}
}
- TypeRef::Error => Ty::new_error(self.interner, ErrorGuaranteed),
+ TypeRef::Error => self.types.types.error,
};
(ty, res)
}
@@ -571,9 +549,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
Ty::new_fn_ptr(
interner,
Binder::dummy(FnSig {
- abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol),
- safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe },
- c_variadic: fn_.is_varargs,
+ fn_sig_kind: FnSigKind::new(
+ fn_.abi,
+ if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe },
+ fn_.is_varargs,
+ ),
inputs_and_output: Tys::new_from_slice(&args),
}),
)
@@ -630,13 +610,13 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
if let Some(type_ref) = path.type_anchor() {
let (ty, res) = self.lower_ty_ext(type_ref);
let mut ctx = self.at_path(path_id);
- return ctx.lower_ty_relative_path(ty, res, false);
+ return ctx.lower_ty_relative_path(ty, res, false, path_id.type_ref().into());
}
let mut ctx = self.at_path(path_id);
let (resolution, remaining_index) = match ctx.resolve_path_in_type_ns() {
Some(it) => it,
- None => return (Ty::new_error(self.interner, ErrorGuaranteed), None),
+ None => return (self.types.types.error, None),
};
if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() {
@@ -646,7 +626,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
return (ty, None);
}
- ctx.lower_partly_resolved_path(resolution, false)
+ ctx.lower_partly_resolved_path(resolution, false, path_id.type_ref().into())
}
fn lower_trait_ref_from_path(
@@ -660,7 +640,15 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
TypeNs::TraitId(tr) => tr,
_ => return None,
};
- Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty, false), ctx))
+ Some((
+ ctx.lower_trait_ref_from_resolved_path(
+ resolved,
+ explicit_self_ty,
+ false,
+ path_id.type_ref().into(),
+ ),
+ ctx,
+ ))
}
fn lower_trait_ref(
@@ -722,7 +710,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
ctx.ty_ctx().unsized_types.insert(self_ty);
} else {
if !ignore_bindings {
- assoc_bounds = ctx.assoc_type_bindings_from_type_bound(trait_ref);
+ assoc_bounds = ctx.assoc_type_bindings_from_type_bound(
+ trait_ref,
+ path.type_ref().into(),
+ );
}
clause = Some(Clause(Predicate::new(
interner,
@@ -769,7 +760,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty<'db> {
let interner = self.interner;
- let dummy_self_ty = dyn_trait_dummy_self(interner);
+ let dummy_self_ty = self.types.types.dyn_trait_dummy_self;
let mut region = None;
// INVARIANT: The principal trait bound, if present, must come first. Others may be in any
// order but should be in the same order for the same set but possibly different order of
@@ -846,7 +837,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
let mut projection_bounds = FxIndexMap::default();
for proj in projections {
let key = (
- proj.skip_binder().def_id().expect_type_alias(),
+ proj.skip_binder().def_id().0,
interner.anonymize_bound_vars(
proj.map_bound(|proj| proj.projection_term.trait_ref(interner)),
),
@@ -894,7 +885,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
.0
.trait_items(self.db)
.associated_types()
- .map(|item| (item, trait_ref)),
+ .map(|item| (item.into(), trait_ref)),
);
}
ClauseKind::Projection(pred) => {
@@ -928,7 +919,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
// the discussion in #56288 for alternatives.
if !references_self {
let key = (
- pred.skip_binder().projection_term.def_id.expect_type_alias(),
+ pred.skip_binder().def_id().0,
interner.anonymize_bound_vars(pred.map_bound(|proj| {
proj.projection_term.trait_ref(interner)
})),
@@ -954,7 +945,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
.filter_map(|key| projection_bounds.get(&key).copied())
.collect();
- projection_bounds.sort_unstable_by_key(|proj| proj.skip_binder().def_id());
+ projection_bounds.sort_unstable_by_key(|proj| proj.skip_binder().def_id().0);
let principal = principal.map(|principal| {
principal.map_bound(|principal| {
@@ -967,7 +958,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
.map(|arg| {
if arg.walk().any(|arg| arg == dummy_self_ty.into()) {
// FIXME: Report an error.
- Ty::new_error(interner, ErrorGuaranteed).into()
+ self.types.types.error.into()
} else {
arg
}
@@ -993,8 +984,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
false
});
if references_self {
- proj.projection_term =
- replace_dummy_self_with_error(interner, proj.projection_term);
+ proj.projection_term = replace_dummy_self_with_error(
+ interner,
+ self.types,
+ proj.projection_term,
+ );
}
ExistentialPredicate::Projection(ExistentialProjection::erase_self_ty(
@@ -1032,17 +1026,17 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
} else {
// FIXME: report error
// (additional non-auto traits, associated type rebound, or no resolved trait)
- Ty::new_error(self.interner, ErrorGuaranteed)
+ self.types.types.error
}
}
- fn lower_impl_trait(&mut self, def_id: SolverDefId, bounds: &[TypeBound]) -> ImplTrait {
+ fn lower_impl_trait(&mut self, def_id: InternedOpaqueTyId, bounds: &[TypeBound]) -> ImplTrait {
let interner = self.interner;
cov_mark::hit!(lower_rpit);
- let args = GenericArgs::identity_for_item(interner, def_id);
+ let args = GenericArgs::identity_for_item(interner, def_id.into());
let self_ty = Ty::new_alias(
self.interner,
- AliasTy::new_from_args(interner, rustc_type_ir::Opaque { def_id }, args),
+ AliasTy::new_from_args(interner, rustc_type_ir::Opaque { def_id: def_id.into() }, args),
);
let (predicates, assoc_ty_bounds_start) =
self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| {
@@ -1094,11 +1088,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
Some(resolution) => match resolution {
LifetimeNs::Static => Region::new_static(self.interner),
LifetimeNs::LifetimeParam(id) => {
- let idx = match self.generics().lifetime_idx(id) {
- None => return Region::error(self.interner),
- Some(idx) => idx,
- };
- self.region_param(id, idx as u32)
+ let idx = self.generics().lifetime_param_idx(id);
+ self.region_param(id, idx)
}
},
None => Region::error(self.interner),
@@ -1106,20 +1097,78 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
}
}
-fn dyn_trait_dummy_self(interner: DbInterner<'_>) -> Ty<'_> {
- // This type must not appear anywhere except here.
- Ty::new_fresh(interner, 0)
+#[derive(Clone, PartialEq, Eq)]
+pub struct TyLoweringResult<T> {
+ pub value: T,
+ info: Option<Box<(ThinVec<TyLoweringDiagnostic>, ThinVec<AnonConstId>)>>,
+}
+
+impl<T: std::fmt::Debug> std::fmt::Debug for TyLoweringResult<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let mut debug = f.debug_struct("TyLoweringResult");
+ debug.field("value", &self.value);
+ let diagnostics = self.diagnostics();
+ if !diagnostics.is_empty() {
+ debug.field("diagnostics", &diagnostics);
+ }
+ let defined_anon_consts = self.defined_anon_consts();
+ if !defined_anon_consts.is_empty() {
+ debug.field("defined_anon_consts", &defined_anon_consts);
+ }
+ debug.finish()
+ }
+}
+
+impl<T> TyLoweringResult<T> {
+ fn new(
+ value: T,
+ mut diagnostics: ThinVec<TyLoweringDiagnostic>,
+ mut defined_anon_consts: ThinVec<AnonConstId>,
+ ) -> Self {
+ let info = if diagnostics.is_empty() && defined_anon_consts.is_empty() {
+ None
+ } else {
+ diagnostics.shrink_to_fit();
+ defined_anon_consts.shrink_to_fit();
+ Some(Box::new((diagnostics, defined_anon_consts)))
+ };
+ Self { value, info }
+ }
+
+ fn from_ctx(value: T, ctx: TyLoweringContext<'_, '_>) -> Self {
+ Self::new(value, ctx.diagnostics, ctx.defined_anon_consts)
+ }
+
+ fn empty(value: T) -> Self {
+ Self { value, info: None }
+ }
+
+ #[inline]
+ pub fn diagnostics(&self) -> &[TyLoweringDiagnostic] {
+ match &self.info {
+ Some(info) => &info.0,
+ None => &[],
+ }
+ }
+
+ #[inline]
+ pub fn defined_anon_consts(&self) -> &[AnonConstId] {
+ match &self.info {
+ Some(info) => &info.1,
+ None => &[],
+ }
+ }
}
fn replace_dummy_self_with_error<'db, T: TypeFoldable<DbInterner<'db>>>(
interner: DbInterner<'db>,
+ types: &DefaultAny<'db>,
t: T,
) -> T {
- let dyn_trait_dummy_self = dyn_trait_dummy_self(interner);
t.fold_with(&mut BottomUpFolder {
interner,
ty_op: |ty| {
- if ty == dyn_trait_dummy_self { Ty::new_error(interner, ErrorGuaranteed) } else { ty }
+ if ty == types.types.dyn_trait_dummy_self { types.types.error } else { ty }
},
lt_op: |lt| lt,
ct_op: |ct| ct,
@@ -1133,62 +1182,36 @@ pub(crate) fn lower_mutability(m: hir_def::type_ref::Mutability) -> Mutability {
}
}
-fn unknown_const(_ty: Ty<'_>) -> Const<'_> {
- Const::new(DbInterner::conjure(), ConstKind::Error(ErrorGuaranteed))
-}
-
-pub(crate) type Diagnostics = Option<ThinArc<(), TyLoweringDiagnostic>>;
-
-pub(crate) fn create_diagnostics(diagnostics: Vec<TyLoweringDiagnostic>) -> Diagnostics {
- (!diagnostics.is_empty()).then(|| ThinArc::from_header_and_iter((), diagnostics.into_iter()))
-}
-
pub(crate) fn impl_trait_query<'db>(
db: &'db dyn HirDatabase,
impl_id: ImplId,
) -> Option<EarlyBinder<'db, TraitRef<'db>>> {
- db.impl_trait_with_diagnostics(impl_id).map(|it| it.0)
+ impl_trait_with_diagnostics(db, impl_id)
+ .as_ref()
+ .map(|it| it.value.get(DbInterner::new_no_crate(db)))
}
-pub(crate) fn impl_trait_with_diagnostics<'db>(
- db: &'db dyn HirDatabase,
+#[salsa::tracked(returns(ref))]
+pub(crate) fn impl_trait_with_diagnostics(
+ db: &dyn HirDatabase,
impl_id: ImplId,
-) -> Option<(EarlyBinder<'db, TraitRef<'db>>, Diagnostics)> {
- return impl_trait_with_diagnostics_query(db, impl_id).as_ref().map(|(binder, diags)| {
- (
- binder.get_with(|(trait_id, args)| {
- TraitRef::new_from_args(
- DbInterner::new_no_crate(db),
- (*trait_id).into(),
- args.as_ref(),
- )
- }),
- diags.clone(),
- )
- });
-
- #[salsa::tracked(returns(ref))]
- pub(crate) fn impl_trait_with_diagnostics_query<'db>(
- db: &'db dyn HirDatabase,
- impl_id: ImplId,
- ) -> Option<(StoredEarlyBinder<(TraitId, StoredGenericArgs)>, Diagnostics)> {
- let impl_data = ImplSignature::of(db, impl_id);
- let resolver = impl_id.resolver(db);
- let mut ctx = TyLoweringContext::new(
- db,
- &resolver,
- &impl_data.store,
- impl_id.into(),
- LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true },
- );
- let self_ty = db.impl_self_ty(impl_id).skip_binder();
- let target_trait = impl_data.target_trait.as_ref()?;
- let trait_ref = ctx.lower_trait_ref(target_trait, self_ty)?;
- Some((
- StoredEarlyBinder::bind((trait_ref.def_id.0, trait_ref.args.store())),
- create_diagnostics(ctx.diagnostics),
- ))
- }
+) -> Option<TyLoweringResult<StoredEarlyBinder<StoredTraitRef>>> {
+ let impl_data = ImplSignature::of(db, impl_id);
+ let resolver = impl_id.resolver(db);
+ let generics = OnceCell::new();
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ &impl_data.store,
+ ExpressionStoreOwnerId::Signature(impl_id.into()),
+ impl_id.into(),
+ &generics,
+ LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true },
+ );
+ let self_ty = db.impl_self_ty(impl_id).skip_binder();
+ let target_trait = impl_data.target_trait.as_ref()?;
+ let trait_ref = ctx.lower_trait_ref(target_trait, self_ty)?;
+ Some(TyLoweringResult::from_ctx(StoredEarlyBinder::bind(StoredTraitRef::new(trait_ref)), ctx))
}
impl ImplTraitId {
@@ -1259,11 +1282,14 @@ impl ImplTraits {
// FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
let data = FunctionSignature::of(db, def);
let resolver = def.resolver(db);
+ let generics = OnceCell::new();
let mut ctx_ret = TyLoweringContext::new(
db,
&resolver,
&data.store,
+ ExpressionStoreOwnerId::Signature(def.into()),
def.into(),
+ &generics,
LifetimeElisionKind::Infer,
)
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
@@ -1287,11 +1313,14 @@ impl ImplTraits {
) -> Option<Box<StoredEarlyBinder<ImplTraits>>> {
let data = TypeAliasSignature::of(db, def);
let resolver = def.resolver(db);
+ let generics = OnceCell::new();
let mut ctx = TyLoweringContext::new(
db,
&resolver,
&data.store,
+ ExpressionStoreOwnerId::Signature(def.into()),
def.into(),
+ &generics,
LifetimeElisionKind::AnonymousReportError,
)
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
@@ -1354,97 +1383,118 @@ pub(crate) fn ty_query<'db>(db: &'db dyn HirDatabase, def: TyDefId) -> EarlyBind
it,
GenericArgs::identity_for_item(interner, it.into()),
)),
- TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).0,
+ TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).value.get(),
}
}
/// Build the declared type of a function. This should not need to look at the
/// function body.
-fn type_for_fn(db: &dyn HirDatabase, def: FunctionId) -> StoredEarlyBinder<StoredTy> {
+fn type_for_fn<'db>(db: &'db dyn HirDatabase, def: FunctionId) -> EarlyBinder<'db, Ty<'db>> {
let interner = DbInterner::new_no_crate(db);
- StoredEarlyBinder::bind(
- Ty::new_fn_def(
- interner,
- CallableDefId::FunctionId(def).into(),
- GenericArgs::identity_for_item(interner, def.into()),
- )
- .store(),
- )
+ EarlyBinder::bind(Ty::new_fn_def(
+ interner,
+ CallableDefId::FunctionId(def).into(),
+ GenericArgs::identity_for_item(interner, def.into()),
+ ))
+}
+
+pub(crate) fn type_for_const<'db>(
+ db: &'db dyn HirDatabase,
+ def: ConstId,
+) -> EarlyBinder<'db, Ty<'db>> {
+ type_for_const_with_diagnostics(db, def).value.get()
}
/// Build the declared type of a const.
-fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> StoredEarlyBinder<StoredTy> {
+#[salsa_macros::tracked(returns(ref))]
+pub(crate) fn type_for_const_with_diagnostics(
+ db: &dyn HirDatabase,
+ def: ConstId,
+) -> TyLoweringResult<StoredEarlyBinder<StoredTy>> {
let resolver = def.resolver(db);
let data = ConstSignature::of(db, def);
let parent = def.loc(db).container;
+ let generics = OnceCell::new();
let mut ctx = TyLoweringContext::new(
db,
&resolver,
&data.store,
+ ExpressionStoreOwnerId::Signature(def.into()),
def.into(),
+ &generics,
LifetimeElisionKind::AnonymousReportError,
);
ctx.set_lifetime_elision(LifetimeElisionKind::for_const(ctx.interner, parent));
- StoredEarlyBinder::bind(ctx.lower_ty(data.type_ref).store())
+ let result = StoredEarlyBinder::bind(ctx.lower_ty(data.type_ref).store());
+ TyLoweringResult::from_ctx(result, ctx)
+}
+
+pub(crate) fn type_for_static<'db>(
+ db: &'db dyn HirDatabase,
+ def: StaticId,
+) -> EarlyBinder<'db, Ty<'db>> {
+ type_for_static_with_diagnostics(db, def).value.get()
}
/// Build the declared type of a static.
-fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> StoredEarlyBinder<StoredTy> {
+#[salsa_macros::tracked(returns(ref))]
+pub(crate) fn type_for_static_with_diagnostics(
+ db: &dyn HirDatabase,
+ def: StaticId,
+) -> TyLoweringResult<StoredEarlyBinder<StoredTy>> {
let resolver = def.resolver(db);
let data = StaticSignature::of(db, def);
+ let generics = OnceCell::new();
let mut ctx = TyLoweringContext::new(
db,
&resolver,
&data.store,
+ ExpressionStoreOwnerId::Signature(def.into()),
def.into(),
+ &generics,
LifetimeElisionKind::AnonymousReportError,
);
ctx.set_lifetime_elision(LifetimeElisionKind::Elided(Region::new_static(ctx.interner)));
- StoredEarlyBinder::bind(ctx.lower_ty(data.type_ref).store())
+ let result = StoredEarlyBinder::bind(ctx.lower_ty(data.type_ref).store());
+ TyLoweringResult::from_ctx(result, ctx)
}
/// Build the type of a tuple struct constructor.
-fn type_for_struct_constructor(
- db: &dyn HirDatabase,
+fn type_for_struct_constructor<'db>(
+ db: &'db dyn HirDatabase,
def: StructId,
-) -> Option<StoredEarlyBinder<StoredTy>> {
+) -> Option<EarlyBinder<'db, Ty<'db>>> {
let struct_data = StructSignature::of(db, def);
match struct_data.shape {
FieldsShape::Record => None,
FieldsShape::Unit => Some(type_for_adt(db, def.into())),
FieldsShape::Tuple => {
let interner = DbInterner::new_no_crate(db);
- Some(StoredEarlyBinder::bind(
- Ty::new_fn_def(
- interner,
- CallableDefId::StructId(def).into(),
- GenericArgs::identity_for_item(interner, def.into()),
- )
- .store(),
- ))
+ Some(EarlyBinder::bind(Ty::new_fn_def(
+ interner,
+ CallableDefId::StructId(def).into(),
+ GenericArgs::identity_for_item(interner, def.into()),
+ )))
}
}
}
/// Build the type of a tuple enum variant constructor.
-fn type_for_enum_variant_constructor(
- db: &dyn HirDatabase,
+fn type_for_enum_variant_constructor<'db>(
+ db: &'db dyn HirDatabase,
def: EnumVariantId,
-) -> Option<StoredEarlyBinder<StoredTy>> {
+) -> Option<EarlyBinder<'db, Ty<'db>>> {
let struct_data = def.fields(db);
match struct_data.shape {
FieldsShape::Record => None,
FieldsShape::Unit => Some(type_for_adt(db, def.loc(db).parent.into())),
FieldsShape::Tuple => {
let interner = DbInterner::new_no_crate(db);
- Some(StoredEarlyBinder::bind(
- Ty::new_fn_def(
- interner,
- CallableDefId::EnumVariantId(def).into(),
- GenericArgs::identity_for_item(interner, def.loc(db).parent.into()),
- )
- .store(),
- ))
+ Some(EarlyBinder::bind(Ty::new_fn_def(
+ interner,
+ CallableDefId::EnumVariantId(def).into(),
+ GenericArgs::identity_for_item(interner, def.loc(db).parent.into()),
+ )))
}
}
}
@@ -1453,213 +1503,190 @@ pub(crate) fn value_ty<'db>(
db: &'db dyn HirDatabase,
def: ValueTyDefId,
) -> Option<EarlyBinder<'db, Ty<'db>>> {
- return value_ty_query(db, def).as_ref().map(|it| it.get());
-
- #[salsa::tracked(returns(ref))]
- pub(crate) fn value_ty_query<'db>(
- db: &'db dyn HirDatabase,
- def: ValueTyDefId,
- ) -> Option<StoredEarlyBinder<StoredTy>> {
- match def {
- ValueTyDefId::FunctionId(it) => Some(type_for_fn(db, it)),
- ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it),
- ValueTyDefId::UnionId(it) => Some(type_for_adt(db, it.into())),
- ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it),
- ValueTyDefId::ConstId(it) => Some(type_for_const(db, it)),
- ValueTyDefId::StaticId(it) => Some(type_for_static(db, it)),
- }
+ match def {
+ ValueTyDefId::FunctionId(it) => Some(type_for_fn(db, it)),
+ ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it),
+ ValueTyDefId::UnionId(it) => Some(type_for_adt(db, it.into())),
+ ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it),
+ ValueTyDefId::ConstId(it) => Some(type_for_const(db, it)),
+ ValueTyDefId::StaticId(it) => Some(type_for_static(db, it)),
}
}
-pub(crate) fn type_for_type_alias_with_diagnostics<'db>(
- db: &'db dyn HirDatabase,
+#[salsa::tracked(returns(ref), cycle_result = type_for_type_alias_with_diagnostics_cycle_result)]
+pub(crate) fn type_for_type_alias_with_diagnostics(
+ db: &dyn HirDatabase,
t: TypeAliasId,
-) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) {
- let (ty, diags) = type_for_type_alias_with_diagnostics_query(db, t);
- return (ty.get(), diags.clone());
-
- #[salsa::tracked(returns(ref), cycle_result = type_for_type_alias_with_diagnostics_cycle_result)]
- pub(crate) fn type_for_type_alias_with_diagnostics_query<'db>(
- db: &'db dyn HirDatabase,
- t: TypeAliasId,
- ) -> (StoredEarlyBinder<StoredTy>, Diagnostics) {
- let type_alias_data = TypeAliasSignature::of(db, t);
- let mut diags = None;
+) -> TyLoweringResult<StoredEarlyBinder<StoredTy>> {
+ let type_alias_data = TypeAliasSignature::of(db, t);
+ let interner = DbInterner::new_no_crate(db);
+ if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) {
+ TyLoweringResult::empty(StoredEarlyBinder::bind(
+ Ty::new_foreign(interner, t.into()).store(),
+ ))
+ } else {
let resolver = t.resolver(db);
- let interner = DbInterner::new_no_crate(db);
- let inner = if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) {
- StoredEarlyBinder::bind(Ty::new_foreign(interner, t.into()).store())
- } else {
- let mut ctx = TyLoweringContext::new(
- db,
- &resolver,
- &type_alias_data.store,
- t.into(),
- LifetimeElisionKind::AnonymousReportError,
- )
- .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
- let res = StoredEarlyBinder::bind(
- type_alias_data
- .ty
- .map(|type_ref| ctx.lower_ty(type_ref))
- .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed))
- .store(),
- );
- diags = create_diagnostics(ctx.diagnostics);
- res
- };
- (inner, diags)
- }
-
- pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result(
- db: &dyn HirDatabase,
- _: salsa::Id,
- _adt: TypeAliasId,
- ) -> (StoredEarlyBinder<StoredTy>, Diagnostics) {
- (
- StoredEarlyBinder::bind(
- Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(),
- ),
- None,
+ let generics = OnceCell::new();
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ &type_alias_data.store,
+ ExpressionStoreOwnerId::Signature(t.into()),
+ t.into(),
+ &generics,
+ LifetimeElisionKind::AnonymousReportError,
)
+ .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
+ let res = StoredEarlyBinder::bind(
+ type_alias_data
+ .ty
+ .map(|type_ref| ctx.lower_ty(type_ref))
+ .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed))
+ .store(),
+ );
+ TyLoweringResult::from_ctx(res, ctx)
}
}
+pub(crate) fn type_for_type_alias_with_diagnostics_cycle_result(
+ db: &dyn HirDatabase,
+ _: salsa::Id,
+ _adt: TypeAliasId,
+) -> TyLoweringResult<StoredEarlyBinder<StoredTy>> {
+ TyLoweringResult::empty(StoredEarlyBinder::bind(
+ Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(),
+ ))
+}
+
pub(crate) fn impl_self_ty_query<'db>(
db: &'db dyn HirDatabase,
impl_id: ImplId,
) -> EarlyBinder<'db, Ty<'db>> {
- db.impl_self_ty_with_diagnostics(impl_id).0
+ impl_self_ty_with_diagnostics(db, impl_id).value.get()
}
-pub(crate) fn impl_self_ty_with_diagnostics<'db>(
- db: &'db dyn HirDatabase,
+#[salsa::tracked(returns(ref), cycle_result = impl_self_ty_with_diagnostics_cycle_result)]
+pub(crate) fn impl_self_ty_with_diagnostics(
+ db: &dyn HirDatabase,
impl_id: ImplId,
-) -> (EarlyBinder<'db, Ty<'db>>, Diagnostics) {
- let (ty, diags) = impl_self_ty_with_diagnostics_query(db, impl_id);
- return (ty.get(), diags.clone());
-
- #[salsa::tracked(returns(ref), cycle_result = impl_self_ty_with_diagnostics_cycle_result)]
- pub(crate) fn impl_self_ty_with_diagnostics_query<'db>(
- db: &'db dyn HirDatabase,
- impl_id: ImplId,
- ) -> (StoredEarlyBinder<StoredTy>, Diagnostics) {
- let resolver = impl_id.resolver(db);
+) -> TyLoweringResult<StoredEarlyBinder<StoredTy>> {
+ let resolver = impl_id.resolver(db);
+ let generics = OnceCell::new();
+ let impl_data = ImplSignature::of(db, impl_id);
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ &impl_data.store,
+ ExpressionStoreOwnerId::Signature(impl_id.into()),
+ impl_id.into(),
+ &generics,
+ LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true },
+ );
+ let ty = ctx.lower_ty(impl_data.self_ty);
+ assert!(!ty.has_escaping_bound_vars());
+ TyLoweringResult::from_ctx(StoredEarlyBinder::bind(ty.store()), ctx)
+}
- let impl_data = ImplSignature::of(db, impl_id);
- let mut ctx = TyLoweringContext::new(
- db,
- &resolver,
- &impl_data.store,
- impl_id.into(),
- LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true },
- );
- let ty = ctx.lower_ty(impl_data.self_ty);
- assert!(!ty.has_escaping_bound_vars());
- (StoredEarlyBinder::bind(ty.store()), create_diagnostics(ctx.diagnostics))
- }
+pub(crate) fn impl_self_ty_with_diagnostics_cycle_result(
+ db: &dyn HirDatabase,
+ _: salsa::Id,
+ _impl_id: ImplId,
+) -> TyLoweringResult<StoredEarlyBinder<StoredTy>> {
+ TyLoweringResult::empty(StoredEarlyBinder::bind(
+ Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(),
+ ))
+}
- pub(crate) fn impl_self_ty_with_diagnostics_cycle_result(
- db: &dyn HirDatabase,
- _: salsa::Id,
- _impl_id: ImplId,
- ) -> (StoredEarlyBinder<StoredTy>, Diagnostics) {
- (
- StoredEarlyBinder::bind(
- Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(),
- ),
- None,
- )
+pub(crate) fn const_param_ty<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> {
+ let param_types = const_param_types(db, def.parent());
+ match param_types.get(def.local_id()) {
+ Some(ty) => ty.as_ref(),
+ None => Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed),
}
}
-pub(crate) fn const_param_ty_query<'db>(db: &'db dyn HirDatabase, def: ConstParamId) -> Ty<'db> {
- db.const_param_ty_with_diagnostics(def).0
+pub(crate) fn const_param_types(
+ db: &dyn HirDatabase,
+ def: GenericDefId,
+) -> &ArenaMap<LocalTypeOrConstParamId, StoredTy> {
+ &const_param_types_with_diagnostics(db, def).value
}
-// returns None if def is a type arg
-pub(crate) fn const_param_ty_with_diagnostics<'db>(
- db: &'db dyn HirDatabase,
- def: ConstParamId,
-) -> (Ty<'db>, Diagnostics) {
- let (ty, diags) = const_param_ty_with_diagnostics_query(db, (), def);
- return (ty.as_ref(), diags.clone());
-
- // FIXME: Make this query non-interned.
- #[salsa::tracked(returns(ref), cycle_result = const_param_ty_with_diagnostics_cycle_result)]
- pub(crate) fn const_param_ty_with_diagnostics_query<'db>(
- db: &'db dyn HirDatabase,
- _: (),
- def: ConstParamId,
- ) -> (StoredTy, Diagnostics) {
- let (parent_data, store) = GenericParams::with_store(db, def.parent());
- let data = &parent_data[def.local_id()];
- let resolver = def.parent().resolver(db);
- let interner = DbInterner::new_no_crate(db);
- let mut ctx = TyLoweringContext::new(
- db,
- &resolver,
- store,
- def.parent(),
- LifetimeElisionKind::AnonymousReportError,
- );
- let ty = match data {
- TypeOrConstParamData::TypeParamData(_) => {
- never!();
- Ty::new_error(interner, ErrorGuaranteed)
- }
- TypeOrConstParamData::ConstParamData(d) => ctx.lower_ty(d.ty),
- };
- (ty.store(), create_diagnostics(ctx.diagnostics))
+#[salsa::tracked(returns(ref), cycle_result = const_param_types_with_diagnostics_cycle_result)]
+pub(crate) fn const_param_types_with_diagnostics(
+ db: &dyn HirDatabase,
+ def: GenericDefId,
+) -> TyLoweringResult<ArenaMap<LocalTypeOrConstParamId, StoredTy>> {
+ let mut result = ArenaMap::new();
+ let (data, store) = GenericParams::with_store(db, def);
+ let resolver = def.resolver(db);
+ let generics = OnceCell::new();
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ store,
+ ExpressionStoreOwnerId::Signature(def),
+ def,
+ &generics,
+ LifetimeElisionKind::AnonymousReportError,
+ );
+ ctx.forbid_params_after(0, ForbidParamsAfterReason::ConstParamTy);
+ for (local_id, param_data) in data.iter_type_or_consts() {
+ if let TypeOrConstParamData::ConstParamData(param_data) = param_data {
+ result.insert(local_id, ctx.lower_ty(param_data.ty).store());
+ }
}
+ result.shrink_to_fit();
+ TyLoweringResult::from_ctx(result, ctx)
+}
- pub(crate) fn const_param_ty_with_diagnostics_cycle_result(
- db: &dyn HirDatabase,
- _: salsa::Id,
- _: (),
- _def: ConstParamId,
- ) -> (StoredTy, Diagnostics) {
- let interner = DbInterner::new_no_crate(db);
- (Ty::new_error(interner, ErrorGuaranteed).store(), None)
- }
+fn const_param_types_with_diagnostics_cycle_result(
+ _db: &dyn HirDatabase,
+ _: salsa::Id,
+ _def: GenericDefId,
+) -> TyLoweringResult<ArenaMap<LocalTypeOrConstParamId, StoredTy>> {
+ TyLoweringResult::empty(ArenaMap::default())
}
pub(crate) fn field_types_query(
db: &dyn HirDatabase,
variant_id: VariantId,
) -> &ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>> {
- &db.field_types_with_diagnostics(variant_id).0
+ &field_types_with_diagnostics(db, variant_id).value
}
/// Build the type of all specific fields of a struct or enum variant.
#[salsa::tracked(returns(ref))]
-pub(crate) fn field_types_with_diagnostics_query<'db>(
- db: &'db dyn HirDatabase,
+pub(crate) fn field_types_with_diagnostics(
+ db: &dyn HirDatabase,
variant_id: VariantId,
-) -> (ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>>, Diagnostics) {
+) -> TyLoweringResult<ArenaMap<LocalFieldId, StoredEarlyBinder<StoredTy>>> {
let var_data = variant_id.fields(db);
let fields = var_data.fields();
if fields.is_empty() {
- return (ArenaMap::default(), None);
+ return TyLoweringResult::empty(ArenaMap::default());
}
- let (resolver, def): (_, GenericDefId) = match variant_id {
+ let (resolver, generic_def): (_, GenericDefId) = match variant_id {
VariantId::StructId(it) => (it.resolver(db), it.into()),
VariantId::UnionId(it) => (it.resolver(db), it.into()),
VariantId::EnumVariantId(it) => (it.resolver(db), it.lookup(db).parent.into()),
};
+ let generics = OnceCell::new();
let mut res = ArenaMap::default();
let mut ctx = TyLoweringContext::new(
db,
&resolver,
&var_data.store,
- def,
+ ExpressionStoreOwnerId::VariantFields(variant_id),
+ generic_def,
+ &generics,
LifetimeElisionKind::AnonymousReportError,
);
for (field_id, field_data) in var_data.fields().iter() {
res.insert(field_id, StoredEarlyBinder::bind(ctx.lower_ty(field_data.type_ref).store()));
}
- (res, create_diagnostics(ctx.diagnostics))
+ TyLoweringResult::from_ctx(res, ctx)
}
#[derive(Debug, PartialEq, Eq, Default)]
@@ -1776,20 +1803,21 @@ fn resolve_type_param_assoc_type_shorthand(
assoc_name: Name,
) -> AssocTypeShorthandResolution {
let generics = generics(db, def);
+ let store = generics.store();
+ let generics = &OnceCell::from(generics);
let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(
db,
&resolver,
- generics.store(),
+ store,
+ ExpressionStoreOwnerId::Signature(def),
def,
+ generics,
LifetimeElisionKind::AnonymousReportError,
);
let interner = ctx.interner;
- let param_ty = Ty::new_param(
- interner,
- param,
- generics.type_or_const_param_idx(param.into()).unwrap() as u32,
- );
+ let generics = generics.get().unwrap();
+ let param_ty = Ty::new_param(interner, param, generics.type_or_const_param_idx(param.into()));
let mut this_trait_resolution = None;
if let GenericDefId::TraitId(containing_trait) = param.parent()
@@ -1805,9 +1833,7 @@ fn resolve_type_param_assoc_type_shorthand(
}
let mut supertraits_resolution = None;
- for maybe_parent_generics in
- std::iter::successors(Some(&generics), |generics| generics.parent_generics())
- {
+ for maybe_parent_generics in generics.iter_owners().rev() {
ctx.store = maybe_parent_generics.store();
for pred in maybe_parent_generics.where_predicates() {
let (WherePredicate::TypeBound { target, bound }
@@ -1869,7 +1895,9 @@ fn resolve_type_param_assoc_type_shorthand(
let (assoc_type, args) = assoc_type_and_args
.get_with(|(assoc_type, args)| (*assoc_type, args.as_ref()))
.skip_binder();
- let args = EarlyBinder::bind(args).instantiate(interner, bounded_trait_ref.args);
+ let args = EarlyBinder::bind(args)
+ .instantiate(interner, bounded_trait_ref.args)
+ .skip_norm_wip();
let current_result = StoredEarlyBinder::bind((assoc_type, args.store()));
if let Some(this_trait_resolution) = &this_trait_resolution {
if *this_trait_resolution == current_result {
@@ -1927,7 +1955,11 @@ pub(crate) fn type_alias_bounds<'db>(
db: &'db dyn HirDatabase,
type_alias: TypeAliasId,
) -> EarlyBinder<'db, &'db [Clause<'db>]> {
- type_alias_bounds_with_diagnostics(db, type_alias).0.predicates.map_bound(|it| it.as_slice())
+ type_alias_bounds_with_diagnostics(db, type_alias)
+ .value
+ .predicates
+ .get()
+ .map_bound(|it| it.as_slice())
}
#[inline]
@@ -1935,89 +1967,74 @@ pub(crate) fn type_alias_self_bounds<'db>(
db: &'db dyn HirDatabase,
type_alias: TypeAliasId,
) -> EarlyBinder<'db, &'db [Clause<'db>]> {
- let (TypeAliasBounds { predicates, assoc_ty_bounds_start }, _) =
- type_alias_bounds_with_diagnostics(db, type_alias);
- predicates.map_bound(|it| &it.as_slice()[..assoc_ty_bounds_start as usize])
+ let TypeAliasBounds { predicates, assoc_ty_bounds_start } =
+ &type_alias_bounds_with_diagnostics(db, type_alias).value;
+ predicates.get().map_bound(|it| &it.as_slice()[..*assoc_ty_bounds_start as usize])
}
#[derive(PartialEq, Eq, Debug, Hash)]
-struct TypeAliasBounds<T> {
+pub struct TypeAliasBounds<T> {
predicates: T,
assoc_ty_bounds_start: u32,
}
-fn type_alias_bounds_with_diagnostics<'db>(
- db: &'db dyn HirDatabase,
+#[salsa::tracked(returns(ref))]
+pub(crate) fn type_alias_bounds_with_diagnostics(
+ db: &dyn HirDatabase,
type_alias: TypeAliasId,
-) -> (TypeAliasBounds<EarlyBinder<'db, Clauses<'db>>>, Diagnostics) {
- let (TypeAliasBounds { predicates, assoc_ty_bounds_start }, diags) =
- type_alias_bounds_with_diagnostics_query(db, type_alias);
- return (
- TypeAliasBounds {
- predicates: predicates.get(),
- assoc_ty_bounds_start: *assoc_ty_bounds_start,
- },
- diags.clone(),
+) -> TyLoweringResult<TypeAliasBounds<StoredEarlyBinder<StoredClauses>>> {
+ let type_alias_data = TypeAliasSignature::of(db, type_alias);
+ let resolver = type_alias.resolver(db);
+ let generics = OnceCell::new();
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ &type_alias_data.store,
+ ExpressionStoreOwnerId::Signature(type_alias.into()),
+ type_alias.into(),
+ &generics,
+ LifetimeElisionKind::AnonymousReportError,
);
+ let interner = ctx.interner;
- #[salsa::tracked(returns(ref))]
- pub fn type_alias_bounds_with_diagnostics_query<'db>(
- db: &'db dyn HirDatabase,
- type_alias: TypeAliasId,
- ) -> (TypeAliasBounds<StoredEarlyBinder<StoredClauses>>, Diagnostics) {
- let type_alias_data = TypeAliasSignature::of(db, type_alias);
- let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db);
- let mut ctx = TyLoweringContext::new(
- db,
- &resolver,
- &type_alias_data.store,
- type_alias.into(),
- LifetimeElisionKind::AnonymousReportError,
- );
- let interner = ctx.interner;
- let def_id = type_alias.into();
+ let item_args = GenericArgs::identity_for_item(interner, type_alias.into());
+ let interner_ty = Ty::new_projection_from_args(interner, type_alias.into(), item_args);
- let item_args = GenericArgs::identity_for_item(interner, def_id);
- let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args);
+ let mut bounds = Vec::new();
+ let mut assoc_ty_bounds = Vec::new();
+ for bound in &type_alias_data.bounds {
+ ctx.lower_type_bound(bound, interner_ty, false).for_each(|(pred, source)| match source {
+ GenericPredicateSource::SelfOnly => {
+ bounds.push(pred);
+ }
+ GenericPredicateSource::AssocTyBound => {
+ assoc_ty_bounds.push(pred);
+ }
+ });
+ }
- let mut bounds = Vec::new();
- let mut assoc_ty_bounds = Vec::new();
- for bound in &type_alias_data.bounds {
- ctx.lower_type_bound(bound, interner_ty, false).for_each(
- |(pred, source)| match source {
- GenericPredicateSource::SelfOnly => {
- bounds.push(pred);
- }
- GenericPredicateSource::AssocTyBound => {
- assoc_ty_bounds.push(pred);
- }
- },
+ if !ctx.unsized_types.contains(&interner_ty) {
+ let sized_trait = ctx.lang_items.Sized;
+ if let Some(sized_trait) = sized_trait {
+ let trait_ref = TraitRef::new_from_args(
+ interner,
+ sized_trait.into(),
+ GenericArgs::new_from_slice(&[interner_ty.into()]),
);
- }
-
- if !ctx.unsized_types.contains(&interner_ty) {
- let sized_trait = ctx.lang_items.Sized;
- if let Some(sized_trait) = sized_trait {
- let trait_ref = TraitRef::new_from_args(
- interner,
- sized_trait.into(),
- GenericArgs::new_from_slice(&[interner_ty.into()]),
- );
- bounds.push(trait_ref.upcast(interner));
- };
- }
+ bounds.push(trait_ref.upcast(interner));
+ };
+ }
- let assoc_ty_bounds_start = bounds.len() as u32;
- bounds.extend(assoc_ty_bounds);
+ let assoc_ty_bounds_start = bounds.len() as u32;
+ bounds.extend(assoc_ty_bounds);
- (
- TypeAliasBounds {
- predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&bounds).store()),
- assoc_ty_bounds_start,
- },
- create_diagnostics(ctx.diagnostics),
- )
- }
+ TyLoweringResult::from_ctx(
+ TypeAliasBounds {
+ predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&bounds).store()),
+ assoc_ty_bounds_start,
+ },
+ ctx,
+ )
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -2048,7 +2065,7 @@ impl<'db> GenericPredicates {
pub fn query_with_diagnostics(
db: &'db dyn HirDatabase,
def: GenericDefId,
- ) -> (GenericPredicates, Diagnostics) {
+ ) -> TyLoweringResult<GenericPredicates> {
generic_predicates(db, def)
}
}
@@ -2058,17 +2075,26 @@ fn generic_predicates_cycle_result(
_db: &dyn HirDatabase,
_: salsa::Id,
_def: GenericDefId,
-) -> (GenericPredicates, Diagnostics) {
- (
- GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
- Clauses::default().store(),
- )),
- None,
- )
+) -> TyLoweringResult<GenericPredicates> {
+ TyLoweringResult::empty(GenericPredicates::from_explicit_own_predicates(
+ StoredEarlyBinder::bind(Clauses::default().store()),
+ ))
}
impl GenericPredicates {
#[inline]
+ pub fn empty() -> &'static GenericPredicates {
+ static EMPTY: OnceLock<GenericPredicates> = OnceLock::new();
+ EMPTY.get_or_init(|| GenericPredicates {
+ predicates: StoredEarlyBinder::bind(Clauses::default().store()),
+ has_trait_implied_predicate: false,
+ parent_explicit_self_predicates_start: 0,
+ own_predicates_start: 0,
+ own_assoc_ty_bounds_start: 0,
+ })
+ }
+
+ #[inline]
pub(crate) fn from_explicit_own_predicates(
predicates: StoredEarlyBinder<StoredClauses>,
) -> Self {
@@ -2084,7 +2110,7 @@ impl GenericPredicates {
#[inline]
pub fn query(db: &dyn HirDatabase, def: GenericDefId) -> &GenericPredicates {
- &Self::query_with_diagnostics(db, def).0
+ &Self::query_with_diagnostics(db, def).value
}
#[inline]
@@ -2161,8 +2187,10 @@ pub(crate) fn param_env_from_predicates<'db>(
interner: DbInterner<'db>,
predicates: &'db GenericPredicates,
) -> ParamEnv<'db> {
- let clauses =
- rustc_type_ir::elaborate::elaborate(interner, predicates.all_predicates().iter_identity());
+ let clauses = rustc_type_ir::elaborate::elaborate(
+ interner,
+ predicates.all_predicates().iter_identity().map(Unnormalized::skip_norm_wip),
+ );
let clauses = Clauses::new_from_iter(interner, clauses);
// FIXME: We should normalize projections here, like rustc does.
@@ -2178,8 +2206,8 @@ pub(crate) fn trait_environment<'db>(
return ParamEnv { clauses: trait_environment_query(db, def).as_ref() };
#[salsa::tracked(returns(ref))]
- pub(crate) fn trait_environment_query<'db>(
- db: &'db dyn HirDatabase,
+ pub(crate) fn trait_environment_query(
+ db: &dyn HirDatabase,
def: GenericDefId,
) -> StoredClauses {
let module = def.module(db);
@@ -2192,17 +2220,25 @@ pub(crate) fn trait_environment<'db>(
/// Resolve the where clause(s) of an item with generics,
/// with a given filter
#[tracing::instrument(skip(db), ret)]
-fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredicates, Diagnostics) {
+fn generic_predicates(
+ db: &dyn HirDatabase,
+ def: GenericDefId,
+) -> TyLoweringResult<GenericPredicates> {
let generics = generics(db, def);
+ let store = generics.store();
+ let generics = &OnceCell::from(generics);
let resolver = def.resolver(db);
let interner = DbInterner::new_no_crate(db);
let mut ctx = TyLoweringContext::new(
db,
&resolver,
- generics.store(),
+ store,
+ ExpressionStoreOwnerId::Signature(def),
def,
+ generics,
LifetimeElisionKind::AnonymousReportError,
);
+ let generics = generics.get().unwrap();
let sized_trait = ctx.lang_items.Sized;
// We need to lower parents and self separately - see the comment below lowering of implicit
@@ -2211,16 +2247,13 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic
let mut parent_predicates = Vec::new();
let mut own_assoc_ty_bounds = Vec::new();
let mut parent_assoc_ty_bounds = Vec::new();
- let all_generics =
- std::iter::successors(Some(&generics), |generics| generics.parent_generics())
- .collect::<ArrayVec<_, 2>>();
let own_implicit_trait_predicate = implicit_trait_predicate(interner, def);
- let parent_implicit_trait_predicate = if all_generics.len() > 1 {
- implicit_trait_predicate(interner, all_generics.last().unwrap().def())
+ let parent_implicit_trait_predicate = if let Some(parent) = generics.parent() {
+ implicit_trait_predicate(interner, parent.def())
} else {
None
};
- for &maybe_parent_generics in all_generics.iter().rev() {
+ for maybe_parent_generics in generics.iter_owners() {
// Collect only diagnostics from the child, not including parents.
ctx.diagnostics.clear();
@@ -2291,12 +2324,9 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic
parent_predicates.push(clause);
}
};
- let parent_params_len = maybe_parent_generics.len_parent();
- maybe_parent_generics.iter_self().enumerate().for_each(
- |(param_idx, (param_id, param_data))| {
- add_sized_clause((param_idx + parent_params_len) as u32, param_id, param_data);
- },
- );
+ maybe_parent_generics.iter_with_idx().for_each(|(param_idx, param_id, param_data)| {
+ add_sized_clause(param_idx, param_id, param_data);
+ });
}
// We do not clear `ctx.unsized_types`, as the `?Sized` clause of a child (e.g. an associated type) can
@@ -2305,7 +2335,8 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic
// But we do have to lower the parent first.
}
- let diagnostics = create_diagnostics(ctx.diagnostics);
+ let diagnostics = mem::take(&mut ctx.diagnostics);
+ let defined_anon_consts = mem::take(&mut ctx.defined_anon_consts);
let predicates = parent_implicit_trait_predicate
.iter()
@@ -2331,7 +2362,7 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic
own_assoc_ty_bounds_start,
predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()),
};
- return (predicates, diagnostics);
+ return TyLoweringResult::new(predicates, diagnostics, defined_anon_consts);
fn implicit_trait_predicate<'db>(
interner: DbInterner<'db>,
@@ -2360,26 +2391,15 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic
fn push_const_arg_has_type_predicates<'db>(
db: &'db dyn HirDatabase,
predicates: &mut Vec<Clause<'db>>,
- generics: &Generics<'db>,
+ single_generics: &SingleGenerics<'db>,
) {
let interner = DbInterner::new_no_crate(db);
- let const_params_offset = generics.len_parent() + generics.len_lifetimes_self();
- for (param_index, (param_idx, param_data)) in generics.iter_self_type_or_consts().enumerate() {
- if !matches!(param_data, TypeOrConstParamData::ConstParamData(_)) {
- continue;
- }
-
- let param_id = ConstParamId::from_unchecked(TypeOrConstParamId {
- parent: generics.def(),
- local_id: param_idx,
- });
+ for (param_index, param_id, _) in single_generics.iter_with_idx() {
+ let GenericParamId::ConstParamId(param_id) = param_id else { continue };
predicates.push(Clause(
ClauseKind::ConstArgHasType(
- Const::new_param(
- interner,
- ParamConst { id: param_id, index: (param_index + const_params_offset) as u32 },
- ),
- db.const_param_ty_ns(param_id),
+ Const::new_param(interner, ParamConst { id: param_id, index: param_index }),
+ db.const_param_ty(param_id),
)
.upcast(interner),
));
@@ -2387,82 +2407,82 @@ fn push_const_arg_has_type_predicates<'db>(
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct GenericDefaults(Option<Arc<[Option<StoredEarlyBinder<StoredGenericArg>>]>>);
+pub struct GenericDefaults(ThinVec<Option<StoredEarlyBinder<StoredGenericArg>>>);
impl GenericDefaults {
#[inline]
- pub fn get<'db>(&self, idx: usize) -> Option<EarlyBinder<'db, GenericArg<'db>>> {
- Some(self.0.as_ref()?[idx].as_ref()?.get_with(|it| it.as_ref()))
+ pub fn as_ref(&self) -> GenericDefaultsRef<'_> {
+ GenericDefaultsRef(&self.0)
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct GenericDefaultsRef<'db>(&'db [Option<StoredEarlyBinder<StoredGenericArg>>]);
+
+impl<'db> GenericDefaultsRef<'db> {
+ #[inline]
+ pub fn get(self, idx: usize) -> Option<EarlyBinder<'db, GenericArg<'db>>> {
+ Some(self.0.get(idx)?.as_ref()?.get())
}
}
-pub(crate) fn generic_defaults_query(db: &dyn HirDatabase, def: GenericDefId) -> GenericDefaults {
- db.generic_defaults_with_diagnostics(def).0
+pub(crate) fn generic_defaults(db: &dyn HirDatabase, def: GenericDefId) -> GenericDefaultsRef<'_> {
+ generic_defaults_with_diagnostics(db, def).value.as_ref()
}
/// Resolve the default type params from generics.
///
/// Diagnostics are only returned for this `GenericDefId` (returned defaults include parents).
-pub(crate) fn generic_defaults_with_diagnostics_query(
+#[salsa_macros::tracked(returns(ref), cycle_result = generic_defaults_with_diagnostics_cycle_result)]
+pub(crate) fn generic_defaults_with_diagnostics(
db: &dyn HirDatabase,
def: GenericDefId,
-) -> (GenericDefaults, Diagnostics) {
- let generic_params = generics(db, def);
- if generic_params.is_empty() {
- return (GenericDefaults(None), None);
+) -> TyLoweringResult<GenericDefaults> {
+ let generics = generics(db, def);
+ if generics.has_no_params() {
+ return TyLoweringResult::empty(GenericDefaults(ThinVec::new()));
}
let resolver = def.resolver(db);
- let store_for_self = generic_params.store();
+ let store_for_self = generics.store();
+ let generics = &OnceCell::from(generics);
let mut ctx = TyLoweringContext::new(
db,
&resolver,
store_for_self,
+ ExpressionStoreOwnerId::Signature(def),
def,
+ generics,
LifetimeElisionKind::AnonymousReportError,
)
.with_impl_trait_mode(ImplTraitLoweringMode::Disallowed);
- let mut idx = 0;
- let mut has_any_default = false;
- let mut defaults = generic_params
- .iter_parents_with_store()
- .map(|((_id, p), store)| {
- ctx.store = store;
- let (result, has_default) = handle_generic_param(&mut ctx, idx, p);
- has_any_default |= has_default;
- idx += 1;
- result
- })
- .collect::<Vec<_>>();
+ let generics = generics.get().unwrap();
+ let mut defaults = ThinVec::new();
+ if let Some(parent) = generics.parent() {
+ ctx.store = parent.store();
+ defaults.extend(
+ parent.iter_with_idx().map(|(idx, _id, p)| handle_generic_param(&mut ctx, idx, p)),
+ );
+ }
ctx.diagnostics.clear(); // Don't include diagnostics from the parent.
+ ctx.defined_anon_consts.clear();
ctx.store = store_for_self;
- defaults.extend(generic_params.iter_self().map(|(_id, p)| {
- let (result, has_default) = handle_generic_param(&mut ctx, idx, p);
- has_any_default |= has_default;
- idx += 1;
- result
- }));
- let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics));
- let defaults = if has_any_default {
- GenericDefaults(Some(Arc::from_iter(defaults)))
- } else {
- GenericDefaults(None)
- };
- return (defaults, diagnostics);
+ defaults.extend(
+ generics.iter_self_with_idx().map(|(idx, _id, p)| handle_generic_param(&mut ctx, idx, p)),
+ );
+ defaults.shrink_to_fit();
+ return TyLoweringResult::from_ctx(GenericDefaults(defaults), ctx);
fn handle_generic_param<'db>(
ctx: &mut TyLoweringContext<'db, '_>,
- idx: usize,
+ idx: u32,
p: GenericParamDataRef<'_>,
- ) -> (Option<StoredEarlyBinder<StoredGenericArg>>, bool) {
- ctx.lowering_param_default(idx as u32);
+ ) -> Option<StoredEarlyBinder<StoredGenericArg>> {
+ ctx.forbid_params_after(idx, ForbidParamsAfterReason::LoweringParamDefault);
match p {
GenericParamDataRef::TypeParamData(p) => {
let ty = p.default.map(|ty| ctx.lower_ty(ty));
- (
- ty.map(|ty| StoredEarlyBinder::bind(GenericArg::from(ty).store())),
- p.default.is_some(),
- )
+ ty.map(|ty| StoredEarlyBinder::bind(GenericArg::from(ty).store()))
}
GenericParamDataRef::ConstParamData(p) => {
let val = p.default.map(|c| {
@@ -2470,19 +2490,19 @@ pub(crate) fn generic_defaults_with_diagnostics_query(
let c = ctx.lower_const(c, param_ty);
GenericArg::from(c).store()
});
- (val.map(StoredEarlyBinder::bind), p.default.is_some())
+ val.map(StoredEarlyBinder::bind)
}
- GenericParamDataRef::LifetimeParamData(_) => (None, false),
+ GenericParamDataRef::LifetimeParamData(_) => None,
}
}
}
-pub(crate) fn generic_defaults_with_diagnostics_cycle_result(
+fn generic_defaults_with_diagnostics_cycle_result(
_db: &dyn HirDatabase,
_: salsa::Id,
_def: GenericDefId,
-) -> (GenericDefaults, Diagnostics) {
- (GenericDefaults(None), None)
+) -> TyLoweringResult<GenericDefaults> {
+ TyLoweringResult::empty(GenericDefaults(ThinVec::new()))
}
/// Build the signature of a callable item (function, struct or enum variant).
@@ -2490,64 +2510,79 @@ pub(crate) fn callable_item_signature<'db>(
db: &'db dyn HirDatabase,
def: CallableDefId,
) -> EarlyBinder<'db, PolyFnSig<'db>> {
- return callable_item_signature_query(db, def).get_with(|sig| sig.get());
+ callable_item_signature_with_diagnostics(db, def).value.get()
+}
- #[salsa::tracked(returns(ref))]
- pub(crate) fn callable_item_signature_query<'db>(
- db: &'db dyn HirDatabase,
- def: CallableDefId,
- ) -> StoredEarlyBinder<StoredPolyFnSig> {
- match def {
- CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f),
- CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s),
- CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e),
+#[salsa::tracked(returns(ref))]
+pub(crate) fn callable_item_signature_with_diagnostics(
+ db: &dyn HirDatabase,
+ def: CallableDefId,
+) -> TyLoweringResult<StoredEarlyBinder<StoredPolyFnSig>> {
+ match def {
+ CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f),
+ CallableDefId::StructId(s) => TyLoweringResult::empty(fn_sig_for_struct_constructor(db, s)),
+ CallableDefId::EnumVariantId(e) => {
+ TyLoweringResult::empty(fn_sig_for_enum_variant_constructor(db, e))
}
}
}
-fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> StoredEarlyBinder<StoredPolyFnSig> {
+fn fn_sig_for_fn(
+ db: &dyn HirDatabase,
+ def: FunctionId,
+) -> TyLoweringResult<StoredEarlyBinder<StoredPolyFnSig>> {
let data = FunctionSignature::of(db, def);
let resolver = def.resolver(db);
let interner = DbInterner::new_no_crate(db);
+ let generics = OnceCell::new();
let mut ctx_params = TyLoweringContext::new(
db,
&resolver,
&data.store,
+ ExpressionStoreOwnerId::Signature(def.into()),
def.into(),
+ &generics,
LifetimeElisionKind::for_fn_params(data),
);
let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr));
+ let mut ctx_ret = TyLoweringContext::new(
+ db,
+ &resolver,
+ &data.store,
+ ExpressionStoreOwnerId::Signature(def.into()),
+ def.into(),
+ &generics,
+ LifetimeElisionKind::for_fn_ret(interner),
+ )
+ .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
let ret = match data.ret_type {
- Some(ret_type) => {
- let mut ctx_ret = TyLoweringContext::new(
- db,
- &resolver,
- &data.store,
- def.into(),
- LifetimeElisionKind::for_fn_ret(interner),
- )
- .with_impl_trait_mode(ImplTraitLoweringMode::Opaque);
- ctx_ret.lower_ty(ret_type)
- }
- None => Ty::new_tup(interner, &[]),
+ Some(ret_type) => ctx_ret.lower_ty(ret_type),
+ None => Ty::new_unit(interner),
};
let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret)));
+
+ ctx_params.diagnostics.extend(ctx_ret.diagnostics);
+ ctx_params.defined_anon_consts.extend(ctx_ret.defined_anon_consts);
+
// If/when we track late bound vars, we need to switch this to not be `dummy`
- StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig {
- abi: data.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol),
- c_variadic: data.is_varargs(),
- safety: if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe },
+ let result = StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig {
inputs_and_output,
- })))
+ fn_sig_kind: FnSigKind::new(
+ data.abi,
+ if data.is_unsafe() { Safety::Unsafe } else { Safety::Safe },
+ data.is_varargs(),
+ ),
+ })));
+ TyLoweringResult::from_ctx(result, ctx_params)
}
-fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> StoredEarlyBinder<StoredTy> {
+fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> {
let interner = DbInterner::new_no_crate(db);
let args = GenericArgs::identity_for_item(interner, adt.into());
let ty = Ty::new_adt(interner, adt, args);
- StoredEarlyBinder::bind(ty.store())
+ EarlyBinder::bind(ty)
}
fn fn_sig_for_struct_constructor(
@@ -2559,11 +2594,9 @@ fn fn_sig_for_struct_constructor(
let ret = type_for_adt(db, def.into()).skip_binder();
let inputs_and_output =
- Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret.as_ref())));
+ Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret)));
StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig {
- abi: FnAbi::Rust,
- c_variadic: false,
- safety: Safety::Safe,
+ fn_sig_kind: FnSigKind::new(ExternAbi::Rust, Safety::Safe, false),
inputs_and_output,
})))
}
@@ -2578,28 +2611,29 @@ fn fn_sig_for_enum_variant_constructor(
let ret = type_for_adt(db, parent.into()).skip_binder();
let inputs_and_output =
- Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret.as_ref())));
+ Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret)));
StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig {
- abi: FnAbi::Rust,
- c_variadic: false,
- safety: Safety::Safe,
+ fn_sig_kind: FnSigKind::new(ExternAbi::Rust, Safety::Safe, false),
inputs_and_output,
})))
}
-// FIXME(next-solver): should merge this with `explicit_item_bounds` in some way
+// FIXME: Remove this.
pub(crate) fn associated_ty_item_bounds<'db>(
db: &'db dyn HirDatabase,
type_alias: TypeAliasId,
) -> EarlyBinder<'db, BoundExistentialPredicates<'db>> {
let type_alias_data = TypeAliasSignature::of(db, type_alias);
- let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db);
+ let resolver = type_alias.resolver(db);
let interner = DbInterner::new_no_crate(db);
+ let generics = OnceCell::new();
let mut ctx = TyLoweringContext::new(
db,
&resolver,
&type_alias_data.store,
+ ExpressionStoreOwnerId::Signature(type_alias.into()),
type_alias.into(),
+ &generics,
LifetimeElisionKind::AnonymousReportError,
);
// FIXME: we should never create non-existential predicates in the first place
@@ -2682,5 +2716,8 @@ pub(crate) fn associated_type_by_name_including_super_traits_allow_ambiguity<'db
.get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref()))
.skip_binder();
let interner = DbInterner::new_no_crate(db);
- Some((assoc_type, EarlyBinder::bind(trait_args).instantiate(interner, trait_ref.args)))
+ Some((
+ assoc_type,
+ EarlyBinder::bind(trait_args).instantiate(interner, trait_ref.args).skip_norm_wip(),
+ ))
}
diff --git a/crates/hir-ty/src/lower/diagnostics.rs b/crates/hir-ty/src/lower/diagnostics.rs
index 009f047109..2565fb46ce 100644
--- a/crates/hir-ty/src/lower/diagnostics.rs
+++ b/crates/hir-ty/src/lower/diagnostics.rs
@@ -81,6 +81,11 @@ pub enum PathLoweringDiagnostic {
def: GenericDefId,
expected_count: u32,
},
+ /// Generic defaults are not allowed to refer to `Self`.
+ GenericDefaultRefersToSelf {
+ /// Index of the `Self` segment.
+ segment: u32,
+ },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs
index 4f70732178..ff9718af11 100644
--- a/crates/hir-ty/src/lower/path.rs
+++ b/crates/hir-ty/src/lower/path.rs
@@ -22,27 +22,26 @@ use rustc_type_ir::{
inherent::{GenericArgs as _, Region as _, Ty as _},
};
use smallvec::SmallVec;
-use stdx::never;
use crate::{
GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource,
- PathLoweringDiagnostic, TyDefId, ValueTyDefId,
- consteval::{unknown_const, unknown_const_as_generic},
+ PathLoweringDiagnostic, Span, TyDefId, ValueTyDefId,
db::HirDatabase,
generics::{Generics, generics},
+ infer::unify::InferenceTable,
lower::{
- AssocTypeShorthandResolution, GenericPredicateSource, LifetimeElisionKind,
- PathDiagnosticCallbackData,
+ AssocTypeShorthandResolution, ForbidParamsAfterReason, GenericPredicateSource,
+ LifetimeElisionKind, PathDiagnosticCallbackData, const_param_ty,
},
next_solver::{
- Binder, Clause, Const, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg, GenericArgs,
- Predicate, ProjectionPredicate, Region, TraitRef, Ty,
+ AliasTermKind, Binder, Clause, Const, DbInterner, EarlyBinder, ErrorGuaranteed, GenericArg,
+ GenericArgs, Predicate, ProjectionPredicate, Region, TraitRef, Ty,
},
};
use super::{
ImplTraitLoweringMode, TyLoweringContext,
- associated_type_by_name_including_super_traits_allow_ambiguity, const_param_ty_query, ty_query,
+ associated_type_by_name_including_super_traits_allow_ambiguity, ty_query,
};
type CallbackData<'a> =
@@ -85,6 +84,11 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
}
}
+ #[track_caller]
+ pub(crate) fn expect_table(&mut self) -> &mut InferenceTable<'db> {
+ self.ctx.expect_table()
+ }
+
#[inline]
#[cold]
fn on_diagnostic(&mut self, diag: PathLoweringDiagnostic) {
@@ -150,17 +154,18 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
// We need the original resolution to lower `Self::AssocTy` correctly
res: Option<TypeNs>,
infer_args: bool,
+ span: Span,
) -> (Ty<'db>, Option<TypeNs>) {
let remaining_segments = self.segments.len() - self.current_segment_idx;
match remaining_segments {
0 => (ty, res),
1 => {
// resolve unselected assoc types
- (self.select_associated_type(res, infer_args), None)
+ (self.select_associated_type(res, infer_args, span), None)
}
_ => {
// FIXME report error (ambiguous associated type)
- (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None)
+ (self.ctx.types.types.error, None)
}
}
}
@@ -170,6 +175,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
&mut self,
resolution: TypeNs,
infer_args: bool,
+ span: Span,
) -> (Ty<'db>, Option<TypeNs>) {
let remaining_segments = self.segments.skip(self.current_segment_idx + 1);
tracing::debug!(?remaining_segments);
@@ -182,8 +188,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
1 => {
let trait_ref = self.lower_trait_ref_from_resolved_path(
trait_,
- Ty::new_error(self.ctx.interner, ErrorGuaranteed),
+ self.ctx.types.types.error,
infer_args,
+ span,
);
tracing::debug!(?trait_ref);
self.skip_resolved_segment();
@@ -204,6 +211,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
infer_args,
None,
true,
+ span,
);
let args = GenericArgs::new_from_iter(
self.ctx.interner,
@@ -223,7 +231,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
}
None => {
// FIXME: report error (associated type not found)
- Ty::new_error(self.ctx.interner, ErrorGuaranteed)
+ self.ctx.types.types.error
}
}
}
@@ -231,11 +239,11 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
// Trait object type without dyn; this should be handled in upstream. See
// `lower_path()`.
stdx::never!("unexpected fully resolved trait path");
- Ty::new_error(self.ctx.interner, ErrorGuaranteed)
+ self.ctx.types.types.error
}
_ => {
// FIXME report error (ambiguous associated type)
- Ty::new_error(self.ctx.interner, ErrorGuaranteed)
+ self.ctx.types.types.error
}
};
return (ty, None);
@@ -243,17 +251,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
TypeNs::GenericParam(param_id) => {
let generics = self.ctx.generics();
let idx = generics.type_or_const_param_idx(param_id.into());
- match idx {
- None => {
- never!("no matching generics");
- Ty::new_error(self.ctx.interner, ErrorGuaranteed)
- }
- Some(idx) => {
- let (pidx, _param) = generics.iter().nth(idx).unwrap();
- assert_eq!(pidx, param_id.into());
- self.ctx.type_param(param_id, idx as u32)
- }
- }
+ self.ctx.type_param(param_id, idx)
}
TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty(impl_id).skip_binder(),
TypeNs::AdtSelfType(adt) => {
@@ -261,19 +259,19 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
Ty::new_adt(self.ctx.interner, adt, args)
}
- TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args),
- TypeNs::BuiltinType(it) => self.lower_path_inner(it.into(), infer_args),
- TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args),
+ TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args, span),
+ TypeNs::BuiltinType(it) => self.lower_path_inner(it.into(), infer_args, span),
+ TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args, span),
// FIXME: report error
TypeNs::EnumVariantId(_) | TypeNs::ModuleId(_) => {
- return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None);
+ return (self.ctx.types.types.error, None);
}
};
tracing::debug!(?ty);
self.skip_resolved_segment();
- self.lower_ty_relative_path(ty, Some(resolution), infer_args)
+ self.lower_ty_relative_path(ty, Some(resolution), infer_args, span)
}
/// This returns whether to keep the resolution (`true`) of throw it (`false`).
@@ -299,9 +297,15 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
TypeNs::AdtSelfType(_) => {
prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy);
- if self.ctx.lowering_param_default.is_some() {
- // Generic defaults are not allowed to refer to `Self`.
- // FIXME: Emit an error.
+ if self.ctx.forbid_params_after.is_some()
+ && self.ctx.forbid_params_after_reason
+ == ForbidParamsAfterReason::LoweringParamDefault
+ {
+ // FIXME: Handle other reasons.
+ let segment = self.current_segment_u32();
+ self.on_diagnostic(PathLoweringDiagnostic::GenericDefaultRefersToSelf {
+ segment,
+ });
return false;
}
}
@@ -475,13 +479,17 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
}
#[tracing::instrument(skip(self), ret)]
- fn select_associated_type(&mut self, res: Option<TypeNs>, infer_args: bool) -> Ty<'db> {
+ fn select_associated_type(
+ &mut self,
+ res: Option<TypeNs>,
+ infer_args: bool,
+ span: Span,
+ ) -> Ty<'db> {
let interner = self.ctx.interner;
let db = self.ctx.db;
- let def = self.ctx.def;
+ let def = self.ctx.generic_def;
let segment = self.current_or_prev_segment;
let assoc_name = segment.name;
- let error_ty = || Ty::new_error(self.ctx.interner, ErrorGuaranteed);
let (assoc_type, trait_args) = match res {
Some(TypeNs::GenericParam(param)) => {
let AssocTypeShorthandResolution::Resolved(assoc_type) =
@@ -492,7 +500,8 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
assoc_name.clone(),
)
else {
- return error_ty();
+ // FIXME: Emit an error.
+ return self.ctx.types.types.error;
};
assoc_type
.get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref()))
@@ -500,9 +509,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
}
Some(TypeNs::SelfType(impl_)) => {
let Some(impl_trait) = db.impl_trait(impl_) else {
- return error_ty();
+ return self.ctx.types.types.error;
};
- let impl_trait = impl_trait.instantiate_identity();
+ let impl_trait = impl_trait.instantiate_identity().skip_norm_wip();
// Searching for `Self::Assoc` in `impl Trait for Type` is like searching for `Self::Assoc` in `Trait`.
let AssocTypeShorthandResolution::Resolved(assoc_type) =
super::resolve_type_param_assoc_type_shorthand(
@@ -512,21 +521,27 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
assoc_name.clone(),
)
else {
- return error_ty();
+ // FIXME: Emit an error.
+ return self.ctx.types.types.error;
};
let (assoc_type, trait_args) = assoc_type
.get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref()))
.skip_binder();
- (assoc_type, EarlyBinder::bind(trait_args).instantiate(interner, impl_trait.args))
+ (
+ assoc_type,
+ EarlyBinder::bind(trait_args)
+ .instantiate(interner, impl_trait.args)
+ .skip_norm_wip(),
+ )
}
- _ => return error_ty(),
+ _ => return self.ctx.types.types.error,
};
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`t.substitution`).
- let substs = self.substs_from_path_segment(assoc_type.into(), infer_args, None, true);
+ let substs = self.substs_from_path_segment(assoc_type.into(), infer_args, None, true, span);
let substs = GenericArgs::new_from_iter(
interner,
@@ -536,7 +551,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
Ty::new_projection_from_args(interner, assoc_type.into(), substs)
}
- fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> {
+ fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool, span: Span) -> Ty<'db> {
let generic_def = match typeable {
TyDefId::BuiltinType(builtinty) => {
return Ty::from_builtin_type(self.ctx.interner, builtinty);
@@ -544,9 +559,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
TyDefId::AdtId(it) => it.into(),
TyDefId::TypeAliasId(it) => it.into(),
};
- let args = self.substs_from_path_segment(generic_def, infer_args, None, false);
+ let args = self.substs_from_path_segment(generic_def, infer_args, None, false, span);
let ty = ty_query(self.ctx.db, typeable);
- ty.instantiate(self.ctx.interner, args)
+ ty.instantiate(self.ctx.interner, args).skip_norm_wip()
}
/// Collect generic arguments from a path into a `Substs`. See also
@@ -559,6 +574,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
resolved: ValueTyDefId,
infer_args: bool,
lowering_assoc_type_generics: bool,
+ span: Span,
) -> GenericArgs<'db> {
let interner = self.ctx.interner;
let prev_current_segment_idx = self.current_segment_idx;
@@ -602,6 +618,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
infer_args,
None,
lowering_assoc_type_generics,
+ span,
);
self.current_segment_idx = prev_current_segment_idx;
self.current_or_prev_segment = prev_current_segment;
@@ -614,6 +631,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
infer_args: bool,
explicit_self_ty: Option<Ty<'db>>,
lowering_assoc_type_generics: bool,
+ span: Span,
) -> GenericArgs<'db> {
let old_lifetime_elision = self.ctx.lifetime_elision;
@@ -638,7 +656,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment },
);
- return unknown_subst(self.ctx.interner, def);
+ return GenericArgs::error_for_item(self.ctx.interner, def.into());
}
// `Fn()`-style generics are treated like functions for the purpose of lifetime elision.
@@ -654,6 +672,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
PathGenericsSource::Segment(self.current_segment_u32()),
lowering_assoc_type_generics,
self.ctx.lifetime_elision,
+ span,
);
self.ctx.lifetime_elision = old_lifetime_elision;
result
@@ -668,10 +687,12 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
generics_source: PathGenericsSource,
lowering_assoc_type_generics: bool,
lifetime_elision: LifetimeElisionKind<'db>,
+ span: Span,
) -> GenericArgs<'db> {
struct LowererCtx<'a, 'b, 'c, 'db> {
ctx: &'a mut PathLoweringContext<'b, 'c, 'db>,
generics_source: PathGenericsSource,
+ span: Span,
}
impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, '_, 'db> {
@@ -725,7 +746,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
};
self.ctx
.ctx
- .lower_const(konst, const_param_ty_query(self.ctx.ctx.db, const_id))
+ .lower_const(konst, const_param_ty(self.ctx.ctx.db, const_id))
.into()
}
_ => unreachable!("unmatching param kinds were passed to `provided_kind()`"),
@@ -734,12 +755,13 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
fn provided_type_like_const(
&mut self,
+ type_ref: TypeRefId,
const_ty: Ty<'db>,
arg: TypeLikeConst<'_>,
) -> Const<'db> {
match arg {
TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty),
- TypeLikeConst::Infer => unknown_const(const_ty),
+ TypeLikeConst::Infer => self.ctx.ctx.next_const_var(type_ref.into()),
}
}
@@ -750,17 +772,18 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
param: GenericParamDataRef<'_>,
infer_args: bool,
preceding_args: &[GenericArg<'db>],
+ had_count_error: bool,
) -> GenericArg<'db> {
- let default =
- || {
- self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map(
- |default| default.instantiate(self.ctx.ctx.interner, preceding_args),
- )
- };
+ let default = || {
+ self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map(|default| {
+ default.instantiate(self.ctx.ctx.interner, preceding_args).skip_norm_wip()
+ })
+ };
+ // If `!infer_args`, we've already emitted an error, so put a dummy span.
+ let span = if !infer_args || had_count_error { Span::Dummy } else { self.span };
match param {
GenericParamDataRef::LifetimeParamData(_) => {
- Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed))
- .into()
+ self.ctx.ctx.next_region_var(span).into()
}
GenericParamDataRef::TypeParamData(param) => {
if !infer_args
@@ -769,7 +792,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
{
return default;
}
- Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into()
+ self.ctx.ctx.next_ty_var(span).into()
}
GenericParamDataRef::ConstParamData(param) => {
if !infer_args
@@ -778,10 +801,10 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
{
return default;
}
- let GenericParamId::ConstParamId(const_id) = param_id else {
+ let GenericParamId::ConstParamId(_) = param_id else {
unreachable!("non-const param ID for const param");
};
- unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id))
+ self.ctx.ctx.next_const_var(span).into()
}
}
}
@@ -791,13 +814,8 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
GenericParamId::TypeParamId(_) => {
Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into()
}
- GenericParamId::ConstParamId(const_id) => {
- unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id))
- }
- GenericParamId::LifetimeParamId(_) => {
- Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed))
- .into()
- }
+ GenericParamId::ConstParamId(_) => self.ctx.ctx.types.consts.error.into(),
+ GenericParamId::LifetimeParamId(_) => self.ctx.ctx.types.regions.error.into(),
}
}
@@ -841,7 +859,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
lifetime_elision,
lowering_assoc_type_generics,
explicit_self_ty,
- &mut LowererCtx { ctx: self, generics_source },
+ &mut LowererCtx { ctx: self, generics_source, span },
)
}
@@ -850,8 +868,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
resolved: TraitId,
explicit_self_ty: Ty<'db>,
infer_args: bool,
+ span: Span,
) -> TraitRef<'db> {
- let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty, infer_args);
+ let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty, infer_args, span);
TraitRef::new_from_args(self.ctx.interner, resolved.into(), args)
}
@@ -860,13 +879,21 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
resolved: TraitId,
explicit_self_ty: Ty<'db>,
infer_args: bool,
+ span: Span,
) -> GenericArgs<'db> {
- self.substs_from_path_segment(resolved.into(), infer_args, Some(explicit_self_ty), false)
+ self.substs_from_path_segment(
+ resolved.into(),
+ infer_args,
+ Some(explicit_self_ty),
+ false,
+ span,
+ )
}
pub(super) fn assoc_type_bindings_from_type_bound<'c>(
mut self,
trait_ref: TraitRef<'db>,
+ span: Span,
) -> Option<impl Iterator<Item = (Clause<'db>, GenericPredicateSource)> + use<'a, 'b, 'c, 'db>>
{
let interner = self.ctx.interner;
@@ -898,14 +925,18 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
},
false,
this.ctx.lifetime_elision,
+ span,
)
});
let args = GenericArgs::new_from_iter(
interner,
super_trait_args.iter().chain(args.iter().skip(super_trait_args.len())),
);
- let projection_term =
- AliasTerm::new_from_args(interner, associated_ty.into(), args);
+ let projection_term = AliasTerm::new_from_args(
+ interner,
+ AliasTermKind::ProjectionTy { def_id: associated_ty.into() },
+ args,
+ );
let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
);
@@ -1000,8 +1031,12 @@ pub(crate) trait GenericArgsLowerer<'db> {
arg: &HirGenericArg,
) -> GenericArg<'db>;
- fn provided_type_like_const(&mut self, const_ty: Ty<'db>, arg: TypeLikeConst<'_>)
- -> Const<'db>;
+ fn provided_type_like_const(
+ &mut self,
+ type_ref: TypeRefId,
+ const_ty: Ty<'db>,
+ arg: TypeLikeConst<'_>,
+ ) -> Const<'db>;
fn inferred_kind(
&mut self,
@@ -1010,6 +1045,7 @@ pub(crate) trait GenericArgsLowerer<'db> {
param: GenericParamDataRef<'_>,
infer_args: bool,
preceding_args: &[GenericArg<'db>],
+ had_count_error: bool,
) -> GenericArg<'db>;
fn parent_arg(&mut self, param_idx: u32, param_id: GenericParamId) -> GenericArg<'db>;
@@ -1178,7 +1214,14 @@ pub(crate) fn substs_from_args_and_bindings<'db>(
ctx.provided_kind(self_param_id, self_param, self_ty)
} else {
explicit_self_ty.map(|it| it.into()).unwrap_or_else(|| {
- ctx.inferred_kind(def, self_param_id, self_param, infer_args, &substs)
+ ctx.inferred_kind(
+ def,
+ self_param_id,
+ self_param,
+ infer_args,
+ &substs,
+ had_count_error,
+ )
})
};
params.next();
@@ -1197,7 +1240,14 @@ pub(crate) fn substs_from_args_and_bindings<'db>(
{
// Do not allow specifying `impl Trait` explicitly. We already err at that, but if we won't handle it here
// we will handle it as if it was specified, instead of inferring it.
- substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
+ substs.push(ctx.inferred_kind(
+ def,
+ param_id,
+ param,
+ infer_args,
+ &substs,
+ had_count_error,
+ ));
params.next();
}
(HirGenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_))
@@ -1213,7 +1263,14 @@ pub(crate) fn substs_from_args_and_bindings<'db>(
) => {
// We expected a lifetime argument, but got a type or const
// argument. That means we're inferring the lifetime.
- substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
+ substs.push(ctx.inferred_kind(
+ def,
+ param_id,
+ param,
+ infer_args,
+ &substs,
+ had_count_error,
+ ));
params.next();
force_infer_lt = Some((arg_idx as u32, param_id));
}
@@ -1222,8 +1279,9 @@ pub(crate) fn substs_from_args_and_bindings<'db>(
let GenericParamId::ConstParamId(param_id) = param_id else {
panic!("unmatching param kinds");
};
- let const_ty = const_param_ty_query(db, param_id);
- substs.push(ctx.provided_type_like_const(const_ty, konst).into());
+ let const_ty = const_param_ty(db, param_id);
+ substs
+ .push(ctx.provided_type_like_const(*type_ref, const_ty, konst).into());
args.next();
params.next();
} else {
@@ -1281,7 +1339,14 @@ pub(crate) fn substs_from_args_and_bindings<'db>(
| LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }
| LifetimeElisionKind::AnonymousReportError => {
assert!(had_count_error);
- ctx.inferred_kind(def, param_id, param, infer_args, &substs)
+ ctx.inferred_kind(
+ def,
+ param_id,
+ param,
+ infer_args,
+ &substs,
+ had_count_error,
+ )
}
LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => {
Region::new_static(interner).into()
@@ -1291,11 +1356,18 @@ pub(crate) fn substs_from_args_and_bindings<'db>(
| LifetimeElisionKind::Infer => {
// FIXME: With `AnonymousCreateParameter`, we need to create a new lifetime parameter here
// (but this will probably be done in hir-def lowering instead).
- ctx.inferred_kind(def, param_id, param, infer_args, &substs)
+ ctx.inferred_kind(
+ def,
+ param_id,
+ param,
+ infer_args,
+ &substs,
+ had_count_error,
+ )
}
}
} else {
- ctx.inferred_kind(def, param_id, param, infer_args, &substs)
+ ctx.inferred_kind(def, param_id, param, infer_args, &substs, had_count_error)
};
substs.push(param);
params.next();
@@ -1324,17 +1396,3 @@ fn type_looks_like_const(
_ => None,
}
}
-
-fn unknown_subst<'db>(interner: DbInterner<'db>, def: impl Into<GenericDefId>) -> GenericArgs<'db> {
- let params = generics(interner.db(), def.into());
- GenericArgs::new_from_iter(
- interner,
- params.iter_id().map(|id| match id {
- GenericParamId::TypeParamId(_) => Ty::new_error(interner, ErrorGuaranteed).into(),
- GenericParamId::ConstParamId(id) => {
- unknown_const_as_generic(const_param_ty_query(interner.db(), id))
- }
- GenericParamId::LifetimeParamId(_) => Region::error(interner).into(),
- }),
- )
-}
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 68c4833d81..5e90e371fc 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -23,8 +23,8 @@ use hir_def::{
nameres::{DefMap, block_def_map, crate_def_map},
resolver::Resolver,
signatures::{ConstSignature, FunctionSignature},
+ unstable_features::UnstableFeatures,
};
-use intern::{Symbol, sym};
use rustc_hash::{FxHashMap, FxHashSet};
use rustc_type_ir::{
TypeVisitableExt,
@@ -35,13 +35,13 @@ use stdx::impl_from;
use triomphe::Arc;
use crate::{
- all_super_traits,
+ Span, all_super_traits,
db::HirDatabase,
infer::{InferenceContext, unify::InferenceTable},
lower::GenericPredicates,
next_solver::{
AnyImplId, Binder, ClauseKind, DbInterner, FnSig, GenericArgs, ParamEnv, PredicateKind,
- SimplifiedType, SolverDefId, TraitRef, Ty, TyKind, TypingMode,
+ SimplifiedType, SolverDefId, TraitRef, Ty, TyKind, TypingMode, Unnormalized,
infer::{
BoundRegionConversionTime, DbInternerInferExt, InferCtxt, InferOk,
select::ImplSource,
@@ -57,32 +57,15 @@ pub use self::probe::{
Candidate, CandidateKind, CandidateStep, CandidateWithPrivate, Mode, Pick, PickKind,
};
-#[derive(Debug, Clone)]
-pub struct MethodResolutionUnstableFeatures {
- arbitrary_self_types: bool,
- arbitrary_self_types_pointers: bool,
- supertrait_item_shadowing: bool,
-}
-
-impl MethodResolutionUnstableFeatures {
- pub fn from_def_map(def_map: &DefMap) -> Self {
- Self {
- arbitrary_self_types: def_map.is_unstable_feature_enabled(&sym::arbitrary_self_types),
- arbitrary_self_types_pointers: def_map
- .is_unstable_feature_enabled(&sym::arbitrary_self_types_pointers),
- supertrait_item_shadowing: def_map
- .is_unstable_feature_enabled(&sym::supertrait_item_shadowing),
- }
- }
-}
-
pub struct MethodResolutionContext<'a, 'db> {
pub infcx: &'a InferCtxt<'db>,
pub resolver: &'a Resolver<'db>,
pub param_env: ParamEnv<'db>,
pub traits_in_scope: &'a FxHashSet<TraitId>,
pub edition: Edition,
- pub unstable_features: &'a MethodResolutionUnstableFeatures,
+ pub features: &'a UnstableFeatures,
+ pub call_span: Span,
+ pub receiver_span: Span,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)]
@@ -152,7 +135,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
receiver: ExprId,
call_expr: ExprId,
) -> Result<(MethodCallee<'db>, bool), MethodError<'db>> {
- let (pick, is_visible) = match self.lookup_probe(name, self_ty) {
+ let (pick, is_visible) = match self.lookup_probe(call_expr, receiver, name, self_ty) {
Ok(it) => (it, true),
Err(MethodError::PrivateMatch(it)) => {
// FIXME: Report error.
@@ -177,10 +160,12 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
#[instrument(level = "debug", skip(self))]
pub(crate) fn lookup_probe(
&self,
+ call_expr: ExprId,
+ receiver: ExprId,
method_name: Name,
self_ty: Ty<'db>,
) -> probe::PickResult<'db> {
- self.with_method_resolution(|ctx| {
+ self.with_method_resolution(call_expr.into(), receiver.into(), |ctx| {
let pick = ctx.probe_for_name(probe::Mode::MethodCall, method_name, self_ty)?;
Ok(pick)
})
@@ -188,6 +173,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
pub(crate) fn with_method_resolution<R>(
&self,
+ call_span: Span,
+ receiver_span: Span,
f: impl FnOnce(&MethodResolutionContext<'_, 'db>) -> R,
) -> R {
let traits_in_scope = self.get_traits_in_scope();
@@ -201,7 +188,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
param_env: self.table.param_env,
traits_in_scope,
edition: self.edition,
- unstable_features: &self.unstable_features,
+ features: self.features,
+ call_span,
+ receiver_span,
};
f(&ctx)
}
@@ -234,8 +223,8 @@ impl<'db> InferenceTable<'db> {
pub(super) fn lookup_method_for_operator(
&self,
cause: ObligationCause,
- method_name: Symbol,
trait_def_id: TraitId,
+ method_item: FunctionId,
self_ty: Ty<'db>,
opt_rhs_ty: Option<Ty<'db>>,
treat_opaques: TreatNotYetDefinedOpaques,
@@ -258,7 +247,7 @@ impl<'db> InferenceTable<'db> {
// FIXME: We should stop passing `None` for the failure case
// when probing for call exprs. I.e. `opt_rhs_ty` should always
// be set when it needs to be.
- self.next_var_for_param(param_id)
+ self.var_for_def(param_id, cause.span())
}
}
},
@@ -288,13 +277,6 @@ impl<'db> InferenceTable<'db> {
// Trait must have a method named `m_name` and it should not have
// type parameters or early-bound regions.
let interner = self.interner();
- // We use `Ident::with_dummy_span` since no built-in operator methods have
- // any macro-specific hygiene, so the span's context doesn't really matter.
- let Some(method_item) =
- trait_def_id.trait_items(self.db).method_by_name(&Name::new_symbol_root(method_name))
- else {
- panic!("expected associated item for operator trait")
- };
let def_id = method_item;
@@ -307,11 +289,16 @@ impl<'db> InferenceTable<'db> {
// N.B., instantiate late-bound regions before normalizing the
// function signature so that normalization does not need to deal
// with bound regions.
- let fn_sig =
- self.db.callable_item_signature(method_item.into()).instantiate(interner, args);
let fn_sig = self
- .infer_ctxt
- .instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, fn_sig);
+ .db
+ .callable_item_signature(method_item.into())
+ .instantiate(interner, args)
+ .skip_norm_wip();
+ let fn_sig = self.infer_ctxt.instantiate_binder_with_fresh_vars(
+ cause.span(),
+ BoundRegionConversionTime::FnCall,
+ fn_sig,
+ );
// Register obligations for the parameters. This will include the
// `Self` parameter, which in turn has a bound of the main trait,
@@ -323,8 +310,8 @@ impl<'db> InferenceTable<'db> {
// any late-bound regions appearing in its bounds.
let bounds = GenericPredicates::query_all(self.db, method_item.into());
let bounds = clauses_as_obligations(
- bounds.iter_instantiated(interner, args.as_slice()),
- ObligationCause::new(),
+ bounds.iter_instantiated(interner, args.as_slice()).map(Unnormalized::skip_norm_wip),
+ cause,
self.param_env,
);
@@ -338,7 +325,7 @@ impl<'db> InferenceTable<'db> {
for ty in fn_sig.inputs_and_output {
obligations.push(Obligation::new(
interner,
- obligation.cause.clone(),
+ obligation.cause,
self.param_env,
Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(ty.into()))),
));
@@ -612,7 +599,7 @@ impl InherentImpls {
for impl_id in module_data.scope.inherent_impls() {
let interner = DbInterner::new_no_crate(db);
let self_ty = db.impl_self_ty(impl_id);
- let self_ty = self_ty.instantiate_identity();
+ let self_ty = self_ty.instantiate_identity().skip_norm_wip();
if let Some(self_ty) =
simplify_type(interner, self_ty, TreatParams::InstantiateWithInfer)
{
@@ -727,7 +714,7 @@ impl TraitImpls {
for (_module_id, module_data) in def_map.modules() {
for impl_id in module_data.scope.trait_impls() {
let trait_ref = match db.impl_trait(impl_id) {
- Some(tr) => tr.instantiate_identity(),
+ Some(tr) => tr.instantiate_identity().skip_norm_wip(),
None => continue,
};
// Reservation impls should be ignored during trait resolution, so we never need
diff --git a/crates/hir-ty/src/method_resolution/confirm.rs b/crates/hir-ty/src/method_resolution/confirm.rs
index 94c70c29f7..c425e69dc5 100644
--- a/crates/hir-ty/src/method_resolution/confirm.rs
+++ b/crates/hir-ty/src/method_resolution/confirm.rs
@@ -2,9 +2,10 @@
//! is valid and registering all obligations.
use hir_def::{
- FunctionId, GenericDefId, GenericParamId, ItemContainerId, TraitId,
+ FunctionId, GenericDefId, GenericParamId, TraitId,
expr_store::path::{GenericArg as HirGenericArg, GenericArgs as HirGenericArgs},
hir::{ExprId, generics::GenericParamDataRef},
+ type_ref::TypeRefId,
};
use rustc_type_ir::{
TypeFoldable,
@@ -15,9 +16,9 @@ use tracing::debug;
use crate::{
Adjust, Adjustment, AutoBorrow, IncorrectGenericsLenKind, InferenceDiagnostic,
- LifetimeElisionKind, PointerCast,
+ LifetimeElisionKind, PointerCast, Span,
db::HirDatabase,
- infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch},
+ infer::{AllowTwoPhase, AutoBorrowMutability, InferenceContext},
lower::{
GenericPredicates,
path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings},
@@ -26,7 +27,7 @@ use crate::{
next_solver::{
Binder, Clause, ClauseKind, Const, DbInterner, EarlyParamRegion, ErrorGuaranteed, FnSig,
GenericArg, GenericArgs, ParamConst, PolyExistentialTraitRef, PolyTraitRef, Region,
- TraitRef, Ty, TyKind,
+ TraitRef, Ty, TyKind, Unnormalized,
infer::{
BoundRegionConversionTime, InferCtxt,
traits::{ObligationCause, PredicateObligation},
@@ -38,7 +39,7 @@ use crate::{
struct ConfirmContext<'a, 'b, 'db> {
ctx: &'a mut InferenceContext<'b, 'db>,
candidate: FunctionId,
- expr: ExprId,
+ call_expr: ExprId,
}
#[derive(Debug)]
@@ -73,9 +74,9 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
fn new(
ctx: &'a mut InferenceContext<'b, 'db>,
candidate: FunctionId,
- expr: ExprId,
+ call_expr: ExprId,
) -> ConfirmContext<'a, 'b, 'db> {
- ConfirmContext { ctx, candidate, expr }
+ ConfirmContext { ctx, candidate, call_expr }
}
#[inline]
@@ -136,7 +137,8 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
);
let illegal_sized_bound = self.predicates_require_illegal_sized_bound(
GenericPredicates::query_all(self.db(), self.candidate.into())
- .iter_instantiated(self.interner(), filler_args.as_slice()),
+ .iter_instantiated(self.interner(), filler_args.as_slice())
+ .map(Unnormalized::skip_norm_wip),
);
// Unify the (adjusted) self type with what the method expects.
@@ -180,7 +182,8 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
) -> (Ty<'db>, Box<[Adjustment]>) {
// Commit the autoderefs by calling `autoderef` again, but this
// time writing the results into the various typeck results.
- let mut autoderef = self.ctx.table.autoderef_with_tracking(unadjusted_self_ty);
+ let mut autoderef =
+ self.ctx.table.autoderef_with_tracking(unadjusted_self_ty, self.call_expr.into());
let Some((mut target, n)) = autoderef.nth(pick.autoderefs) else {
return (Ty::new_error(self.interner(), ErrorGuaranteed), Box::new([]));
};
@@ -190,7 +193,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
self.ctx.table.register_infer_ok(autoderef.adjust_steps_as_infer_ok());
match pick.autoref_or_ptr_adjustment {
Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => {
- let region = self.infcx().next_region_var();
+ let region = self.infcx().next_region_var(self.call_expr.into());
// Type we're wrapping in a reference, used later for unsizing
let base_ty = target;
@@ -254,7 +257,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
) -> GenericArgs<'db> {
match pick.kind {
probe::InherentImplPick(impl_def_id) => {
- self.infcx().fresh_args_for_item(impl_def_id.into())
+ self.infcx().fresh_args_for_item(self.call_expr.into(), impl_def_id.into())
}
probe::ObjectPick(trait_def_id) => {
@@ -296,7 +299,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
// the process we will unify the transformed-self-type
// of the method with the actual type in order to
// unify some of these variables.
- self.infcx().fresh_args_for_item(trait_def_id.into())
+ self.infcx().fresh_args_for_item(self.call_expr.into(), trait_def_id.into())
}
probe::WhereClausePick(poly_trait_ref) => {
@@ -316,12 +319,11 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
// yield an object-type (e.g., `&Object` or `Box<Object>`
// etc).
- let mut autoderef = self.ctx.table.autoderef(self_ty);
+ let mut autoderef = self.ctx.table.autoderef(self_ty, self.call_expr.into());
// We don't need to gate this behind arbitrary self types
// per se, but it does make things a bit more gated.
- if self.ctx.unstable_features.arbitrary_self_types
- || self.ctx.unstable_features.arbitrary_self_types_pointers
+ if self.ctx.features.arbitrary_self_types || self.ctx.features.arbitrary_self_types_pointers
{
autoderef = autoderef.use_receiver_trait();
}
@@ -401,8 +403,8 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
let GenericParamId::ConstParamId(const_id) = param_id else {
unreachable!("non-const param ID for const param");
};
- let const_ty = self.ctx.db.const_param_ty_ns(const_id);
- self.ctx.make_body_const(*konst, const_ty).into()
+ let const_ty = self.ctx.db.const_param_ty(const_id);
+ self.ctx.create_body_anon_const(konst.expr, const_ty, false).into()
}
_ => unreachable!("unmatching param kinds were passed to `provided_kind()`"),
}
@@ -410,12 +412,13 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
fn provided_type_like_const(
&mut self,
- const_ty: Ty<'db>,
+ _type_ref: TypeRefId,
+ _const_ty: Ty<'db>,
arg: TypeLikeConst<'_>,
) -> Const<'db> {
match arg {
- TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path, const_ty),
- TypeLikeConst::Infer => self.ctx.table.next_const_var(),
+ TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path),
+ TypeLikeConst::Infer => self.ctx.table.next_const_var(Span::Dummy),
}
}
@@ -424,12 +427,15 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
_def: GenericDefId,
param_id: GenericParamId,
_param: GenericParamDataRef<'_>,
- _infer_args: bool,
+ infer_args: bool,
_preceding_args: &[GenericArg<'db>],
+ had_count_error: bool,
) -> GenericArg<'db> {
// Always create an inference var, even when `infer_args == false`. This helps with diagnostics,
// and I think it's also required in the presence of `impl Trait` (that must be inferred).
- self.ctx.table.next_var_for_param(param_id)
+ let span =
+ if !infer_args || had_count_error { Span::Dummy } else { self.expr.into() };
+ self.ctx.table.var_for_def(param_id, span)
}
fn parent_arg(&mut self, param_idx: u32, _param_id: GenericParamId) -> GenericArg<'db> {
@@ -463,7 +469,11 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
LifetimeElisionKind::Infer,
false,
None,
- &mut LowererCtx { ctx: self.ctx, expr: self.expr, parent_args: parent_args.as_slice() },
+ &mut LowererCtx {
+ ctx: self.ctx,
+ expr: self.call_expr,
+ parent_args: parent_args.as_slice(),
+ },
)
}
@@ -477,17 +487,14 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
"unify_receivers: self_ty={:?} method_self_ty={:?} pick={:?}",
self_ty, method_self_ty, pick
);
- let cause = ObligationCause::new();
+ let cause = ObligationCause::new(self.call_expr);
match self.ctx.table.at(&cause).sup(method_self_ty, self_ty) {
Ok(infer_ok) => {
self.ctx.table.register_infer_ok(infer_ok);
}
Err(_) => {
- if self.ctx.unstable_features.arbitrary_self_types {
- self.ctx.result.type_mismatches.get_or_insert_default().insert(
- self.expr.into(),
- TypeMismatch { expected: method_self_ty.store(), actual: self_ty.store() },
- );
+ if self.ctx.features.arbitrary_self_types {
+ self.ctx.emit_type_mismatch(self.call_expr.into(), method_self_ty, self_ty);
}
}
}
@@ -509,13 +516,17 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
let def_id = self.candidate;
let method_predicates = clauses_as_obligations(
GenericPredicates::query_all(self.db(), def_id.into())
- .iter_instantiated(self.interner(), all_args),
- ObligationCause::new(),
+ .iter_instantiated(self.interner(), all_args)
+ .map(Unnormalized::skip_norm_wip),
+ ObligationCause::new(self.call_expr),
self.ctx.table.param_env,
);
- let sig =
- self.db().callable_item_signature(def_id.into()).instantiate(self.interner(), all_args);
+ let sig = self
+ .db()
+ .callable_item_signature(def_id.into())
+ .instantiate(self.interner(), all_args)
+ .skip_norm_wip();
debug!("type scheme instantiated, sig={:?}", sig);
let sig = self.instantiate_binder_with_fresh_vars(sig);
@@ -536,13 +547,13 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
// this is a projection from a trait reference, so we have to
// make sure that the trait reference inputs are well-formed.
- self.ctx.table.add_wf_bounds(all_args);
+ self.ctx.table.add_wf_bounds(self.call_expr.into(), all_args);
// the function type must also be well-formed (this is not
// implied by the args being well-formed because of inherent
// impls and late-bound regions - see issue #28609).
for ty in sig.inputs_and_output {
- self.ctx.table.register_wf_obligation(ty.into(), ObligationCause::new());
+ self.ctx.table.register_wf_obligation(ty.into(), ObligationCause::new(self.call_expr));
}
}
@@ -570,9 +581,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
fn check_for_illegal_method_calls(&self) {
// Disallow calls to the method `drop` defined in the `Drop` trait.
- if let ItemContainerId::TraitId(trait_def_id) = self.candidate.loc(self.db()).container
- && self.ctx.lang_items.Drop.is_some_and(|drop_trait| drop_trait == trait_def_id)
- {
+ if self.ctx.lang_items.Drop_drop.is_some_and(|drop_fn| drop_fn == self.candidate) {
// FIXME: Report an error.
}
}
@@ -610,6 +619,10 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
where
T: TypeFoldable<DbInterner<'db>> + Copy,
{
- self.infcx().instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, value)
+ self.infcx().instantiate_binder_with_fresh_vars(
+ self.call_expr.into(),
+ BoundRegionConversionTime::FnCall,
+ value,
+ )
}
}
diff --git a/crates/hir-ty/src/method_resolution/probe.rs b/crates/hir-ty/src/method_resolution/probe.rs
index 3604076ccd..4b2f0cfd70 100644
--- a/crates/hir-ty/src/method_resolution/probe.rs
+++ b/crates/hir-ty/src/method_resolution/probe.rs
@@ -15,7 +15,7 @@ use rustc_type_ir::{
InferTy, TypeVisitableExt, Upcast, Variance,
elaborate::{self, supertrait_def_ids},
fast_reject::{DeepRejectCtxt, TreatParams, simplify_type},
- inherent::{AdtDef as _, BoundExistentialPredicates as _, IntoKind, Ty as _},
+ inherent::{BoundExistentialPredicates as _, IntoKind, Ty as _},
};
use smallvec::{SmallVec, smallvec};
use tracing::{debug, instrument};
@@ -32,7 +32,7 @@ use crate::{
},
next_solver::{
Binder, Canonical, ClauseKind, DbInterner, FnSig, GenericArg, GenericArgs, Goal, ParamEnv,
- PolyTraitRef, Predicate, Region, SimplifiedType, TraitRef, Ty, TyKind,
+ PolyTraitRef, Predicate, Region, SimplifiedType, TraitRef, Ty, TyKind, Unnormalized,
infer::{
BoundRegionConversionTime, InferCtxt, InferOk,
canonical::{QueryResponse, canonicalizer::OriginalQueryValues},
@@ -284,7 +284,8 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
// special handling for this "trivial case" is a good idea.
let infcx = self.infcx;
- let (self_ty, var_values) = infcx.instantiate_canonical(&query_input);
+ let (self_ty, var_values) =
+ infcx.instantiate_canonical(self.call_span, &query_input);
debug!(?self_ty, ?query_input, "probe_op: Mode::Path");
let prev_opaque_entries =
self.infcx.inner.borrow_mut().opaque_types().num_entries();
@@ -315,7 +316,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
// ambiguous.
if let Some(bad_ty) = &steps.opt_bad_ty {
if bad_ty.reached_raw_pointer
- && !self.unstable_features.arbitrary_self_types_pointers
+ && !self.features.arbitrary_self_types_pointers
&& self.edition.at_least_2018()
{
// this case used to be allowed by the compiler,
@@ -338,7 +339,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
let ty = self
.infcx
.instantiate_query_response_and_region_obligations(
- &ObligationCause::new(),
+ &ObligationCause::new(self.receiver_span),
self.param_env,
&orig_values,
ty,
@@ -380,7 +381,8 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
// chain to support recursive calls. We do error if the final
// infer var is not an opaque.
let infcx = self.infcx;
- let (self_ty, inference_vars) = infcx.instantiate_canonical(self_ty);
+ let (self_ty, inference_vars) =
+ infcx.instantiate_canonical(self.receiver_span, self_ty);
let prev_opaque_entries = infcx.inner.borrow_mut().opaque_types().num_entries();
let self_ty_is_opaque = |ty: Ty<'_>| {
@@ -401,18 +403,20 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
// converted to, in order to find out which of those methods might actually
// be callable.
let mut autoderef_via_deref =
- Autoderef::new(infcx, self.param_env, self_ty).include_raw_pointers();
+ Autoderef::new(infcx, self.param_env, self_ty, self.receiver_span)
+ .include_raw_pointers();
let mut reached_raw_pointer = false;
- let arbitrary_self_types_enabled = self.unstable_features.arbitrary_self_types
- || self.unstable_features.arbitrary_self_types_pointers;
+ let arbitrary_self_types_enabled =
+ self.features.arbitrary_self_types || self.features.arbitrary_self_types_pointers;
let (mut steps, reached_recursion_limit) = if arbitrary_self_types_enabled {
let reachable_via_deref =
autoderef_via_deref.by_ref().map(|_| true).chain(std::iter::repeat(false));
- let mut autoderef_via_receiver = Autoderef::new(infcx, self.param_env, self_ty)
- .include_raw_pointers()
- .use_receiver_trait();
+ let mut autoderef_via_receiver =
+ Autoderef::new(infcx, self.param_env, self_ty, self.receiver_span)
+ .include_raw_pointers()
+ .use_receiver_trait();
let steps = autoderef_via_receiver
.by_ref()
.zip(reachable_via_deref)
@@ -613,7 +617,7 @@ impl<'db> ProbeChoice<'db> for ProbeForNameChoice<'db> {
// We collapse to a subtrait pick *after* filtering unstable candidates
// to make sure we don't prefer a unstable subtrait method over a stable
// supertrait method.
- if this.ctx.unstable_features.supertrait_item_shadowing
+ if this.ctx.features.supertrait_item_shadowing
&& let Some(pick) =
this.collapse_candidates_to_subtrait_pick(self_ty, &applicable_candidates)
{
@@ -921,7 +925,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
// will still match the original object type, but it won't pollute our
// type variables in any form, so just do that!
let (QueryResponse { value: generalized_self_ty, .. }, _ignored_var_values) =
- self.infcx().instantiate_canonical(self_ty);
+ self.infcx().instantiate_canonical(self.ctx.call_span, self_ty);
self.assemble_inherent_candidates_from_object(generalized_self_ty);
self.assemble_inherent_impl_candidates_for_type(
@@ -935,7 +939,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
}
}
TyKind::Adt(def, _) => {
- let def_id = def.def_id().0;
+ let def_id = def.def_id();
self.assemble_inherent_impl_candidates_for_type(
&SimplifiedType::Adt(def_id.into()),
receiver_steps,
@@ -1117,7 +1121,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
#[instrument(level = "debug", skip(self))]
fn assemble_extension_candidates_for_trait(&mut self, trait_def_id: TraitId) {
- let trait_args = self.infcx().fresh_args_for_item(trait_def_id.into());
+ let trait_args = self.infcx().fresh_args_for_item(self.ctx.call_span, trait_def_id.into());
let trait_ref = TraitRef::new_from_args(self.interner(), trait_def_id.into(), trait_args);
self.with_trait_item(trait_def_id, |this, item| {
@@ -1183,8 +1187,8 @@ impl<'a, 'db> ProbeContext<'a, 'db, ProbeForNameChoice<'db>> {
// The errors emitted by this function are part of
// the arbitrary self types work, and should not impact
// other users.
- if !self.ctx.unstable_features.arbitrary_self_types
- && !self.ctx.unstable_features.arbitrary_self_types_pointers
+ if !self.ctx.features.arbitrary_self_types
+ && !self.ctx.features.arbitrary_self_types_pointers
{
return Ok(());
}
@@ -1268,7 +1272,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
let InferOk { value: self_ty, obligations: instantiate_self_ty_obligations } = self
.infcx()
.instantiate_query_response_and_region_obligations(
- &ObligationCause::new(),
+ &ObligationCause::new(self.ctx.receiver_span),
self.param_env(),
self.orig_steps_var_values,
&step.self_ty,
@@ -1495,8 +1499,12 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
&self,
trait_ref: TraitRef<'db>,
) -> SelectionResult<'db, Selection<'db>> {
- let obligation =
- Obligation::new(self.interner(), ObligationCause::new(), self.param_env(), trait_ref);
+ let obligation = Obligation::new(
+ self.interner(),
+ ObligationCause::new(self.ctx.call_span),
+ self.param_env(),
+ trait_ref,
+ );
self.infcx().select(&obligation)
}
@@ -1510,6 +1518,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
}
TraitCandidate(trait_ref) => self.infcx().probe(|_| {
let trait_ref = self.infcx().instantiate_binder_with_fresh_vars(
+ self.ctx.call_span,
BoundRegionConversionTime::FnCall,
trait_ref,
);
@@ -1522,7 +1531,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
// up with the `self` parameter of the method.
let _ = self
.infcx()
- .at(&ObligationCause::dummy(), self.param_env())
+ .at(&ObligationCause::new(self.ctx.call_span), self.param_env())
.sup(xform_self_ty, self_ty);
match self.select_trait_candidate(trait_ref) {
Ok(Some(ImplSource::UserDefined(ref impl_data))) => {
@@ -1553,7 +1562,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
) -> ProbeResult {
self.infcx().probe(|_| {
let mut result = ProbeResult::Match;
- let cause = &ObligationCause::new();
+ let cause = &ObligationCause::new(self.ctx.call_span);
let mut ocx = ObligationCtxt::new(self.infcx());
// Subtle: we're not *really* instantiating the current self type while
@@ -1574,9 +1583,13 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
match probe.kind {
InherentImplCandidate { impl_def_id, .. } => {
- let impl_args = self.infcx().fresh_args_for_item(impl_def_id.into());
- let impl_ty =
- self.db().impl_self_ty(impl_def_id).instantiate(self.interner(), impl_args);
+ let impl_args =
+ self.infcx().fresh_args_for_item(self.ctx.call_span, impl_def_id.into());
+ let impl_ty = self
+ .db()
+ .impl_self_ty(impl_def_id)
+ .instantiate(self.interner(), impl_args)
+ .skip_norm_wip();
(xform_self_ty, xform_ret_ty) =
self.xform_self_ty(probe.item, impl_ty, impl_args.as_slice());
match ocx.relate(
@@ -1595,8 +1608,10 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
// Check whether the impl imposes obligations we have to worry about.
let impl_bounds = GenericPredicates::query_all(self.db(), impl_def_id.into());
let impl_bounds = clauses_as_obligations(
- impl_bounds.iter_instantiated(self.interner(), impl_args.as_slice()),
- ObligationCause::new(),
+ impl_bounds
+ .iter_instantiated(self.interner(), impl_args.as_slice())
+ .map(Unnormalized::skip_norm_wip),
+ ObligationCause::new(self.ctx.call_span),
self.param_env(),
);
// Convert the bounds into obligations.
@@ -1632,6 +1647,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
}
let trait_ref = self.infcx().instantiate_binder_with_fresh_vars(
+ self.ctx.call_span,
BoundRegionConversionTime::FnCall,
poly_trait_ref,
);
@@ -1655,7 +1671,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
}
let obligation = Obligation::new(
self.interner(),
- cause.clone(),
+ *cause,
self.param_env(),
Binder::dummy(trait_ref),
);
@@ -1667,6 +1683,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
}
ObjectCandidate(poly_trait_ref) | WhereClauseCandidate(poly_trait_ref) => {
let trait_ref = self.infcx().instantiate_binder_with_fresh_vars(
+ self.ctx.call_span,
BoundRegionConversionTime::FnCall,
poly_trait_ref,
);
@@ -1728,7 +1745,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
if let Some(xform_ret_ty) = xform_ret_ty {
ocx.register_obligation(Obligation::new(
self.interner(),
- cause.clone(),
+ *cause,
self.param_env(),
ClauseKind::WellFormed(xform_ret_ty.into()),
));
@@ -1799,7 +1816,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
// reachable. In this case we don't care about opaque
// types there.
let Ok(ok) = self.infcx().instantiate_query_response_and_region_obligations(
- &ObligationCause::new(),
+ &ObligationCause::new(self.ctx.receiver_span),
self.param_env(),
self.orig_steps_var_values,
&step.self_ty,
@@ -2029,8 +2046,12 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
// In general, during probe we erase regions.
Region::new_erased(self.interner()).into()
}
- GenericParamId::TypeParamId(_) => self.infcx().next_ty_var().into(),
- GenericParamId::ConstParamId(_) => self.infcx().next_const_var().into(),
+ GenericParamId::TypeParamId(_) => {
+ self.infcx().next_ty_var(self.ctx.call_span).into()
+ }
+ GenericParamId::ConstParamId(_) => {
+ self.infcx().next_const_var(self.ctx.call_span).into()
+ }
}
}
},
@@ -2038,7 +2059,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
fn_sig.instantiate(self.interner(), args)
};
- self.interner().instantiate_bound_regions_with_erased(xform_fn_sig)
+ self.interner().instantiate_bound_regions_with_erased(xform_fn_sig.skip_norm_wip())
}
fn with_impl_item(&mut self, def_id: ImplId, callback: impl FnMut(&mut Self, CandidateId)) {
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index a8e06f3a2b..5f61b1defb 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -5,7 +5,7 @@ use std::{collections::hash_map::Entry, fmt::Display, iter};
use base_db::Crate;
use either::Either;
use hir_def::{
- DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId,
+ FieldId, StaticId, TupleFieldId, UnionId, VariantId,
hir::{BindingId, Expr, ExprId, Ordering, PatId},
};
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
@@ -16,7 +16,7 @@ use smallvec::{SmallVec, smallvec};
use stdx::{impl_from, never};
use crate::{
- CallableDefId, InferenceResult, MemoryMap,
+ CallableDefId, InferBodyId, InferenceResult, MemoryMap,
consteval::usize_const,
db::{HirDatabase, InternedClosureId},
display::{DisplayTarget, HirDisplay},
@@ -47,9 +47,6 @@ pub use monomorphization::{
monomorphized_mir_body_for_closure_query, monomorphized_mir_body_query,
};
-pub(crate) use lower::mir_body_cycle_result;
-pub(crate) use monomorphization::monomorphized_mir_body_cycle_result;
-
use super::consteval::try_const_usize;
pub type BasicBlockId = Idx<BasicBlock>;
@@ -200,9 +197,10 @@ impl<V: PartialEq> ProjectionElem<V> {
}
},
ProjectionElem::Field(Either::Left(f)) => match base.kind() {
- TyKind::Adt(_, subst) => {
- db.field_types(f.parent)[f.local_id].get().instantiate(interner, subst)
- }
+ TyKind::Adt(_, subst) => db.field_types(f.parent)[f.local_id]
+ .get()
+ .instantiate(interner, subst)
+ .skip_norm_wip(),
ty => {
never!("Only adt has field, found {:?}", ty);
Ty::new_error(interner, ErrorGuaranteed)
@@ -1087,7 +1085,7 @@ pub struct MirBody {
pub basic_blocks: Arena<BasicBlock>,
pub locals: Arena<Local>,
pub start_block: BasicBlockId,
- pub owner: DefWithBodyId,
+ pub owner: InferBodyId,
pub binding_locals: ArenaMap<BindingId, LocalId>,
pub upvar_locals: FxHashMap<BindingId, Vec<(LocalId, crate::closure_analysis::Place)>>,
pub param_locals: Vec<LocalId>,
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs
index 17715d3fcd..940bc57259 100644
--- a/crates/hir-ty/src/mir/borrowck.rs
+++ b/crates/hir-ty/src/mir/borrowck.rs
@@ -5,13 +5,14 @@
use std::iter;
-use hir_def::{DefWithBodyId, ExpressionStoreOwnerId, HasModule};
+use either::Either;
+use hir_def::HasModule;
use la_arena::ArenaMap;
use rustc_hash::FxHashMap;
use stdx::never;
-use triomphe::Arc;
use crate::{
+ InferBodyId,
closure_analysis::ProjectionKind as HirProjectionKind,
db::{HirDatabase, InternedClosureId},
display::DisplayTarget,
@@ -57,91 +58,109 @@ pub struct BorrowRegion {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BorrowckResult {
- pub mir_body: Arc<MirBody>,
+ owner: Either<InferBodyId, InternedClosureId>,
pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
pub moved_out_of_ref: Vec<MovedOutOfRef>,
pub partially_moved: Vec<PartiallyMoved>,
pub borrow_regions: Vec<BorrowRegion>,
}
-fn all_mir_bodies(
- db: &dyn HirDatabase,
- def: DefWithBodyId,
- mut cb: impl FnMut(Arc<MirBody>) -> BorrowckResult,
- mut merge_from_closures: impl FnMut(&mut BorrowckResult, &BorrowckResult),
-) -> Result<Arc<[BorrowckResult]>, MirLowerError> {
- fn for_closure(
- db: &dyn HirDatabase,
+impl BorrowckResult {
+ pub fn mir_body<'db>(&self, db: &'db dyn HirDatabase) -> &'db MirBody {
+ match self.owner {
+ Either::Left(it) => db.mir_body(it).unwrap(),
+ Either::Right(it) => db.mir_body_for_closure(it).unwrap(),
+ }
+ }
+}
+
+fn all_mir_bodies<'db>(
+ db: &'db dyn HirDatabase,
+ def: InferBodyId,
+ mut cb: impl FnMut(&'db MirBody, Either<InferBodyId, InternedClosureId>) -> BorrowckResult,
+ mut merge_from_closures: impl FnMut(
+ (&mut BorrowckResult, &'db MirBody),
+ (&BorrowckResult, &'db MirBody),
+ ),
+) -> Result<Box<[BorrowckResult]>, MirLowerError> {
+ fn for_closure<'db>(
+ db: &'db dyn HirDatabase,
c: InternedClosureId,
- results: &mut Vec<BorrowckResult>,
- cb: &mut impl FnMut(Arc<MirBody>) -> BorrowckResult,
- merge_from_closures: &mut impl FnMut(&mut BorrowckResult, &BorrowckResult),
+ results: &mut Vec<(BorrowckResult, &'db MirBody)>,
+ cb: &mut impl FnMut(&'db MirBody, Either<InferBodyId, InternedClosureId>) -> BorrowckResult,
+ merge_from_closures: &mut impl FnMut(
+ (&mut BorrowckResult, &'db MirBody),
+ (&BorrowckResult, &'db MirBody),
+ ),
) -> Result<(), MirLowerError> {
match db.mir_body_for_closure(c) {
Ok(body) => {
let parent_index = results.len();
- results.push(cb(body.clone()));
+ results.push((cb(body, Either::Right(c)), body));
body.closures
.iter()
.try_for_each(|&it| for_closure(db, it, results, cb, merge_from_closures))?;
merge(results, merge_from_closures, parent_index);
Ok(())
}
- Err(e) => Err(e),
+ Err(e) => Err(e.clone()),
}
}
- fn merge(
- results: &mut [BorrowckResult],
- merge: &mut impl FnMut(&mut BorrowckResult, &BorrowckResult),
+ fn merge<'db>(
+ results: &mut [(BorrowckResult, &'db MirBody)],
+ merge: &mut impl FnMut((&mut BorrowckResult, &'db MirBody), (&BorrowckResult, &'db MirBody)),
parent_index: usize,
) {
let (parent_and_before, children) = results.split_at_mut(parent_index + 1);
- let parent = &mut parent_and_before[parent_and_before.len() - 1];
- children.iter().for_each(|child| merge(parent, child));
+ let (parent, parent_mir_body) = &mut parent_and_before[parent_and_before.len() - 1];
+ children.iter().for_each(|(child, child_mir_body)| {
+ merge((parent, parent_mir_body), (child, child_mir_body))
+ });
}
let mut results = Vec::new();
match db.mir_body(def) {
Ok(body) => {
- results.push(cb(body.clone()));
+ results.push((cb(body, Either::Left(def)), body));
body.closures.iter().try_for_each(|&it| {
for_closure(db, it, &mut results, &mut cb, &mut merge_from_closures)
})?;
merge(&mut results, &mut merge_from_closures, 0);
- Ok(results.into())
+ Ok(results.into_iter().map(|(it, _)| it).collect())
}
- Err(e) => Err(e),
+ Err(e) => Err(e.clone()),
}
}
+#[salsa_macros::tracked(returns(ref), lru = 2024)]
pub fn borrowck_query(
db: &dyn HirDatabase,
- def: DefWithBodyId,
-) -> Result<Arc<[BorrowckResult]>, MirLowerError> {
+ def: InferBodyId,
+) -> Result<Box<[BorrowckResult]>, MirLowerError> {
let _p = tracing::info_span!("borrowck_query").entered();
let module = def.module(db);
let interner = DbInterner::new_with(db, module.krate(db));
- let env = db.trait_environment(ExpressionStoreOwnerId::from(def));
+ let env = db.trait_environment(def.expression_store_owner(db));
// This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`.
let typing_mode = TypingMode::borrowck(interner, def.into());
let res = all_mir_bodies(
db,
def,
- |body| {
+ |body, owner| {
// FIXME(next-solver): Opaques.
let infcx = interner.infer_ctxt().build(typing_mode);
BorrowckResult {
- mutability_of_locals: mutability_of_locals(&infcx, env, &body),
- moved_out_of_ref: moved_out_of_ref(&infcx, env, &body),
- partially_moved: partially_moved(&infcx, env, &body),
- borrow_regions: borrow_regions(db, &body),
- mir_body: body,
+ owner,
+ mutability_of_locals: mutability_of_locals(&infcx, env, body),
+ moved_out_of_ref: moved_out_of_ref(&infcx, env, body),
+ partially_moved: partially_moved(&infcx, env, body),
+ borrow_regions: borrow_regions(db, body),
}
},
- |parent, child| {
- for (upvar, child_locals) in &child.mir_body.upvar_locals {
- let Some(&parent_local) = parent.mir_body.binding_locals.get(*upvar) else {
+ |(parent, parent_mir_body), (child, child_mir_body)| {
+ for (upvar, child_locals) in &child_mir_body.upvar_locals {
+ let Some(&parent_local) = parent_mir_body.binding_locals.get(*upvar) else {
continue;
};
for (child_local, capture_place) in child_locals {
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 80e429c4c8..3372f6ec2e 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -5,20 +5,19 @@ use std::{borrow::Cow, cell::RefCell, fmt::Write, iter, mem, ops::Range};
use base_db::{Crate, target::TargetLoadError};
use either::Either;
use hir_def::{
- AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, FunctionId, GeneralConstId,
- HasModule, ItemContainerId, Lookup, StaticId, VariantId,
- expr_store::{Body, HygieneId},
+ AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId,
+ VariantId,
+ expr_store::{Body, ExpressionStore, HygieneId},
item_tree::FieldsShape,
lang_item::LangItems,
layout::{TagEncoding, Variants},
- resolver::{HasResolver, TypeNs, ValueNs},
+ resolver::{HasResolver, ValueNs},
signatures::{
EnumSignature, FunctionSignature, StaticFlags, StaticSignature, StructFlags,
StructSignature, TraitSignature,
},
};
-use hir_expand::{InFile, mod_path::path, name::Name};
-use intern::sym;
+use hir_expand::{InFile, mod_path::path};
use la_arena::ArenaMap;
use macros::GenericTypeVisitable;
use rustc_abi::{Size, TargetDataLayout};
@@ -30,7 +29,7 @@ use rustc_ast_ir::Mutability;
use rustc_hash::{FxHashMap, FxHashSet};
use rustc_type_ir::{
AliasTyKind,
- inherent::{AdtDef, GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _},
+ inherent::{GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _},
};
use span::FileId;
use stdx::never;
@@ -38,9 +37,9 @@ use syntax::{SyntaxNodePtr, TextRange};
use triomphe::Arc;
use crate::{
- CallableDefId, ComplexMemoryMap, InferenceResult, MemoryMap, ParamEnvAndCrate,
+ CallableDefId, ComplexMemoryMap, InferBodyId, InferenceResult, MemoryMap, ParamEnvAndCrate,
consteval::{self, ConstEvalError, try_const_usize},
- db::{HirDatabase, InternedClosureId},
+ db::{GeneralConstId, HirDatabase, InternedClosureId},
display::{ClosureStyle, DisplayTarget, HirDisplay},
infer::PointerCast,
layout::{Layout, LayoutError, RustcEnumVariantIdx},
@@ -156,26 +155,26 @@ impl TlsData {
}
}
-struct StackFrame {
- locals: Locals,
+struct StackFrame<'a> {
+ locals: Locals<'a>,
destination: Option<BasicBlockId>,
prev_stack_ptr: usize,
- span: (MirSpan, DefWithBodyId),
+ span: (MirSpan, InferBodyId),
}
#[derive(Clone)]
-enum MirOrDynIndex {
- Mir(Arc<MirBody>),
+enum MirOrDynIndex<'a> {
+ Mir(&'a MirBody),
Dyn(usize),
}
-pub struct Evaluator<'db> {
+pub struct Evaluator<'a, 'db> {
db: &'db dyn HirDatabase,
param_env: ParamEnvAndCrate<'db>,
- target_data_layout: Arc<TargetDataLayout>,
+ target_data_layout: &'db TargetDataLayout,
stack: Vec<u8>,
heap: Vec<u8>,
- code_stack: Vec<StackFrame>,
+ code_stack: Vec<StackFrame<'a>>,
/// 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>,
@@ -190,11 +189,11 @@ pub struct Evaluator<'db> {
layout_cache: RefCell<FxHashMap<Ty<'db>, Arc<Layout>>>,
projected_ty_cache: RefCell<FxHashMap<(Ty<'db>, PlaceElem), Ty<'db>>>,
not_special_fn_cache: RefCell<FxHashSet<FunctionId>>,
- mir_or_dyn_index_cache: RefCell<FxHashMap<(FunctionId, GenericArgs<'db>), MirOrDynIndex>>,
+ mir_or_dyn_index_cache: RefCell<FxHashMap<(FunctionId, GenericArgs<'db>), MirOrDynIndex<'a>>>,
/// Constantly dropping and creating `Locals` is very costly. We store
/// old locals that we normally want to drop here, to reuse their allocations
/// later.
- unused_locals_store: RefCell<FxHashMap<DefWithBodyId, Vec<Locals>>>,
+ unused_locals_store: RefCell<FxHashMap<InferBodyId, Vec<Locals<'a>>>>,
cached_ptr_size: usize,
cached_fn_trait_func: Option<FunctionId>,
cached_fn_mut_trait_func: Option<FunctionId>,
@@ -237,17 +236,21 @@ impl Interval {
Self { addr, size }
}
- fn get<'a, 'db>(&self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> {
+ fn get<'b, 'a, 'db: 'a>(&self, memory: &'b Evaluator<'a, 'db>) -> Result<'db, &'b [u8]> {
memory.read_memory(self.addr, self.size)
}
- fn write_from_bytes<'db>(&self, memory: &mut Evaluator<'db>, bytes: &[u8]) -> Result<'db, ()> {
+ fn write_from_bytes<'a, 'db: 'a>(
+ &self,
+ memory: &mut Evaluator<'a, 'db>,
+ bytes: &[u8],
+ ) -> Result<'db, ()> {
memory.write_memory(self.addr, bytes)
}
- fn write_from_interval<'db>(
+ fn write_from_interval<'a, 'db: 'a>(
&self,
- memory: &mut Evaluator<'db>,
+ memory: &mut Evaluator<'a, 'db>,
interval: Interval,
) -> Result<'db, ()> {
memory.copy_from_interval(self.addr, interval)
@@ -259,16 +262,22 @@ impl Interval {
}
impl<'db> IntervalAndTy<'db> {
- fn get<'a>(&self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> {
+ fn get<'b, 'a>(&self, memory: &'b Evaluator<'a, 'db>) -> Result<'db, &'b [u8]>
+ where
+ 'db: 'a,
+ {
memory.read_memory(self.interval.addr, self.interval.size)
}
- fn new(
+ fn new<'a>(
addr: Address,
ty: Ty<'db>,
- evaluator: &Evaluator<'db>,
- locals: &Locals,
- ) -> Result<'db, IntervalAndTy<'db>> {
+ evaluator: &Evaluator<'a, 'db>,
+ locals: &Locals<'a>,
+ ) -> Result<'db, IntervalAndTy<'db>>
+ where
+ 'db: 'a,
+ {
let size = evaluator.size_of_sized(ty, locals, "type of interval")?;
Ok(IntervalAndTy { interval: Interval { addr, size }, ty })
}
@@ -286,7 +295,7 @@ impl From<Interval> for IntervalOrOwned {
}
impl IntervalOrOwned {
- fn get<'a, 'db>(&'a self, memory: &'a Evaluator<'db>) -> Result<'db, &'a [u8]> {
+ fn get<'b, 'a, 'db: 'a>(&'b self, memory: &'b Evaluator<'a, 'db>) -> Result<'db, &'b [u8]> {
Ok(match self {
IntervalOrOwned::Owned(o) => o,
IntervalOrOwned::Borrowed(b) => b.get(memory)?,
@@ -362,7 +371,7 @@ pub enum MirEvalError {
InvalidConst,
InFunction(
Box<MirEvalError>,
- Vec<(Either<FunctionId, InternedClosureId>, MirSpan, DefWithBodyId)>,
+ Vec<(Either<FunctionId, InternedClosureId>, MirSpan, InferBodyId)>,
),
ExecutionLimitExceeded,
StackOverflow,
@@ -401,7 +410,16 @@ impl MirEvalError {
writeln!(f, "In {closure:?}")?;
}
}
- let source_map = &Body::with_source_map(db, *def).1;
+ let (source_map, self_param_syntax) = match *def {
+ InferBodyId::DefWithBodyId(def) => {
+ let body = &Body::with_source_map(db, def).1;
+ (&**body, body.self_param_syntax())
+ }
+ InferBodyId::AnonConstId(def) => {
+ let store = ExpressionStore::with_source_map(db, def.loc(db).owner).1;
+ (store, None)
+ }
+ };
let span: InFile<SyntaxNodePtr> = match span {
MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
Ok(s) => s.map(|it| it.into()),
@@ -421,7 +439,7 @@ impl MirEvalError {
None => continue,
}
}
- MirSpan::SelfParam => match source_map.self_param_syntax() {
+ MirSpan::SelfParam => match self_param_syntax {
Some(s) => s.map(|it| it.syntax_node_ptr()),
None => continue,
},
@@ -449,6 +467,7 @@ impl MirEvalError {
ItemContainerId::ImplId(impl_id) => Some({
db.impl_self_ty(impl_id)
.instantiate_identity()
+ .skip_norm_wip()
.display(db, display_target)
.to_string()
}),
@@ -576,9 +595,9 @@ impl DropFlags {
}
#[derive(Debug)]
-struct Locals {
+struct Locals<'a> {
ptr: ArenaMap<LocalId, Interval>,
- body: Arc<MirBody>,
+ body: &'a MirBody,
drop_flags: DropFlags,
}
@@ -596,9 +615,9 @@ impl MirOutput {
}
}
-pub fn interpret_mir<'db>(
+pub fn interpret_mir<'a, 'db: 'a>(
db: &'db dyn HirDatabase,
- body: Arc<MirBody>,
+ body: &MirBody,
// FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now
// they share their body with their parent, so in MIR lowering we have locals of the parent body, which
// might have placeholders. With this argument, we (wrongly) assume that every placeholder type has
@@ -613,7 +632,7 @@ pub fn interpret_mir<'db>(
if evaluator.ptr_size() != size_of::<usize>() {
not_supported!("targets with different pointer size from host");
}
- let interval = evaluator.interpret_mir(body.clone(), None.into_iter())?;
+ let interval = evaluator.interpret_mir(body, None.into_iter())?;
let bytes = interval.get(&evaluator)?;
let mut memory_map = evaluator.create_memory_map(
bytes,
@@ -638,13 +657,13 @@ const EXECUTION_LIMIT: usize = 100_000;
#[cfg(not(test))]
const EXECUTION_LIMIT: usize = 10_000_000;
-impl<'db> Evaluator<'db> {
+impl<'a, 'db: 'a> Evaluator<'a, 'db> {
pub fn new(
db: &'db dyn HirDatabase,
- owner: DefWithBodyId,
+ owner: InferBodyId,
assert_placeholder_ty_is_unused: bool,
trait_env: Option<ParamEnvAndCrate<'db>>,
- ) -> Result<'db, Evaluator<'db>> {
+ ) -> Result<'db, Evaluator<'a, 'db>> {
let module = owner.module(db);
let crate_id = module.krate(db);
let target_data_layout = match db.target_data_layout(crate_id) {
@@ -666,7 +685,7 @@ impl<'db> Evaluator<'db> {
db,
random_state: oorandom::Rand64::new(0),
param_env: trait_env.unwrap_or_else(|| ParamEnvAndCrate {
- param_env: db.trait_environment(ExpressionStoreOwnerId::from(owner)),
+ param_env: db.trait_environment(owner.expression_store_owner(db)),
krate: crate_id,
}),
crate_id,
@@ -682,15 +701,9 @@ impl<'db> Evaluator<'db> {
mir_or_dyn_index_cache: RefCell::new(Default::default()),
unused_locals_store: RefCell::new(Default::default()),
cached_ptr_size,
- cached_fn_trait_func: lang_items
- .Fn
- .and_then(|x| x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call))),
- cached_fn_mut_trait_func: lang_items.FnMut.and_then(|x| {
- x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call_mut))
- }),
- cached_fn_once_trait_func: lang_items.FnOnce.and_then(|x| {
- x.trait_items(db).method_by_name(&Name::new_symbol_root(sym::call_once))
- }),
+ cached_fn_trait_func: lang_items.Fn_call,
+ cached_fn_mut_trait_func: lang_items.FnMut_call_mut,
+ cached_fn_once_trait_func: lang_items.FnOnce_call_once,
infcx,
})
}
@@ -705,11 +718,11 @@ impl<'db> Evaluator<'db> {
self.infcx.interner.lang_items()
}
- fn place_addr(&self, p: &Place, locals: &Locals) -> Result<'db, Address> {
+ fn place_addr(&self, p: &Place, locals: &Locals<'a>) -> Result<'db, Address> {
Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0)
}
- fn place_interval(&self, p: &Place, locals: &Locals) -> Result<'db, Interval> {
+ fn place_interval(&self, p: &Place, locals: &Locals<'a>) -> Result<'db, Interval> {
let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?;
Ok(Interval {
addr: place_addr_and_ty.0,
@@ -736,10 +749,10 @@ impl<'db> Evaluator<'db> {
r
}
- fn place_addr_and_ty_and_metadata<'a>(
- &'a self,
+ fn place_addr_and_ty_and_metadata<'b>(
+ &'b self,
p: &Place,
- locals: &'a Locals,
+ locals: &'b Locals<'a>,
) -> Result<'db, (Address, Ty<'db>, Option<IntervalOrOwned>)> {
let mut addr = locals.ptr[p.local].addr;
let mut ty: Ty<'db> = locals.body.locals[p.local].ty.as_ref();
@@ -873,11 +886,11 @@ impl<'db> Evaluator<'db> {
self.layout(Ty::new_adt(self.interner(), adt, subst))
}
- fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals) -> Result<'db, Ty<'db>> {
+ fn place_ty<'b>(&'b self, p: &Place, locals: &'b Locals<'a>) -> Result<'db, Ty<'db>> {
Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1)
}
- fn operand_ty(&self, o: &Operand, locals: &Locals) -> Result<'db, Ty<'db>> {
+ fn operand_ty(&self, o: &Operand, locals: &Locals<'a>) -> Result<'db, Ty<'db>> {
Ok(match &o.kind {
OperandKind::Copy(p) | OperandKind::Move(p) => self.place_ty(p, locals)?,
OperandKind::Constant { konst: _, ty } => ty.as_ref(),
@@ -898,7 +911,7 @@ impl<'db> Evaluator<'db> {
fn operand_ty_and_eval(
&mut self,
o: &Operand,
- locals: &mut Locals,
+ locals: &mut Locals<'a>,
) -> Result<'db, IntervalAndTy<'db>> {
Ok(IntervalAndTy {
interval: self.eval_operand(o, locals)?,
@@ -908,7 +921,7 @@ impl<'db> Evaluator<'db> {
fn interpret_mir(
&mut self,
- body: Arc<MirBody>,
+ body: &'a MirBody,
args: impl Iterator<Item = IntervalOrOwned>,
) -> Result<'db, Interval> {
if let Some(it) = self.stack_depth_limit.checked_sub(1) {
@@ -917,8 +930,8 @@ impl<'db> Evaluator<'db> {
return Err(MirEvalError::StackOverflow);
}
let mut current_block_idx = body.start_block;
- let (mut locals, prev_stack_ptr) = self.create_locals_for_body(&body, None)?;
- self.fill_locals_for_body(&body, &mut locals, args)?;
+ let (mut locals, prev_stack_ptr) = self.create_locals_for_body(body, None)?;
+ self.fill_locals_for_body(body, &mut locals, args)?;
let prev_code_stack = mem::take(&mut self.code_stack);
let span = (MirSpan::Unknown, body.owner);
self.code_stack.push(StackFrame { locals, destination: None, prev_stack_ptr, span });
@@ -1041,7 +1054,7 @@ impl<'db> Evaluator<'db> {
let my_code_stack = mem::replace(&mut self.code_stack, prev_code_stack);
let mut error_stack = vec![];
for frame in my_code_stack.into_iter().rev() {
- if let DefWithBodyId::FunctionId(f) = frame.locals.body.owner {
+ if let Some(f) = frame.locals.body.owner.as_function() {
error_stack.push((Either::Left(f), frame.span.0, frame.span.1));
}
}
@@ -1073,7 +1086,7 @@ impl<'db> Evaluator<'db> {
fn fill_locals_for_body(
&mut self,
body: &MirBody,
- locals: &mut Locals,
+ locals: &mut Locals<'a>,
args: impl Iterator<Item = IntervalOrOwned>,
) -> Result<'db, ()> {
let mut remain_args = body.param_locals.len();
@@ -1096,19 +1109,15 @@ impl<'db> Evaluator<'db> {
fn create_locals_for_body(
&mut self,
- body: &Arc<MirBody>,
+ body: &'a MirBody,
destination: Option<Interval>,
- ) -> Result<'db, (Locals, usize)> {
+ ) -> Result<'db, (Locals<'a>, usize)> {
let mut locals =
match self.unused_locals_store.borrow_mut().entry(body.owner).or_default().pop() {
- None => Locals {
- ptr: ArenaMap::new(),
- body: body.clone(),
- drop_flags: DropFlags::default(),
- },
+ None => Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() },
Some(mut l) => {
l.drop_flags.clear();
- l.body = body.clone();
+ l.body = body;
l
}
};
@@ -1145,7 +1154,7 @@ impl<'db> Evaluator<'db> {
Ok((locals, prev_stack_pointer))
}
- fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals) -> Result<'db, IntervalOrOwned> {
+ fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'a>) -> Result<'db, IntervalOrOwned> {
use IntervalOrOwned::*;
Ok(match r {
Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?),
@@ -1651,7 +1660,7 @@ impl<'db> Evaluator<'db> {
let TyKind::Adt(adt_def, _) = ty.kind() else {
return Ok(0);
};
- let AdtId::EnumId(e) = adt_def.def_id().0 else {
+ let AdtId::EnumId(e) = adt_def.def_id() else {
return Ok(0);
};
match &layout.variants {
@@ -1662,7 +1671,7 @@ impl<'db> Evaluator<'db> {
Ok(r)
}
Variants::Multiple { tag, tag_encoding, variants, .. } => {
- let size = tag.size(&*self.target_data_layout).bytes_usize();
+ let size = tag.size(self.target_data_layout).bytes_usize();
let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
let is_signed = tag.is_signed();
match tag_encoding {
@@ -1701,13 +1710,13 @@ impl<'db> Evaluator<'db> {
return Ok(it);
}
if let TyKind::Adt(adt_ef, subst) = kind
- && let AdtId::StructId(struct_id) = adt_ef.def_id().0
+ && let AdtId::StructId(struct_id) = adt_ef.def_id()
{
let field_types = self.db.field_types(struct_id.into());
if let Some(ty) =
field_types.iter().last().map(|it| it.1.get().instantiate(self.interner(), subst))
{
- return self.coerce_unsized_look_through_fields(ty, goal);
+ return self.coerce_unsized_look_through_fields(ty.skip_norm_wip(), goal);
}
}
Err(MirEvalError::CoerceUnsizedError(ty.store()))
@@ -1768,8 +1777,8 @@ impl<'db> Evaluator<'db> {
}
TyKind::Adt(adt_def, target_subst) => match &current_ty.kind() {
TyKind::Adt(current_adt_def, current_subst) => {
- let id = adt_def.def_id().0;
- let current_id = current_adt_def.def_id().0;
+ let id = adt_def.def_id();
+ let current_id = current_adt_def.def_id();
if id != current_id {
not_supported!("unsizing struct with different type");
}
@@ -1784,10 +1793,12 @@ impl<'db> Evaluator<'db> {
};
let target_last_field = self.db.field_types(id.into())[last_field]
.get()
- .instantiate(self.interner(), target_subst);
+ .instantiate(self.interner(), target_subst)
+ .skip_norm_wip();
let current_last_field = self.db.field_types(id.into())[last_field]
.get()
- .instantiate(self.interner(), current_subst);
+ .instantiate(self.interner(), current_subst)
+ .skip_norm_wip();
return self.unsizing_ptr_from_addr(
target_last_field,
current_last_field,
@@ -1804,10 +1815,10 @@ impl<'db> Evaluator<'db> {
&mut self,
it: VariantId,
subst: GenericArgs<'db>,
- locals: &Locals,
+ locals: &Locals<'a>,
) -> Result<'db, (usize, Arc<Layout>, Option<(usize, usize, i128)>)> {
let adt = it.adt_id(self.db);
- if let DefWithBodyId::VariantId(f) = locals.body.owner
+ if let Some(f) = locals.body.owner.as_variant()
&& let VariantId::EnumVariantId(it) = it
&& let AdtId::EnumId(e) = adt
&& f.lookup(self.db).parent == e
@@ -1851,7 +1862,7 @@ impl<'db> Evaluator<'db> {
if have_tag {
Some((
layout.fields.offset(0).bytes_usize(),
- tag.size(&*self.target_data_layout).bytes_usize(),
+ tag.size(self.target_data_layout).bytes_usize(),
discriminant,
))
} else {
@@ -1898,7 +1909,7 @@ impl<'db> Evaluator<'db> {
Ok(result)
}
- fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result<'db, Interval> {
+ fn eval_operand(&mut self, it: &Operand, locals: &mut Locals<'a>) -> Result<'db, Interval> {
Ok(match &it.kind {
OperandKind::Copy(p) | OperandKind::Move(p) => {
locals.drop_flags.remove_place(p, &locals.body.projection_store);
@@ -2051,7 +2062,7 @@ impl<'db> Evaluator<'db> {
#[allow(clippy::double_parens)]
fn allocate_const_in_heap(
&mut self,
- locals: &Locals,
+ locals: &Locals<'a>,
konst: Const<'db>,
) -> Result<'db, Interval> {
match konst.kind() {
@@ -2059,9 +2070,9 @@ impl<'db> Evaluator<'db> {
ConstKind::Unevaluated(UnevaluatedConst { def: const_id, args: subst }) => {
let mut id = const_id.0;
let mut subst = subst;
- if let hir_def::GeneralConstId::ConstId(c) = id {
+ if let GeneralConstId::ConstId(c) = id {
let (c, s) = lookup_impl_const(&self.infcx, self.param_env.param_env, c, subst);
- id = hir_def::GeneralConstId::ConstId(c);
+ id = GeneralConstId::ConstId(c);
subst = s;
}
let allocation = match id {
@@ -2077,9 +2088,13 @@ impl<'db> Evaluator<'db> {
MirEvalError::ConstEvalError(name, Box::new(e))
})?
}
- GeneralConstId::AnonConstId(_) => {
- not_supported!("anonymous const evaluation")
- }
+ GeneralConstId::AnonConstId(anon_const_id) => self
+ .db
+ .anon_const_eval(anon_const_id, subst, Some(self.param_env))
+ .map_err(|e| {
+ let name = id.name(self.db);
+ MirEvalError::ConstEvalError(name, Box::new(e))
+ })?,
};
self.allocate_allocation_in_heap(locals, allocation)
}
@@ -2089,7 +2104,7 @@ impl<'db> Evaluator<'db> {
fn allocate_allocation_in_heap(
&mut self,
- locals: &Locals,
+ locals: &Locals<'a>,
allocation: Allocation<'db>,
) -> Result<'db, Interval> {
let AllocationData { ty, memory: ref v, ref memory_map } = *allocation;
@@ -2128,7 +2143,7 @@ impl<'db> Evaluator<'db> {
Ok(Interval::new(addr, size))
}
- fn eval_place(&mut self, p: &Place, locals: &Locals) -> Result<'db, Interval> {
+ fn eval_place(&mut self, p: &Place, locals: &Locals<'a>) -> Result<'db, Interval> {
let addr = self.place_addr(p, locals)?;
Ok(Interval::new(
addr,
@@ -2228,13 +2243,17 @@ impl<'db> Evaluator<'db> {
Ok(())
}
- fn size_align_of(&self, ty: Ty<'db>, locals: &Locals) -> Result<'db, Option<(usize, usize)>> {
+ fn size_align_of(
+ &self,
+ ty: Ty<'db>,
+ locals: &Locals<'a>,
+ ) -> Result<'db, Option<(usize, usize)>> {
if let Some(layout) = self.layout_cache.borrow().get(&ty) {
return Ok(layout
.is_sized()
.then(|| (layout.size.bytes_usize(), layout.align.bytes() as usize)));
}
- if let DefWithBodyId::VariantId(f) = locals.body.owner
+ if let Some(f) = locals.body.owner.as_variant()
&& let Some((AdtId::EnumId(e), _)) = ty.as_adt()
&& f.lookup(self.db).parent == e
{
@@ -2257,7 +2276,7 @@ impl<'db> Evaluator<'db> {
fn size_of_sized(
&self,
ty: Ty<'db>,
- locals: &Locals,
+ locals: &Locals<'a>,
what: &'static str,
) -> Result<'db, usize> {
match self.size_align_of(ty, locals)? {
@@ -2271,7 +2290,7 @@ impl<'db> Evaluator<'db> {
fn size_align_of_sized(
&self,
ty: Ty<'db>,
- locals: &Locals,
+ locals: &Locals<'a>,
what: &'static str,
) -> Result<'db, (usize, usize)> {
match self.size_align_of(ty, locals)? {
@@ -2312,13 +2331,13 @@ impl<'db> Evaluator<'db> {
&self,
bytes: &[u8],
ty: Ty<'db>,
- locals: &Locals,
+ locals: &Locals<'a>,
) -> Result<'db, ComplexMemoryMap<'db>> {
- fn rec<'db>(
- this: &Evaluator<'db>,
+ fn rec<'a, 'db: 'a>(
+ this: &Evaluator<'a, 'db>,
bytes: &[u8],
ty: Ty<'db>,
- locals: &Locals,
+ locals: &Locals<'a>,
mm: &mut ComplexMemoryMap<'db>,
stack_depth_limit: usize,
) -> Result<'db, ()> {
@@ -2409,7 +2428,7 @@ impl<'db> Evaluator<'db> {
)?;
}
}
- TyKind::Adt(adt, subst) => match adt.def_id().0 {
+ TyKind::Adt(adt, subst) => match adt.def_id() {
AdtId::StructId(s) => {
let data = s.fields(this.db);
let layout = this.layout(ty)?;
@@ -2419,7 +2438,10 @@ impl<'db> Evaluator<'db> {
.fields
.offset(u32::from(f.into_raw()) as usize)
.bytes_usize();
- let ty = field_types[f].get().instantiate(this.interner(), subst);
+ let ty = field_types[f]
+ .get()
+ .instantiate(this.interner(), subst)
+ .skip_norm_wip();
let size = this.layout(ty)?.size.bytes_usize();
rec(
this,
@@ -2436,7 +2458,7 @@ impl<'db> Evaluator<'db> {
if let Some((v, l)) = detect_variant_from_bytes(
&layout,
this.db,
- &this.target_data_layout,
+ this.target_data_layout,
bytes,
e,
) {
@@ -2445,7 +2467,10 @@ impl<'db> Evaluator<'db> {
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].get().instantiate(this.interner(), subst);
+ let ty = field_types[f]
+ .get()
+ .instantiate(this.interner(), subst)
+ .skip_norm_wip();
let size = this.layout(ty)?.size.bytes_usize();
rec(
this,
@@ -2487,7 +2512,7 @@ impl<'db> Evaluator<'db> {
ty_of_bytes: impl Fn(&[u8]) -> Result<'db, Ty<'db>> + Copy,
addr: Address,
ty: Ty<'db>,
- locals: &Locals,
+ locals: &Locals<'a>,
) -> Result<'db, ()> {
// FIXME: support indirect references
let layout = self.layout(ty)?;
@@ -2527,11 +2552,11 @@ impl<'db> Evaluator<'db> {
let new_id = self.vtable_map.id(ty);
self.write_memory(addr, &new_id.to_le_bytes())?;
}
- TyKind::Adt(id, args) => match id.def_id().0 {
+ TyKind::Adt(id, args) => match id.def_id() {
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.get().instantiate(self.interner(), args);
+ let ty = ty.get().instantiate(self.interner(), args).skip_norm_wip();
self.patch_addresses(
patch_map,
ty_of_bytes,
@@ -2546,13 +2571,13 @@ impl<'db> Evaluator<'db> {
if let Some((ev, layout)) = detect_variant_from_bytes(
&layout,
self.db,
- &self.target_data_layout,
+ self.target_data_layout,
self.read_memory(addr, layout.size.bytes_usize())?,
e,
) {
for (i, (_, ty)) in self.db.field_types(ev.into()).iter().enumerate() {
let offset = layout.fields.offset(i).bytes_usize();
- let ty = ty.get().instantiate(self.interner(), args);
+ let ty = ty.get().instantiate(self.interner(), args).skip_norm_wip();
self.patch_addresses(
patch_map,
ty_of_bytes,
@@ -2619,10 +2644,10 @@ impl<'db> Evaluator<'db> {
bytes: Interval,
destination: Interval,
args: &[IntervalAndTy<'db>],
- locals: &Locals,
+ locals: &Locals<'a>,
target_bb: Option<BasicBlockId>,
span: MirSpan,
- ) -> Result<'db, Option<StackFrame>> {
+ ) -> Result<'db, Option<StackFrame<'a>>> {
let id = from_bytes!(usize, bytes.get(self)?);
let next_ty = self.vtable_map.ty(id)?;
use rustc_type_ir::TyKind;
@@ -2650,9 +2675,9 @@ impl<'db> Evaluator<'db> {
generic_args: GenericArgs<'db>,
destination: Interval,
args: &[IntervalAndTy<'db>],
- locals: &Locals,
+ locals: &Locals<'a>,
span: MirSpan,
- ) -> Result<'db, Option<StackFrame>> {
+ ) -> Result<'db, Option<StackFrame<'a>>> {
let mir_body = self
.db
.monomorphized_mir_body_for_closure(
@@ -2688,10 +2713,10 @@ impl<'db> Evaluator<'db> {
generic_args: GenericArgs<'db>,
destination: Interval,
args: &[IntervalAndTy<'db>],
- locals: &Locals,
+ locals: &Locals<'a>,
target_bb: Option<BasicBlockId>,
span: MirSpan,
- ) -> Result<'db, Option<StackFrame>> {
+ ) -> Result<'db, Option<StackFrame<'a>>> {
match def {
CallableDefId::FunctionId(def) => {
if self.detect_fn_trait(def).is_some() {
@@ -2746,9 +2771,9 @@ impl<'db> Evaluator<'db> {
&self,
def: FunctionId,
generic_args: GenericArgs<'db>,
- locals: &Locals,
+ locals: &Locals<'a>,
span: MirSpan,
- ) -> Result<'db, MirOrDynIndex> {
+ ) -> Result<'db, MirOrDynIndex<'a>> {
let pair = (def, generic_args);
if let Some(r) = self.mir_or_dyn_index_cache.borrow().get(&pair) {
return Ok(r.clone());
@@ -2788,11 +2813,11 @@ impl<'db> Evaluator<'db> {
mut def: FunctionId,
args: &[IntervalAndTy<'db>],
generic_args: GenericArgs<'db>,
- locals: &Locals,
+ locals: &Locals<'a>,
destination: Interval,
target_bb: Option<BasicBlockId>,
span: MirSpan,
- ) -> Result<'db, Option<StackFrame>> {
+ ) -> Result<'db, Option<StackFrame<'a>>> {
if self.detect_and_exec_special_function(
def,
args,
@@ -2854,18 +2879,18 @@ impl<'db> Evaluator<'db> {
fn exec_looked_up_function(
&mut self,
- mir_body: Arc<MirBody>,
- locals: &Locals,
+ mir_body: &'a MirBody,
+ locals: &Locals<'a>,
def: FunctionId,
arg_bytes: impl Iterator<Item = IntervalOrOwned>,
span: MirSpan,
destination: Interval,
target_bb: Option<BasicBlockId>,
- ) -> Result<'db, Option<StackFrame>> {
+ ) -> Result<'db, Option<StackFrame<'a>>> {
Ok(if let Some(target_bb) = target_bb {
let (mut locals, prev_stack_ptr) =
- self.create_locals_for_body(&mir_body, Some(destination))?;
- self.fill_locals_for_body(&mir_body, &mut locals, arg_bytes.into_iter())?;
+ self.create_locals_for_body(mir_body, Some(destination))?;
+ self.fill_locals_for_body(mir_body, &mut locals, arg_bytes.into_iter())?;
let span = (span, locals.body.owner);
Some(StackFrame { locals, destination: Some(target_bb), prev_stack_ptr, span })
} else {
@@ -2885,11 +2910,11 @@ impl<'db> Evaluator<'db> {
def: FunctionId,
args: &[IntervalAndTy<'db>],
generic_args: GenericArgs<'db>,
- locals: &Locals,
+ locals: &Locals<'a>,
destination: Interval,
target_bb: Option<BasicBlockId>,
span: MirSpan,
- ) -> Result<'db, Option<StackFrame>> {
+ ) -> Result<'db, Option<StackFrame<'a>>> {
let func = args
.first()
.ok_or_else(|| MirEvalError::InternalError("fn trait with no arg".into()))?;
@@ -2954,7 +2979,7 @@ impl<'db> Evaluator<'db> {
}
}
- fn eval_static(&mut self, st: StaticId, locals: &Locals) -> Result<'db, Address> {
+ fn eval_static(&mut self, st: StaticId, locals: &Locals<'a>) -> Result<'db, Address> {
if let Some(o) = self.static_locations.get(&st) {
return Ok(*o);
};
@@ -3001,7 +3026,12 @@ impl<'db> Evaluator<'db> {
}
}
- fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<'db, ()> {
+ fn drop_place(
+ &mut self,
+ place: &Place,
+ locals: &mut Locals<'a>,
+ span: MirSpan,
+ ) -> Result<'db, ()> {
let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?;
if !locals.drop_flags.remove_place(place, &locals.body.projection_store) {
return Ok(());
@@ -3016,15 +3046,12 @@ impl<'db> Evaluator<'db> {
fn run_drop_glue_deep(
&mut self,
ty: Ty<'db>,
- locals: &Locals,
+ locals: &Locals<'a>,
addr: Address,
_metadata: &[u8],
span: MirSpan,
) -> Result<'db, ()> {
- let Some(drop_fn) = (|| {
- let drop_trait = self.lang_items().Drop?;
- drop_trait.trait_items(self.db).method_by_name(&Name::new_symbol_root(sym::drop))
- })() else {
+ let Some(drop_fn) = self.lang_items().Drop_drop else {
// in some tests we don't have drop trait in minicore, and
// we can ignore drop in them.
return Ok(());
@@ -3046,7 +3073,7 @@ impl<'db> Evaluator<'db> {
}
match ty.kind() {
TyKind::Adt(adt_def, subst) => {
- let id = adt_def.def_id().0;
+ let id = adt_def.def_id();
match id {
AdtId::StructId(s) => {
let data = StructSignature::of(self.db, s);
@@ -3066,7 +3093,8 @@ impl<'db> Evaluator<'db> {
let addr = addr.offset(offset);
let ty = field_types[field]
.get()
- .instantiate(self.interner(), subst);
+ .instantiate(self.interner(), subst)
+ .skip_norm_wip();
self.run_drop_glue_deep(ty, locals, addr, &[], span)?;
}
}
@@ -3122,7 +3150,7 @@ impl<'db> Evaluator<'db> {
pub fn render_const_using_debug_impl<'db>(
db: &'db dyn HirDatabase,
- owner: DefWithBodyId,
+ owner: InferBodyId,
c: Allocation<'db>,
ty: Ty<'db>,
) -> Result<'db, String> {
@@ -3135,16 +3163,9 @@ pub fn render_const_using_debug_impl<'db>(
drop_flags: DropFlags::default(),
};
let data = evaluator.allocate_allocation_in_heap(locals, c)?;
+ let lang_items = evaluator.interner().lang_items();
let resolver = owner.resolver(db);
- let Some(TypeNs::TraitId(debug_trait)) = resolver.resolve_path_in_type_ns_fully(
- db,
- &hir_def::expr_store::path::Path::from_known_path_with_no_generic(path![core::fmt::Debug]),
- ) else {
- not_supported!("core::fmt::Debug not found");
- };
- let Some(debug_fmt_fn) =
- debug_trait.trait_items(db).method_by_name(&Name::new_symbol_root(sym::fmt))
- else {
+ let Some(debug_fmt_fn) = lang_items.Debug_fmt else {
not_supported!("core::fmt::Debug::fmt not found");
};
// a1 = &[""]
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index 9586d38abc..b4a5aa8a87 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -4,9 +4,8 @@
use std::cmp::{self, Ordering};
use hir_def::{attrs::AttrFlags, signatures::FunctionSignature};
-use hir_expand::name::Name;
-use intern::sym;
-use rustc_type_ir::inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Ty as _};
+use rustc_abi::ExternAbi;
+use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, SliceLike, Ty as _};
use stdx::never;
use crate::{
@@ -29,13 +28,13 @@ enum EvalLangItem {
DropInPlace,
}
-impl<'db> Evaluator<'db> {
+impl<'a, 'db: 'a> Evaluator<'a, 'db> {
pub(super) fn detect_and_exec_special_function(
&mut self,
def: FunctionId,
args: &[IntervalAndTy<'db>],
generic_args: GenericArgs<'db>,
- locals: &Locals,
+ locals: &Locals<'a>,
destination: Interval,
span: MirSpan,
) -> Result<'db, bool> {
@@ -60,7 +59,9 @@ impl<'db> Evaluator<'db> {
);
}
let is_extern_c = match def.lookup(self.db).container {
- hir_def::ItemContainerId::ExternBlockId(block) => block.abi(self.db) == Some(sym::C),
+ hir_def::ItemContainerId::ExternBlockId(block) => {
+ matches!(block.abi(self.db), ExternAbi::C { .. })
+ }
_ => false,
};
if is_extern_c {
@@ -132,7 +133,7 @@ impl<'db> Evaluator<'db> {
def: FunctionId,
args: &[IntervalAndTy<'db>],
self_ty: Ty<'db>,
- locals: &Locals,
+ locals: &Locals<'a>,
destination: Interval,
span: MirSpan,
) -> Result<'db, ()> {
@@ -190,7 +191,7 @@ impl<'db> Evaluator<'db> {
layout: Arc<Layout>,
addr: Address,
def: FunctionId,
- locals: &Locals,
+ locals: &Locals<'a>,
destination: Interval,
span: MirSpan,
) -> Result<'db, ()> {
@@ -296,7 +297,7 @@ impl<'db> Evaluator<'db> {
it: EvalLangItem,
generic_args: GenericArgs<'db>,
args: &[IntervalAndTy<'db>],
- locals: &Locals,
+ locals: &Locals<'a>,
span: MirSpan,
) -> Result<'db, Vec<u8>> {
use EvalLangItem::*;
@@ -368,7 +369,7 @@ impl<'db> Evaluator<'db> {
id: i64,
args: &[IntervalAndTy<'db>],
destination: Interval,
- _locals: &Locals,
+ _locals: &Locals<'a>,
_span: MirSpan,
) -> Result<'db, ()> {
match id {
@@ -399,7 +400,7 @@ impl<'db> Evaluator<'db> {
args: &[IntervalAndTy<'db>],
_generic_args: GenericArgs<'db>,
destination: Interval,
- locals: &Locals,
+ locals: &Locals<'a>,
span: MirSpan,
) -> Result<'db, ()> {
match as_str {
@@ -563,7 +564,7 @@ impl<'db> Evaluator<'db> {
args: &[IntervalAndTy<'db>],
generic_args: GenericArgs<'db>,
destination: Interval,
- locals: &Locals,
+ locals: &Locals<'a>,
span: MirSpan,
needs_override: bool,
) -> Result<'db, bool> {
@@ -1202,11 +1203,7 @@ impl<'db> Evaluator<'db> {
let addr = tuple.interval.addr.offset(offset);
args.push(IntervalAndTy::new(addr, field, self, locals)?);
}
- if let Some(target) = self.lang_items().FnOnce
- && let Some(def) = target
- .trait_items(self.db)
- .method_by_name(&Name::new_symbol_root(sym::call_once))
- {
+ if let Some(def) = self.lang_items().FnOnce_call_once {
self.exec_fn_trait(
def,
&args,
@@ -1345,7 +1342,7 @@ impl<'db> Evaluator<'db> {
&mut self,
ty: Ty<'db>,
metadata: Interval,
- locals: &Locals,
+ locals: &Locals<'a>,
) -> Result<'db, (usize, usize)> {
Ok(match ty.kind() {
TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1),
@@ -1360,7 +1357,7 @@ impl<'db> Evaluator<'db> {
"dyn concrete type",
)?,
TyKind::Adt(adt_def, subst) => {
- let id = adt_def.def_id().0;
+ let id = adt_def.def_id();
let layout = self.layout_adt(id, subst)?;
let id = match id {
AdtId::StructId(s) => s,
@@ -1373,7 +1370,8 @@ impl<'db> Evaluator<'db> {
.unwrap()
.1
.get()
- .instantiate(self.interner(), subst);
+ .instantiate(self.interner(), subst)
+ .skip_norm_wip();
let sized_part_size =
layout.fields.offset(field_types.iter().count() - 1).bytes_usize();
let sized_part_align = layout.align.bytes() as usize;
@@ -1404,7 +1402,7 @@ impl<'db> Evaluator<'db> {
args: &[IntervalAndTy<'db>],
generic_args: GenericArgs<'db>,
destination: Interval,
- locals: &Locals,
+ locals: &Locals<'a>,
_span: MirSpan,
) -> Result<'db, ()> {
// We are a single threaded runtime with no UB checking and no optimization, so
diff --git a/crates/hir-ty/src/mir/eval/shim/simd.rs b/crates/hir-ty/src/mir/eval/shim/simd.rs
index e0b3e571b8..074c5a9c77 100644
--- a/crates/hir-ty/src/mir/eval/shim/simd.rs
+++ b/crates/hir-ty/src/mir/eval/shim/simd.rs
@@ -6,14 +6,14 @@ use crate::consteval::try_const_usize;
use super::*;
-impl<'db> Evaluator<'db> {
+impl<'a, 'db: 'a> Evaluator<'a, 'db> {
fn detect_simd_ty(&self, ty: Ty<'db>) -> Result<'db, (usize, Ty<'db>)> {
match ty.kind() {
TyKind::Adt(adt_def, subst) => {
let len = match subst.as_slice().get(1).and_then(|it| it.konst()) {
Some(len) => len,
_ => {
- if let AdtId::StructId(id) = adt_def.def_id().0 {
+ if let AdtId::StructId(id) = adt_def.def_id() {
let struct_data = id.fields(self.db);
let fields = struct_data.fields();
let Some((first_field, _)) = fields.iter().next() else {
@@ -21,7 +21,8 @@ impl<'db> Evaluator<'db> {
};
let field_ty = self.db.field_types(id.into())[first_field]
.get()
- .instantiate(self.interner(), subst);
+ .instantiate(self.interner(), subst)
+ .skip_norm_wip();
return Ok((fields.len(), field_ty));
}
return Err(MirEvalError::InternalError(
@@ -53,7 +54,7 @@ impl<'db> Evaluator<'db> {
args: &[IntervalAndTy<'db>],
_generic_args: GenericArgs<'db>,
destination: Interval,
- _locals: &Locals,
+ _locals: &Locals<'a>,
_span: MirSpan,
) -> Result<'db, ()> {
match name {
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 0f0ed729c9..3852db909e 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -4,8 +4,8 @@ use std::{fmt::Write, iter, mem};
use base_db::Crate;
use hir_def::{
- AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GeneralConstId, GenericParamId,
- HasModule, ItemContainerId, LocalFieldId, Lookup, TraitId, TupleId,
+ AdtId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, GenericParamId, HasModule,
+ ItemContainerId, LocalFieldId, Lookup, TraitId, TupleId,
expr_store::{Body, ExpressionStore, HygieneId, path::Path},
hir::{
ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ClosureKind, ExprId, ExprOrPatId,
@@ -22,19 +22,18 @@ use itertools::{EitherOrBoth, Itertools};
use la_arena::ArenaMap;
use rustc_apfloat::Float;
use rustc_hash::FxHashMap;
-use rustc_type_ir::inherent::{AdtDef, Const as _, GenericArgs as _, IntoKind, Ty as _};
+use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _};
use span::{Edition, FileId};
use syntax::TextRange;
-use triomphe::Arc;
use crate::{
- Adjust, Adjustment, AutoBorrow, CallableDefId, ParamEnvAndCrate,
+ Adjust, Adjustment, AutoBorrow, CallableDefId, InferBodyId, ParamEnvAndCrate,
consteval::ConstEvalError,
- db::{HirDatabase, InternedClosure, InternedClosureId},
+ db::{GeneralConstId, HirDatabase, InternedClosure, InternedClosureId},
display::{DisplayTarget, HirDisplay, hir_display_with_store},
generics::generics,
infer::{
- CaptureSourceStack, CapturedPlace, TypeMismatch, UpvarCapture,
+ CaptureSourceStack, CapturedPlace, UpvarCapture,
cast::CastTy,
closure::analysis::expr_use_visitor::{
Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
@@ -82,7 +81,8 @@ struct DropScope {
struct MirLowerCtx<'a, 'db> {
result: MirBody,
- owner: DefWithBodyId,
+ owner: InferBodyId,
+ store_owner: ExpressionStoreOwnerId,
current_loop_blocks: Option<LoopBlocks>,
labeled_loop_blocks: FxHashMap<LabelId, LoopBlocks>,
discr_temp: Option<Place>,
@@ -110,8 +110,7 @@ pub enum MirLowerError {
UnresolvedMethod(String),
UnresolvedField,
UnsizedTemporary(StoredTy),
- MissingFunctionDefinition(DefWithBodyId, ExprId),
- TypeMismatch(TypeMismatch),
+ MissingFunctionDefinition(InferBodyId, ExprId),
HasErrors,
/// This should never happen. Type mismatch should catch everything.
TypeError(&'static str),
@@ -190,20 +189,21 @@ impl MirLowerError {
}
}
MirLowerError::MissingFunctionDefinition(owner, it) => {
- let body = Body::of(db, *owner);
+ let owner = owner.expression_store_owner(db);
+ let store = ExpressionStore::of(db, owner);
writeln!(
f,
"Missing function definition for {}",
- body.pretty_print_expr(db, *owner, *it, display_target.edition)
+ hir_def::expr_store::pretty::print_expr_hir(
+ db,
+ store,
+ owner,
+ *it,
+ display_target.edition
+ )
)?;
}
MirLowerError::HasErrors => writeln!(f, "Type inference result contains errors")?,
- MirLowerError::TypeMismatch(e) => writeln!(
- f,
- "Type mismatch: Expected {}, found {}",
- e.expected.as_ref().display(db, display_target),
- e.actual.as_ref().display(db, display_target),
- )?,
MirLowerError::GenericArgNotProvided(id, subst) => {
let param_name = match *id {
GenericParamId::TypeParamId(id) => {
@@ -289,7 +289,7 @@ type Result<'db, T> = std::result::Result<T, MirLowerError>;
impl<'a, 'db> MirLowerCtx<'a, 'db> {
fn new(
db: &'db dyn HirDatabase,
- owner: DefWithBodyId,
+ owner: InferBodyId,
store: &'a ExpressionStore,
infer: &'a InferenceResult,
) -> Self {
@@ -312,8 +312,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
owner,
closures: vec![],
};
- let resolver = owner.resolver(db);
- let env = db.trait_environment(ExpressionStoreOwnerId::from(owner));
+ let store_owner = owner.expression_store_owner(db);
+ let resolver = store_owner.resolver(db);
+ let env = db.trait_environment(store_owner);
let interner = DbInterner::new_with(db, resolver.krate());
// FIXME(next-solver): Is `non_body_analysis()` correct here? Don't we want to reveal opaque types defined by this body?
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
@@ -325,6 +326,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
store,
types: crate::next_solver::default_types(db),
owner,
+ store_owner,
resolver,
current_loop_blocks: None,
labeled_loop_blocks: Default::default(),
@@ -475,7 +477,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
not_supported!("builtin#asm")
}
Expr::Missing => {
- if let DefWithBodyId::FunctionId(f) = self.owner {
+ if let Some(f) = self.owner.as_function() {
let assoc = f.lookup(self.db);
if let ItemContainerId::TraitId(t) = assoc.container {
let name = &FunctionSignature::of(self.db, f).name;
@@ -505,7 +507,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
}
} else {
let resolver_guard =
- self.resolver.update_to_inner_scope(self.db, self.owner, expr_id);
+ self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
let hygiene = self.store.expr_path_hygiene(expr_id);
let result = self
.resolver
@@ -563,14 +565,9 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
Ok(Some(current))
}
ValueNs::GenericParam(p) => {
- let Some(def) = self.owner.as_generic_def_id(self.db) else {
- not_supported!("owner without generic def id");
- };
+ let def = self.owner.generic_def(self.db);
let generics = generics(self.db, def);
- let index = generics
- .type_or_const_param_idx(p.into())
- .ok_or(MirLowerError::TypeError("fail to lower const generic param"))?
- as u32;
+ let index = generics.type_or_const_param_idx(p.into());
self.push_assignment(
current,
place,
@@ -581,7 +578,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
ParamConst { id: p, index },
)
.store(),
- ty: self.db.const_param_ty_ns(p).store(),
+ ty: self.db.const_param_ty(p).store(),
},
span: None,
}),
@@ -626,7 +623,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
};
self.push_fake_read(current, cond_place, expr_id.into());
let resolver_guard =
- self.resolver.update_to_inner_scope(self.db, self.owner, expr_id);
+ self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
let (then_target, else_target) =
self.pattern_match(current, None, cond_place, *pat)?;
self.resolver.reset_to_guard(resolver_guard);
@@ -764,7 +761,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
self.push_fake_read(current, cond_place, expr_id.into());
let mut end = None;
let resolver_guard =
- self.resolver.update_to_inner_scope(self.db, self.owner, expr_id);
+ self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
for MatchArm { pat, guard, expr } in arms.iter() {
let (then, mut otherwise) =
self.pattern_match(current, None, cond_place, *pat)?;
@@ -886,13 +883,12 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
RecordSpread::FieldDefaults => not_supported!("empty record spread"),
};
let variant_id =
- self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path {
- Some(p) => MirLowerError::UnresolvedName(
- hir_display_with_store(&**p, self.store)
+ self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| {
+ MirLowerError::UnresolvedName(
+ hir_display_with_store(path, self.store)
.display(self.db, self.display_target())
.to_string(),
- ),
- None => MirLowerError::RecordLiteralWithoutPath,
+ )
})?;
let subst = match self.expr_ty_without_adjust(expr_id).kind() {
TyKind::Adt(_, s) => s,
@@ -1194,7 +1190,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
};
self.push_fake_read(current, value, expr_id.into());
let resolver_guard =
- self.resolver.update_to_inner_scope(self.db, self.owner, expr_id);
+ self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
current = self.pattern_match_assignment(current, value, target)?;
self.resolver.reset_to_guard(resolver_guard);
Ok(Some(current))
@@ -1255,7 +1251,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
not_supported!("closure with non closure type");
};
self.result.closures.push(id.0);
- let closure_data = &self.infer.closures_data[&id.0.loc(self.db).1];
+ let closure_data = &self.infer.closures_data[&id.0.loc(self.db).expr];
let span = |sources: &[CaptureSourceStack]| match sources
.first()
@@ -1541,23 +1537,16 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
subst: GenericArgs<'db>,
const_id: GeneralConstId,
) -> Result<'db, Operand> {
- if matches!(const_id, GeneralConstId::AnonConstId(_)) {
- // FIXME:
- not_supported!("anon consts are not supported yet in const eval");
- }
let konst = Const::new_unevaluated(
self.interner(),
UnevaluatedConst { def: const_id.into(), args: subst },
);
- let ty = self
- .db
- .value_ty(match const_id {
- GeneralConstId::ConstId(id) => id.into(),
- GeneralConstId::StaticId(id) => id.into(),
- GeneralConstId::AnonConstId(_) => unreachable!("handled above"),
- })
- .unwrap()
- .instantiate(self.interner(), subst);
+ let ty = match const_id {
+ GeneralConstId::ConstId(id) => self.db.value_ty(id.into()).unwrap(),
+ GeneralConstId::StaticId(id) => self.db.value_ty(id.into()).unwrap(),
+ GeneralConstId::AnonConstId(id) => id.loc(self.db).ty.get(),
+ };
+ let ty = ty.instantiate(self.interner(), subst).skip_norm_wip();
Ok(Operand {
kind: OperandKind::Constant { konst: konst.store(), ty: ty.store() },
span: None,
@@ -1841,8 +1830,11 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
self.push_fake_read(current, init_place, span);
// Using the initializer for the resolver scope is good enough for us, as it cannot create new declarations
// and has all declarations of the `let`.
- let resolver_guard =
- self.resolver.update_to_inner_scope(self.db, self.owner, *expr_id);
+ let resolver_guard = self.resolver.update_to_inner_scope(
+ self.db,
+ self.store_owner,
+ *expr_id,
+ );
(current, else_block) =
self.pattern_match(current, None, init_place, *pat)?;
self.resolver.reset_to_guard(resolver_guard);
@@ -1973,8 +1965,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
match self.result.binding_locals.get(b) {
Some(it) => Ok(*it),
None => {
- // FIXME: It should never happens, but currently it will happen in `const_dependent_on_local` test, which
- // is a hir lowering problem IMO.
+ // FIXME: It should never happens, but currently it will happen in some cases, not sure when exactly.
// never!("Using inaccessible local for binding is always a bug");
Err(MirLowerError::InaccessibleLocal)
}
@@ -2098,7 +2089,7 @@ fn convert_closure_capture_projections(
}
TyKind::Adt(adt_def, _) => {
let local_field_id = LocalFieldId::from_raw(RawIdx::from_u32(field_idx));
- let field = match adt_def.def_id().0 {
+ let field = match adt_def.def_id() {
AdtId::StructId(id) => {
FieldId { parent: id.into(), local_id: local_field_id }
}
@@ -2141,16 +2132,15 @@ fn cast_kind<'db>(
})
}
+#[salsa_macros::tracked(returns(ref), cycle_result = mir_body_for_closure_cycle_result)]
pub fn mir_body_for_closure_query<'db>(
db: &'db dyn HirDatabase,
closure: InternedClosureId,
-) -> Result<'db, Arc<MirBody>> {
- let InternedClosure(owner, expr) = closure.loc(db);
- let body_owner =
- owner.as_def_with_body().expect("MIR lowering should only happen for body-owned closures");
- let body = Body::of(db, body_owner);
+) -> Result<'db, MirBody> {
+ let InternedClosure { owner: body_owner, expr, .. } = closure.loc(db);
+ let store = ExpressionStore::of(db, body_owner.expression_store_owner(db));
let infer = InferenceResult::of(db, body_owner);
- let Expr::Closure { args, body: root, .. } = &body[expr] else {
+ let Expr::Closure { args, body: root, .. } = &store[expr] else {
implementation_error!("closure expression is not closure");
};
let crate::next_solver::TyKind::Closure(_, substs) = infer.expr_ty(expr).kind() else {
@@ -2158,7 +2148,7 @@ pub fn mir_body_for_closure_query<'db>(
};
let kind = substs.as_closure().kind();
let captures = infer.closures_data[&expr].min_captures.values().flatten();
- let mut ctx = MirLowerCtx::new(db, body_owner, &body.store, infer);
+ let mut ctx = MirLowerCtx::new(db, body_owner, store, infer);
// 0 is return local
ctx.result.locals.alloc(Local { ty: infer.expr_ty(*root).store() });
@@ -2183,7 +2173,7 @@ pub fn mir_body_for_closure_query<'db>(
ctx.result.param_locals.push(closure_local);
let sig = ctx.interner().signature_unclosure(substs.as_closure().sig(), Safety::Safe);
- let resolver_guard = ctx.resolver.update_to_inner_scope(db, body_owner, expr);
+ let resolver_guard = ctx.resolver.update_to_inner_scope(db, ctx.store_owner, expr);
let current = ctx.lower_params_and_bindings(
args.iter().zip(sig.skip_binder().inputs().iter()).map(|(it, y)| (*it, *y)),
None,
@@ -2297,49 +2287,66 @@ pub fn mir_body_for_closure_query<'db>(
return Err(MirLowerError::UnresolvedUpvar(err));
}
ctx.result.shrink_to_fit();
- Ok(Arc::new(ctx.result))
+ Ok(ctx.result)
}
-pub fn mir_body_query<'db>(
- db: &'db dyn HirDatabase,
- def: DefWithBodyId,
-) -> Result<'db, Arc<MirBody>> {
+#[salsa_macros::tracked(returns(ref), cycle_result = mir_body_cycle_result)]
+pub fn mir_body_query<'db>(db: &'db dyn HirDatabase, def: InferBodyId) -> Result<'db, MirBody> {
let krate = def.krate(db);
let edition = krate.data(db).edition;
let detail = match def {
- DefWithBodyId::FunctionId(it) => {
+ InferBodyId::DefWithBodyId(DefWithBodyId::FunctionId(it)) => {
FunctionSignature::of(db, it).name.display(db, edition).to_string()
}
- DefWithBodyId::StaticId(it) => {
+ InferBodyId::DefWithBodyId(DefWithBodyId::StaticId(it)) => {
StaticSignature::of(db, it).name.display(db, edition).to_string()
}
- DefWithBodyId::ConstId(it) => ConstSignature::of(db, it)
+ InferBodyId::DefWithBodyId(DefWithBodyId::ConstId(it)) => ConstSignature::of(db, it)
.name
.clone()
.unwrap_or_else(Name::missing)
.display(db, edition)
.to_string(),
- DefWithBodyId::VariantId(it) => {
+ InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(it)) => {
let loc = it.lookup(db);
loc.parent.enum_variants(db).variants[loc.index as usize]
.1
.display(db, edition)
.to_string()
}
+ InferBodyId::AnonConstId(_) => "{const}".to_owned(),
};
let _p = tracing::info_span!("mir_body_query", ?detail).entered();
- let body = Body::of(db, def);
+ let (store, root_expr, self_param, params) = match def {
+ InferBodyId::DefWithBodyId(def) => {
+ let body = Body::of(db, def);
+ (&**body, body.root_expr(), body.self_param(), &*body.params)
+ }
+ InferBodyId::AnonConstId(def) => {
+ let loc = def.loc(db);
+ let store = ExpressionStore::of(db, loc.owner);
+ (store, loc.expr, None, &[][..])
+ }
+ };
let infer = InferenceResult::of(db, def);
- let mut result = lower_body_to_mir(db, def, body, infer, body.root_expr())?;
+ let mut result = lower_body_to_mir(db, def, store, infer, root_expr, self_param, params)?;
result.shrink_to_fit();
- Ok(Arc::new(result))
+ Ok(result)
}
-pub(crate) fn mir_body_cycle_result<'db>(
+fn mir_body_cycle_result<'db>(
_db: &'db dyn HirDatabase,
_: salsa::Id,
- _def: DefWithBodyId,
-) -> Result<'db, Arc<MirBody>> {
+ _def: InferBodyId,
+) -> Result<'db, MirBody> {
+ Err(MirLowerError::Loop)
+}
+
+fn mir_body_for_closure_cycle_result<'db>(
+ _db: &'db dyn HirDatabase,
+ _: salsa::Id,
+ _def: InternedClosureId,
+) -> Result<'db, MirBody> {
Err(MirLowerError::Loop)
}
@@ -2347,42 +2354,31 @@ pub(crate) fn mir_body_cycle_result<'db>(
/// then delegates to [`lower_to_mir_with_store`].
pub fn lower_body_to_mir<'db>(
db: &'db dyn HirDatabase,
- owner: DefWithBodyId,
- body: &Body,
+ owner: InferBodyId,
+ store: &ExpressionStore,
infer: &InferenceResult,
- // FIXME: root_expr should always be the body.body_expr,
- // but this is currently also used for `X` in `[(); X]` which live in the same expression store
root_expr: ExprId,
+ self_param: Option<BindingId>,
+ params: &[PatId],
) -> Result<'db, MirBody> {
- let is_root = root_expr == body.root_expr();
// Extract params and self_param only when lowering the body's root expression for a function.
- if is_root && let DefWithBodyId::FunctionId(fid) = owner {
+ if let Some(fid) = owner.as_function() {
let callable_sig =
db.callable_item_signature(fid.into()).instantiate_identity().skip_binder();
let mut param_tys = callable_sig.inputs().iter().copied();
- let self_param = body.self_param.and_then(|id| Some((id, param_tys.next()?)));
+ let self_param = self_param.and_then(|id| Some((id, param_tys.next()?)));
lower_to_mir_with_store(
db,
owner,
- &body.store,
+ store,
infer,
root_expr,
- body.params.iter().copied().zip(param_tys),
+ params.iter().copied().zip(param_tys),
self_param,
- is_root,
)
} else {
- lower_to_mir_with_store(
- db,
- owner,
- &body.store,
- infer,
- root_expr,
- iter::empty(),
- None,
- is_root,
- )
+ lower_to_mir_with_store(db, owner, store, infer, root_expr, iter::empty(), None)
}
}
@@ -2392,24 +2388,22 @@ pub fn lower_body_to_mir<'db>(
/// const (picks bindings owned by `root_expr`).
pub fn lower_to_mir_with_store<'db>(
db: &'db dyn HirDatabase,
- owner: DefWithBodyId,
+ owner: InferBodyId,
store: &ExpressionStore,
infer: &InferenceResult,
root_expr: ExprId,
params: impl Iterator<Item = (PatId, Ty<'db>)> + Clone,
self_param: Option<(BindingId, Ty<'db>)>,
- is_root: bool,
) -> Result<'db, MirBody> {
- if infer.type_mismatches().next().is_some() || infer.is_erroneous() {
+ if infer.has_type_mismatches() || infer.is_erroneous() {
return Err(MirLowerError::HasErrors);
}
let mut ctx = MirLowerCtx::new(db, owner, store, infer);
// 0 is return local
ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr).store() });
- let binding_picker = |b: BindingId| {
- let owner = ctx.store.binding_owner(b);
- if is_root { owner.is_none() } else { owner == Some(root_expr) }
- };
+ let expected_binding_owner =
+ if matches!(owner, InferBodyId::DefWithBodyId(_)) { None } else { Some(root_expr) };
+ let binding_picker = |b: BindingId| ctx.store.binding_owner(b) == expected_binding_owner;
let current = ctx.lower_params_and_bindings(params, self_param, 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, root_expr.into())?;
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index fb4a9add81..2ed7aedecf 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -1,7 +1,6 @@
//! MIR lowering for places
use hir_def::FunctionId;
-use intern::sym;
use rustc_type_ir::inherent::{Region as _, Ty as _};
use super::*;
@@ -131,7 +130,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
match &self.store[expr_id] {
Expr::Path(p) => {
let resolver_guard =
- self.resolver.update_to_inner_scope(self.db, self.owner, expr_id);
+ self.resolver.update_to_inner_scope(self.db, self.store_owner, expr_id);
let hygiene = self.store.expr_path_hygiene(expr_id);
let resolved = self.resolver.resolve_path_in_value_ns_fully(self.db, p, hygiene);
self.resolver.reset_to_guard(resolver_guard);
@@ -183,10 +182,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
expr_id.into(),
'b: {
if let Some((f, _)) = self.infer.method_resolution(expr_id)
- && let Some(deref_trait) = self.lang_items().DerefMut
- && let Some(deref_fn) = deref_trait
- .trait_items(self.db)
- .method_by_name(&Name::new_symbol_root(sym::deref_mut))
+ && let Some(deref_fn) = self.lang_items().DerefMut_deref_mut
{
break 'b deref_fn == f;
}
@@ -315,18 +311,12 @@ impl<'db> MirLowerCtx<'_, 'db> {
mutability: bool,
) -> Result<'db, Option<(Place, BasicBlockId)>> {
let lang_items = self.lang_items();
- let (mutability, trait_lang_item, trait_method_name, borrow_kind) = if !mutability {
- (
- Mutability::Not,
- lang_items.Deref,
- Name::new_symbol_root(sym::deref),
- BorrowKind::Shared,
- )
+ let (mutability, deref_fn, borrow_kind) = if !mutability {
+ (Mutability::Not, lang_items.Deref_deref, BorrowKind::Shared)
} else {
(
Mutability::Mut,
- lang_items.DerefMut,
- Name::new_symbol_root(sym::deref_mut),
+ lang_items.DerefMut_deref_mut,
BorrowKind::Mut { kind: MutBorrowKind::Default },
)
};
@@ -335,11 +325,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
let target_ty_ref = Ty::new_ref(self.interner(), error_region, target_ty, mutability);
let ref_place: Place = self.temp(ty_ref, current, span)?.into();
self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span);
- let deref_trait = trait_lang_item.ok_or(MirLowerError::LangItemNotFound)?;
- let deref_fn = deref_trait
- .trait_items(self.db)
- .method_by_name(&trait_method_name)
- .ok_or(MirLowerError::LangItemNotFound)?;
+ let deref_fn = deref_fn.ok_or(MirLowerError::LangItemNotFound)?;
let deref_fn_op = Operand::const_zst(Ty::new_fn_def(
self.interner(),
CallableDefId::FunctionId(deref_fn).into(),
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 99c5f0fc65..c924c5bdf0 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -4,7 +4,7 @@ use hir_def::{hir::ExprId, signatures::VariantFields};
use rustc_type_ir::inherent::{IntoKind, Ty as _};
use crate::{
- BindingMode,
+ BindingMode, ByRef,
mir::{
LocalId, MutBorrowKind, Operand, OperandKind,
lower::{
@@ -104,7 +104,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
self.pattern_match_binding(
id,
- BindingMode::Move,
+ BindingMode(ByRef::No, rustc_ast_ir::Mutability::Not),
local.into(),
MirSpan::SelfParam,
current,
@@ -132,7 +132,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
.into(),
);
Ok(match &self.store[pattern] {
- Pat::Missing => return Err(MirLowerError::IncompletePattern),
+ Pat::Missing | Pat::Rest => return Err(MirLowerError::IncompletePattern),
Pat::Wild => (current, current_else),
Pat::Tuple { args, ellipsis } => {
let subst = match self.infer.pat_ty(pattern).kind() {
@@ -248,9 +248,18 @@ impl<'db> MirLowerCtx<'_, 'db> {
(current, current_else)
}
Pat::Slice { prefix, slice, suffix } => {
+ let pat_ty = self.infer.pat_ty(pattern);
+ // FIXME: MIR lowering should be skipped for bodies with inference errors. Once
+ // that happens, this recovery for invalid slice patterns can be removed.
+ if !matches!(pat_ty.kind(), TyKind::Array(..) | TyKind::Slice(_)) {
+ return Err(MirLowerError::TypeError(
+ "non array or slice type matched with slice pattern",
+ ));
+ }
+
if mode == MatchingMode::Check {
// emit runtime length check for slice
- if let TyKind::Slice(_) = self.infer.pat_ty(pattern).kind() {
+ if let TyKind::Slice(_) = pat_ty.kind() {
let pattern_len = prefix.len() + suffix.len();
let place_len: Place = self
.temp(Ty::new_usize(self.interner()), current, pattern.into())?
@@ -385,7 +394,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
self.push_match_assignment(
current,
local,
- BindingMode::Move,
+ BindingMode(ByRef::No, rustc_ast_ir::Mutability::Not),
cond_place,
pattern.into(),
);
@@ -504,6 +513,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
(current, current_else)
}
Pat::Box { .. } => not_supported!("box pattern"),
+ Pat::Deref { .. } => not_supported!("deref pattern"),
Pat::ConstBlock(_) => not_supported!("const block pattern"),
})
}
@@ -535,13 +545,13 @@ impl<'db> MirLowerCtx<'_, 'db> {
current,
target_place.into(),
match mode {
- BindingMode::Move => {
+ BindingMode(ByRef::No, _) => {
Operand { kind: OperandKind::Copy(cond_place), span: None }.into()
}
- BindingMode::Ref(rustc_ast_ir::Mutability::Not) => {
+ BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Not), _) => {
Rvalue::Ref(BorrowKind::Shared, cond_place)
}
- BindingMode::Ref(rustc_ast_ir::Mutability::Mut) => {
+ BindingMode(ByRef::Yes(rustc_ast_ir::Mutability::Mut), _) => {
Rvalue::Ref(BorrowKind::Mut { kind: MutBorrowKind::Default }, cond_place)
}
},
diff --git a/crates/hir-ty/src/mir/lower/tests.rs b/crates/hir-ty/src/mir/lower/tests.rs
index 73399dab7f..8e10284cc1 100644
--- a/crates/hir-ty/src/mir/lower/tests.rs
+++ b/crates/hir-ty/src/mir/lower/tests.rs
@@ -78,7 +78,7 @@ fn check_borrowck(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
}
for body in bodies {
- let _ = db.borrowck(body);
+ let _ = db.borrowck(body.into());
}
})
}
diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs
index 41044f00c2..06871a3f18 100644
--- a/crates/hir-ty/src/mir/monomorphization.rs
+++ b/crates/hir-ty/src/mir/monomorphization.rs
@@ -7,15 +7,13 @@
//!
//! So the monomorphization should be called even if the substitution is empty.
-use hir_def::DefWithBodyId;
use rustc_type_ir::inherent::IntoKind;
use rustc_type_ir::{
FallibleTypeFolder, TypeFlags, TypeFoldable, TypeSuperFoldable, TypeVisitableExt,
};
-use triomphe::Arc;
use crate::{
- ParamEnvAndCrate,
+ InferBodyId, ParamEnvAndCrate,
next_solver::{
Allocation, AllocationData, Const, ConstKind, Region, RegionKind, StoredConst,
StoredGenericArgs, StoredTy,
@@ -65,6 +63,9 @@ impl<'db> FallibleTypeFolder<DbInterner<'db>> for Filler<'db> {
ty,
)
.map_err(|_| MirLowerError::NotSupported("can't normalize alias".to_owned()))?;
+ // Normalization could introduce infer vars (for example, if the alias cannot be normalized),
+ // and we must not have infer vars in the body.
+ let ty = ty.replace_infer_with_error(self.infcx.interner);
ty.try_super_fold_with(self)
}
TyKind::Param(param) => Ok(self
@@ -237,38 +238,50 @@ impl<'db> Filler<'db> {
}
}
+#[salsa_macros::tracked(returns(ref), cycle_result = monomorphized_mir_body_cycle_result)]
pub fn monomorphized_mir_body_query(
db: &dyn HirDatabase,
- owner: DefWithBodyId,
+ owner: InferBodyId,
subst: StoredGenericArgs,
trait_env: StoredParamEnvAndCrate,
-) -> Result<Arc<MirBody>, MirLowerError> {
+) -> Result<MirBody, MirLowerError> {
let mut filler = Filler::new(db, trait_env.as_ref(), subst.as_ref());
let body = db.mir_body(owner)?;
let mut body = (*body).clone();
filler.fill_body(&mut body)?;
- Ok(Arc::new(body))
+ Ok(body)
}
-pub(crate) fn monomorphized_mir_body_cycle_result(
+fn monomorphized_mir_body_cycle_result(
_db: &dyn HirDatabase,
_: salsa::Id,
- _: DefWithBodyId,
+ _: InferBodyId,
_: StoredGenericArgs,
_: StoredParamEnvAndCrate,
-) -> Result<Arc<MirBody>, MirLowerError> {
+) -> Result<MirBody, MirLowerError> {
Err(MirLowerError::Loop)
}
+#[salsa_macros::tracked(returns(ref), cycle_result = monomorphized_mir_body_for_closure_cycle_result)]
pub fn monomorphized_mir_body_for_closure_query(
db: &dyn HirDatabase,
closure: InternedClosureId,
subst: StoredGenericArgs,
trait_env: StoredParamEnvAndCrate,
-) -> Result<Arc<MirBody>, MirLowerError> {
+) -> Result<MirBody, MirLowerError> {
let mut filler = Filler::new(db, trait_env.as_ref(), subst.as_ref());
let body = db.mir_body_for_closure(closure)?;
let mut body = (*body).clone();
filler.fill_body(&mut body)?;
- Ok(Arc::new(body))
+ Ok(body)
+}
+
+fn monomorphized_mir_body_for_closure_cycle_result(
+ _db: &dyn HirDatabase,
+ _: salsa::Id,
+ _: InternedClosureId,
+ _: StoredGenericArgs,
+ _: StoredParamEnvAndCrate,
+) -> Result<MirBody, MirLowerError> {
+ Err(MirLowerError::Loop)
}
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index de5ee223a1..777cf170bc 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -7,7 +7,7 @@ use std::{
use either::Either;
use hir_def::{
- expr_store::Body,
+ expr_store::ExpressionStore,
hir::BindingId,
signatures::{ConstSignature, EnumSignature, FunctionSignature, StaticSignature},
};
@@ -15,6 +15,7 @@ use hir_expand::{Lookup, name::Name};
use la_arena::ArenaMap;
use crate::{
+ InferBodyId,
db::{HirDatabase, InternedClosureId},
display::{ClosureStyle, DisplayTarget, HirDisplay},
mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind},
@@ -42,18 +43,18 @@ macro_rules! wln {
impl MirBody {
pub fn pretty_print(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
- let hir_body = Body::of(db, self.owner);
+ let hir_body = ExpressionStore::of(db, self.owner.expression_store_owner(db));
let mut ctx = MirPrettyCtx::new(self, hir_body, db, display_target);
ctx.for_body(|this| match ctx.body.owner {
- hir_def::DefWithBodyId::FunctionId(id) => {
+ InferBodyId::DefWithBodyId(hir_def::DefWithBodyId::FunctionId(id)) => {
let data = FunctionSignature::of(db, id);
w!(this, "fn {}() ", data.name.display(db, this.display_target.edition));
}
- hir_def::DefWithBodyId::StaticId(id) => {
+ InferBodyId::DefWithBodyId(hir_def::DefWithBodyId::StaticId(id)) => {
let data = StaticSignature::of(db, id);
w!(this, "static {}: _ = ", data.name.display(db, this.display_target.edition));
}
- hir_def::DefWithBodyId::ConstId(id) => {
+ InferBodyId::DefWithBodyId(hir_def::DefWithBodyId::ConstId(id)) => {
let data = ConstSignature::of(db, id);
w!(
this,
@@ -64,7 +65,7 @@ impl MirBody {
.display(db, this.display_target.edition)
);
}
- hir_def::DefWithBodyId::VariantId(id) => {
+ InferBodyId::DefWithBodyId(hir_def::DefWithBodyId::VariantId(id)) => {
let loc = id.lookup(db);
let edition = this.display_target.edition;
w!(
@@ -78,6 +79,7 @@ impl MirBody {
.display(db, edition),
)
}
+ InferBodyId::AnonConstId(_) => w!(this, "{{const}}"),
});
ctx.result
}
@@ -97,7 +99,7 @@ impl MirBody {
struct MirPrettyCtx<'a, 'db> {
body: &'a MirBody,
- hir_body: &'a Body,
+ hir_body: &'a ExpressionStore,
db: &'db dyn HirDatabase,
result: String,
indent: String,
@@ -160,7 +162,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> {
let result = mem::take(&mut self.result);
let indent = mem::take(&mut self.indent);
let mut ctx = MirPrettyCtx {
- body: &body,
+ body,
local_to_binding: body.local_to_binding_map(),
result,
indent,
@@ -184,7 +186,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> {
fn new(
body: &'a MirBody,
- hir_body: &'a Body,
+ hir_body: &'a ExpressionStore,
db: &'db dyn HirDatabase,
display_target: DisplayTarget,
) -> Self {
diff --git a/crates/hir-ty/src/next_solver.rs b/crates/hir-ty/src/next_solver.rs
index 161a3142df..47b4b1dc4a 100644
--- a/crates/hir-ty/src/next_solver.rs
+++ b/crates/hir-ty/src/next_solver.rs
@@ -39,6 +39,7 @@ pub use interner::*;
pub use opaques::*;
pub use predicate::*;
pub use region::*;
+use rustc_type_ir::MayBeErased;
pub use solver::*;
pub use ty::*;
@@ -48,6 +49,7 @@ pub use rustc_ast_ir::Mutability;
pub type Binder<'db, T> = rustc_type_ir::Binder<DbInterner<'db>, T>;
pub type EarlyBinder<'db, T> = rustc_type_ir::EarlyBinder<DbInterner<'db>, T>;
+pub type Unnormalized<'db, T> = rustc_type_ir::Unnormalized<DbInterner<'db>, T>;
pub type Canonical<'db, T> = rustc_type_ir::Canonical<DbInterner<'db>, T>;
pub type CanonicalVarValues<'db> = rustc_type_ir::CanonicalVarValues<DbInterner<'db>>;
pub type CanonicalVarKind<'db> = rustc_type_ir::CanonicalVarKind<DbInterner<'db>>;
@@ -55,7 +57,7 @@ pub type CanonicalQueryInput<'db, V> = rustc_type_ir::CanonicalQueryInput<DbInte
pub type AliasTy<'db> = rustc_type_ir::AliasTy<DbInterner<'db>>;
pub type FnSig<'db> = rustc_type_ir::FnSig<DbInterner<'db>>;
pub type PolyFnSig<'db> = Binder<'db, rustc_type_ir::FnSig<DbInterner<'db>>>;
-pub type TypingMode<'db> = rustc_type_ir::TypingMode<DbInterner<'db>>;
+pub type TypingMode<'db, S = MayBeErased> = rustc_type_ir::TypingMode<DbInterner<'db>, S>;
pub type TypeError<'db> = rustc_type_ir::error::TypeError<DbInterner<'db>>;
pub type QueryResult<'db> = rustc_type_ir::solve::QueryResult<DbInterner<'db>>;
pub type FxIndexMap<K, V> = rustc_type_ir::data_structures::IndexMap<K, V>;
@@ -85,8 +87,13 @@ pub struct DefaultTypes<'db> {
pub error: Ty<'db>,
/// `&'static str`
pub static_str_ref: Ty<'db>,
+ /// `[u8]`
+ pub u8_slice: Ty<'db>,
+ /// `&'static [u8]`
+ pub static_u8_slice: Ty<'db>,
/// `*mut ()`
pub mut_unit_ptr: Ty<'db>,
+ pub dyn_trait_dummy_self: Ty<'db>,
}
pub struct DefaultConsts<'db> {
@@ -236,6 +243,8 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> {
let empty_tys = create_tys(&[]);
let unit = create_ty(TyKind::Tuple(empty_tys));
let u8 = create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U8));
+ let u8_slice = create_ty(TyKind::Slice(u8));
+ let static_u8_slice = create_ty(TyKind::Ref(statik, u8_slice, Mutability::Not));
DefaultAny {
types: DefaultTypes {
usize: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::Usize)),
@@ -261,7 +270,11 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> {
never: create_ty(TyKind::Never),
error: create_ty(TyKind::Error(ErrorGuaranteed)),
static_str_ref: create_ty(TyKind::Ref(statik, str, rustc_ast_ir::Mutability::Not)),
+ u8_slice,
+ static_u8_slice,
mut_unit_ptr: create_ty(TyKind::RawPtr(unit, rustc_ast_ir::Mutability::Mut)),
+ // This type must not appear anywhere except here.
+ dyn_trait_dummy_self: create_ty(TyKind::Infer(rustc_type_ir::InferTy::FreshTy(0))),
},
consts: DefaultConsts {
error: create_const(ConstKind::Error(ErrorGuaranteed)),
diff --git a/crates/hir-ty/src/next_solver/abi.rs b/crates/hir-ty/src/next_solver/abi.rs
index 1813abab86..3121d5631f 100644
--- a/crates/hir-ty/src/next_solver/abi.rs
+++ b/crates/hir-ty/src/next_solver/abi.rs
@@ -1,7 +1,10 @@
//! ABI-related things in the next-trait-solver.
-use rustc_type_ir::{error::TypeError, relate::Relate};
-
-use crate::FnAbi;
+use rustc_abi::ExternAbi;
+use rustc_ast_ir::visit::VisitorResult;
+use rustc_type_ir::{
+ FallibleTypeFolder, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, error::TypeError,
+ relate::Relate,
+};
use super::interner::DbInterner;
@@ -40,9 +43,32 @@ impl<'db> rustc_type_ir::inherent::Safety<DbInterner<'db>> for Safety {
Self::Safe => "",
}
}
+
+ fn unsafe_mode() -> Self {
+ Safety::Unsafe
+ }
+}
+
+impl<'db> TypeVisitable<DbInterner<'db>> for ExternAbi {
+ fn visit_with<V: TypeVisitor<DbInterner<'db>>>(&self, _visitor: &mut V) -> V::Result {
+ V::Result::output()
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for ExternAbi {
+ fn try_fold_with<F: FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ _folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(self)
+ }
+
+ fn fold_with<F: TypeFolder<DbInterner<'db>>>(self, _folder: &mut F) -> Self {
+ self
+ }
}
-impl<'db> Relate<DbInterner<'db>> for FnAbi {
+impl<'db> Relate<DbInterner<'db>> for ExternAbi {
fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
_relation: &mut R,
a: Self,
@@ -55,13 +81,3 @@ impl<'db> Relate<DbInterner<'db>> for FnAbi {
}
}
}
-
-impl<'db> rustc_type_ir::inherent::Abi<DbInterner<'db>> for FnAbi {
- fn rust() -> Self {
- FnAbi::Rust
- }
-
- fn is_rust(self) -> bool {
- matches!(self, FnAbi::Rust)
- }
-}
diff --git a/crates/hir-ty/src/next_solver/binder.rs b/crates/hir-ty/src/next_solver/binder.rs
index 3645f8096c..95f437165d 100644
--- a/crates/hir-ty/src/next_solver/binder.rs
+++ b/crates/hir-ty/src/next_solver/binder.rs
@@ -1,9 +1,10 @@
-use crate::{
- FnAbi,
- next_solver::{
- Binder, Clauses, EarlyBinder, FnSig, PolyFnSig, StoredBoundVarKinds, StoredClauses,
- StoredTy, StoredTys, Ty, abi::Safety,
- },
+use hir_def::TraitId;
+use macros::{TypeFoldable, TypeVisitable};
+
+use crate::next_solver::{
+ Binder, Clauses, DbInterner, EarlyBinder, FnSig, FnSigKind, GenericArg, PolyFnSig,
+ StoredBoundVarKinds, StoredClauses, StoredGenericArg, StoredGenericArgs, StoredTy, StoredTys,
+ TraitRef, Ty,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -38,6 +39,13 @@ impl StoredEarlyBinder<StoredTy> {
}
}
+impl StoredEarlyBinder<StoredGenericArg> {
+ #[inline]
+ pub fn get<'db>(&self) -> EarlyBinder<'db, GenericArg<'db>> {
+ self.get_with(|it| it.as_ref())
+ }
+}
+
impl StoredEarlyBinder<StoredClauses> {
#[inline]
pub fn get<'db>(&self) -> EarlyBinder<'db, Clauses<'db>> {
@@ -45,13 +53,25 @@ impl StoredEarlyBinder<StoredClauses> {
}
}
+impl StoredEarlyBinder<StoredPolyFnSig> {
+ #[inline]
+ pub fn get<'db>(&'db self) -> EarlyBinder<'db, PolyFnSig<'db>> {
+ self.get_with(|it| it.get())
+ }
+}
+
+impl StoredEarlyBinder<StoredTraitRef> {
+ #[inline]
+ pub fn get<'db>(&'db self, interner: DbInterner<'db>) -> EarlyBinder<'db, TraitRef<'db>> {
+ self.get_with(|it| it.get(interner))
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StoredPolyFnSig {
bound_vars: StoredBoundVarKinds,
inputs_and_output: StoredTys,
- c_variadic: bool,
- safety: Safety,
- abi: FnAbi,
+ fn_sig_kind: FnSigKind<'static>,
}
impl StoredPolyFnSig {
@@ -62,9 +82,11 @@ impl StoredPolyFnSig {
Self {
bound_vars,
inputs_and_output: sig.inputs_and_output.store(),
- c_variadic: sig.c_variadic,
- safety: sig.safety,
- abi: sig.abi,
+ fn_sig_kind: FnSigKind::new(
+ sig.fn_sig_kind.abi(),
+ sig.fn_sig_kind.safety(),
+ sig.fn_sig_kind.c_variadic(),
+ ),
}
}
@@ -73,11 +95,28 @@ impl StoredPolyFnSig {
Binder::bind_with_vars(
FnSig {
inputs_and_output: self.inputs_and_output.as_ref(),
- c_variadic: self.c_variadic,
- safety: self.safety,
- abi: self.abi,
+ fn_sig_kind: self.fn_sig_kind,
},
self.bound_vars.as_ref(),
)
}
}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
+pub struct StoredTraitRef {
+ #[type_visitable(ignore)]
+ def_id: TraitId,
+ args: StoredGenericArgs,
+}
+
+impl StoredTraitRef {
+ #[inline]
+ pub fn new(trait_ref: TraitRef<'_>) -> Self {
+ Self { def_id: trait_ref.def_id.0, args: trait_ref.args.store() }
+ }
+
+ #[inline]
+ pub fn get<'db>(&'db self, interner: DbInterner<'db>) -> TraitRef<'db> {
+ TraitRef::new_from_args(interner, self.def_id.into(), self.args.as_ref())
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/consts.rs b/crates/hir-ty/src/next_solver/consts.rs
index fa90e3d8a0..2df9a5259d 100644
--- a/crates/hir-ty/src/next_solver/consts.rs
+++ b/crates/hir-ty/src/next_solver/consts.rs
@@ -11,13 +11,14 @@ use rustc_ast_ir::visit::VisitorResult;
use rustc_type_ir::{
BoundVar, BoundVarIndexKind, ConstVid, DebruijnIndex, FlagComputation, Flags,
GenericTypeVisitable, InferConst, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
- TypeVisitable, WithCachedTypeInfo, inherent::IntoKind, relate::Relate,
+ TypeVisitable, TypeVisitableExt, WithCachedTypeInfo, inherent::IntoKind, relate::Relate,
};
use crate::{
ParamEnvAndCrate,
next_solver::{
- AllocationData, impl_foldable_for_interned_slice, impl_stored_interned, interned_slice,
+ AllocationData, ClauseKind, ParamEnv, impl_foldable_for_interned_slice,
+ impl_stored_interned, interned_slice,
},
};
@@ -146,6 +147,40 @@ impl std::fmt::Debug for ParamConst {
}
}
+impl ParamConst {
+ pub fn find_const_ty_from_env<'db>(self, env: ParamEnv<'db>) -> Ty<'db> {
+ let mut candidates = env.clauses.iter().filter_map(|clause| {
+ // `ConstArgHasType` are never desugared to be higher ranked.
+ match clause.kind().skip_binder() {
+ ClauseKind::ConstArgHasType(param_ct, ty) => {
+ assert!(!(param_ct, ty).has_escaping_bound_vars());
+
+ match param_ct.kind() {
+ ConstKind::Param(param_ct) if param_ct.index == self.index => Some(ty),
+ _ => None,
+ }
+ }
+ _ => None,
+ }
+ });
+
+ // N.B. it may be tempting to fix ICEs by making this function return
+ // `Option<Ty<'db>>` instead of `Ty<'db>`; however, this is generally
+ // considered to be a bandaid solution, since it hides more important
+ // underlying issues with how we construct generics and predicates of
+ // items. It's advised to fix the underlying issue rather than trying
+ // to modify this function.
+ let ty = candidates.next().unwrap_or_else(|| {
+ panic!("cannot find `{self:?}` in param-env: {env:#?}");
+ });
+ assert!(
+ candidates.next().is_none(),
+ "did not expect duplicate `ConstParamHasTy` for `{self:?}` in param-env: {env:#?}"
+ );
+ ty
+ }
+}
+
#[derive(
Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable, GenericTypeVisitable,
)]
diff --git a/crates/hir-ty/src/next_solver/def_id.rs b/crates/hir-ty/src/next_solver/def_id.rs
index 542eca3ded..63337b297d 100644
--- a/crates/hir-ty/src/next_solver/def_id.rs
+++ b/crates/hir-ty/src/next_solver/def_id.rs
@@ -1,9 +1,9 @@
//! Definition of `SolverDefId`
use hir_def::{
- AdtId, AnonConstId, AttrDefId, BuiltinDeriveImplId, CallableDefId, ConstId, DefWithBodyId,
- EnumId, EnumVariantId, ExpressionStoreOwnerId, FunctionId, GeneralConstId, GenericDefId,
- ImplId, StaticId, StructId, TraitId, TypeAliasId, UnionId, VariantId,
+ AdtId, AttrDefId, BuiltinDeriveImplId, CallableDefId, ConstId, DefWithBodyId, EnumId,
+ EnumVariantId, ExpressionStoreOwnerId, FunctionId, GenericDefId, ImplId, StaticId, StructId,
+ TraitId, TypeAliasId, UnionId, VariantId,
signatures::{
ConstSignature, EnumSignature, FunctionSignature, StaticSignature, StructSignature,
TraitSignature, TypeAliasSignature, UnionSignature,
@@ -12,8 +12,12 @@ use hir_def::{
use rustc_type_ir::inherent;
use stdx::impl_from;
-use crate::db::{
- InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId, InternedOpaqueTyId,
+use crate::{
+ InferBodyId,
+ db::{
+ AnonConstId, GeneralConstId, InternedClosureId, InternedCoroutineClosureId,
+ InternedCoroutineId, InternedOpaqueTyId,
+ },
};
use super::DbInterner;
@@ -173,6 +177,16 @@ impl From<DefWithBodyId> for SolverDefId {
}
}
+impl From<InferBodyId> for SolverDefId {
+ #[inline]
+ fn from(value: InferBodyId) -> Self {
+ match value {
+ InferBodyId::DefWithBodyId(id) => id.into(),
+ InferBodyId::AnonConstId(id) => id.into(),
+ }
+ }
+}
+
impl From<VariantId> for SolverDefId {
#[inline]
fn from(value: VariantId) -> Self {
@@ -246,6 +260,32 @@ impl TryFrom<SolverDefId> for DefWithBodyId {
}
}
+impl TryFrom<SolverDefId> for InferBodyId {
+ type Error = ();
+
+ #[inline]
+ fn try_from(value: SolverDefId) -> Result<Self, Self::Error> {
+ let id = match value {
+ SolverDefId::ConstId(id) => id.into(),
+ SolverDefId::FunctionId(id) => id.into(),
+ SolverDefId::StaticId(id) => id.into(),
+ SolverDefId::EnumVariantId(id) | SolverDefId::Ctor(Ctor::Enum(id)) => id.into(),
+ SolverDefId::AnonConstId(id) => id.into(),
+ SolverDefId::InternedOpaqueTyId(_)
+ | SolverDefId::TraitId(_)
+ | SolverDefId::TypeAliasId(_)
+ | SolverDefId::ImplId(_)
+ | SolverDefId::BuiltinDeriveImplId(_)
+ | SolverDefId::InternedClosureId(_)
+ | SolverDefId::InternedCoroutineId(_)
+ | SolverDefId::InternedCoroutineClosureId(_)
+ | SolverDefId::Ctor(Ctor::Struct(_))
+ | SolverDefId::AdtId(_) => return Err(()),
+ };
+ Ok(id)
+ }
+}
+
impl TryFrom<SolverDefId> for GenericDefId {
type Error = ();
@@ -270,26 +310,6 @@ impl TryFrom<SolverDefId> for GenericDefId {
}
}
-impl SolverDefId {
- #[inline]
- #[track_caller]
- pub fn expect_opaque_ty(self) -> InternedOpaqueTyId {
- match self {
- SolverDefId::InternedOpaqueTyId(it) => it,
- _ => panic!("expected opaque type, found {self:?}"),
- }
- }
-
- #[inline]
- #[track_caller]
- pub fn expect_type_alias(self) -> TypeAliasId {
- match self {
- SolverDefId::TypeAliasId(it) => it,
- _ => panic!("expected type alias, found {self:?}"),
- }
- }
-}
-
impl<'db> inherent::DefId<DbInterner<'db>> for SolverDefId {
fn as_local(self) -> Option<SolverDefId> {
Some(self)
@@ -301,6 +321,26 @@ impl<'db> inherent::DefId<DbInterner<'db>> for SolverDefId {
macro_rules! declare_id_wrapper {
($name:ident, $wraps:ident) => {
+ declare_id_wrapper!($name, $wraps, SolverDefId);
+ };
+
+ ($name:ident, $wraps:ident, $local:ident) => {
+ declare_id_wrapper!($name, $wraps, $local, no_try_from);
+
+ impl TryFrom<SolverDefId> for $name {
+ type Error = ();
+
+ #[inline]
+ fn try_from(value: SolverDefId) -> Result<Self, Self::Error> {
+ match value {
+ SolverDefId::$wraps(it) => Ok(Self(it)),
+ _ => Err(()),
+ }
+ }
+ }
+ };
+
+ ($name:ident, $wraps:ident, $local:ident, no_try_from) => {
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct $name(pub $wraps);
@@ -331,35 +371,106 @@ macro_rules! declare_id_wrapper {
}
}
- impl TryFrom<SolverDefId> for $name {
+ impl<'db> inherent::DefId<DbInterner<'db>, $local> for $name {
+ fn as_local(self) -> Option<$local> {
+ Some(self.into())
+ }
+ fn is_local(self) -> bool {
+ true
+ }
+ }
+ };
+}
+
+declare_id_wrapper!(TraitIdWrapper, TraitId);
+declare_id_wrapper!(TypeAliasIdWrapper, TypeAliasId);
+declare_id_wrapper!(ClosureIdWrapper, InternedClosureId);
+declare_id_wrapper!(CoroutineIdWrapper, InternedCoroutineId);
+declare_id_wrapper!(CoroutineClosureIdWrapper, InternedCoroutineClosureId);
+declare_id_wrapper!(AdtIdWrapper, AdtId);
+declare_id_wrapper!(OpaqueTyIdWrapper, InternedOpaqueTyId, OpaqueTyIdWrapper);
+
+macro_rules! declare_ty_const_pair {
+ ( $ty_id_name:ident, $const_id_name:ident, $term_id_name:ident ) => {
+ declare_id_wrapper!($ty_id_name, TypeAliasId);
+ declare_id_wrapper!($const_id_name, ConstId);
+ declare_id_wrapper!($term_id_name, TermId, SolverDefId, no_try_from);
+
+ impl TryFrom<SolverDefId> for $term_id_name {
type Error = ();
#[inline]
fn try_from(value: SolverDefId) -> Result<Self, Self::Error> {
match value {
- SolverDefId::$wraps(it) => Ok(Self(it)),
+ SolverDefId::TypeAliasId(it) => Ok(Self(TermId::TypeAliasId(it))),
+ SolverDefId::ConstId(it) => Ok(Self(TermId::ConstId(it))),
_ => Err(()),
}
}
}
- impl<'db> inherent::DefId<DbInterner<'db>> for $name {
- fn as_local(self) -> Option<SolverDefId> {
- Some(self.into())
+ impl From<$ty_id_name> for $term_id_name {
+ fn from(value: $ty_id_name) -> Self {
+ $term_id_name(TermId::TypeAliasId(value.0))
}
- fn is_local(self) -> bool {
- true
+ }
+
+ impl From<$const_id_name> for $term_id_name {
+ fn from(value: $const_id_name) -> Self {
+ $term_id_name(TermId::ConstId(value.0))
+ }
+ }
+
+ impl TryFrom<$term_id_name> for $ty_id_name {
+ type Error = ();
+
+ fn try_from(value: $term_id_name) -> Result<Self, Self::Error> {
+ match value.0 {
+ TermId::TypeAliasId(id) => Ok($ty_id_name(id)),
+ TermId::ConstId(_) => Err(()),
+ }
+ }
+ }
+
+ impl TryFrom<$term_id_name> for $const_id_name {
+ type Error = ();
+
+ fn try_from(value: $term_id_name) -> Result<Self, Self::Error> {
+ match value.0 {
+ TermId::ConstId(id) => Ok($const_id_name(id)),
+ TermId::TypeAliasId(_) => Err(()),
+ }
+ }
+ }
+
+ impl From<$const_id_name> for GeneralConstIdWrapper {
+ fn from(value: $const_id_name) -> Self {
+ GeneralConstIdWrapper(GeneralConstId::ConstId(value.0))
}
}
};
}
-declare_id_wrapper!(TraitIdWrapper, TraitId);
-declare_id_wrapper!(TypeAliasIdWrapper, TypeAliasId);
-declare_id_wrapper!(ClosureIdWrapper, InternedClosureId);
-declare_id_wrapper!(CoroutineIdWrapper, InternedCoroutineId);
-declare_id_wrapper!(CoroutineClosureIdWrapper, InternedCoroutineClosureId);
-declare_id_wrapper!(AdtIdWrapper, AdtId);
+declare_ty_const_pair!(TraitAssocTyId, TraitAssocConstId, TraitAssocTermId);
+declare_ty_const_pair!(ImplOrTraitAssocTyId, ImplOrTraitAssocConstId, ImplOrTraitAssocTermId);
+declare_ty_const_pair!(FreeTyAliasId, FreeConstAliasId, FreeTermAliasId);
+declare_ty_const_pair!(InherentAssocTyId, InherentAssocConstId, InherentAssocTermId);
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum TermId {
+ TypeAliasId(TypeAliasId),
+ ConstId(ConstId),
+}
+impl_from!(TypeAliasId, ConstId for TermId);
+
+impl From<TermId> for SolverDefId {
+ fn from(value: TermId) -> Self {
+ match value {
+ TermId::TypeAliasId(id) => id.into(),
+ TermId::ConstId(id) => id.into(),
+ }
+ }
+}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct GeneralConstIdWrapper(pub GeneralConstId);
diff --git a/crates/hir-ty/src/next_solver/format_proof_tree.rs b/crates/hir-ty/src/next_solver/format_proof_tree.rs
index 66da6d5400..4b2f66a17d 100644
--- a/crates/hir-ty/src/next_solver/format_proof_tree.rs
+++ b/crates/hir-ty/src/next_solver/format_proof_tree.rs
@@ -3,7 +3,7 @@ use serde_derive::{Deserialize, Serialize};
use crate::next_solver::inspect::{InspectCandidate, InspectGoal};
use crate::next_solver::{AnyImplId, infer::InferCtxt};
-use crate::next_solver::{DbInterner, Span};
+use crate::{Span, next_solver::DbInterner};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProofTreeData {
@@ -59,7 +59,7 @@ impl<'a, 'db> ProofTreeSerializer<'a, 'db> {
let mut nested = Vec::new();
self.infcx.probe(|_| {
- for nested_goal in candidate.instantiate_nested_goals() {
+ for nested_goal in candidate.instantiate_nested_goals(Span::Dummy) {
nested.push(self.serialize_goal(&nested_goal));
}
});
diff --git a/crates/hir-ty/src/next_solver/fulfill.rs b/crates/hir-ty/src/next_solver/fulfill.rs
index 6739795a00..33dd33cb1d 100644
--- a/crates/hir-ty/src/next_solver/fulfill.rs
+++ b/crates/hir-ty/src/next_solver/fulfill.rs
@@ -9,17 +9,20 @@ use rustc_next_trait_solver::{
};
use rustc_type_ir::{
Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
- inherent::{IntoKind, Span as _},
+ inherent::IntoKind,
solve::{Certainty, NoSolution},
};
-use crate::next_solver::{
- DbInterner, SolverContext, SolverDefId, Span, Ty, TyKind, TypingMode,
- infer::{
- InferCtxt,
- traits::{PredicateObligation, PredicateObligations},
+use crate::{
+ Span,
+ next_solver::{
+ DbInterner, SolverContext, SolverDefId, Ty, TyKind, TypingMode,
+ infer::{
+ InferCtxt,
+ traits::{PredicateObligation, PredicateObligations},
+ },
+ inspect::ProofTreeVisitor,
},
- inspect::ProofTreeVisitor,
};
type PendingObligations<'db> =
@@ -36,7 +39,7 @@ type PendingObligations<'db> =
///
/// It is also likely that we want to use slightly different datastructures
/// here as this will have to deal with far more root goals than `evaluate_all`.
-#[derive(Debug, Clone)]
+#[derive(Debug)]
pub struct FulfillmentCtxt<'db> {
obligations: ObligationStorage<'db>,
@@ -44,7 +47,6 @@ pub struct FulfillmentCtxt<'db> {
/// outside of this snapshot leads to subtle bugs if the snapshot
/// gets rolled back. Because of this we explicitly check that we only
/// use the context in exactly this snapshot.
- #[expect(unused)]
usable_in_snapshot: usize,
try_evaluate_obligations_scratch: PendingObligations<'db>,
}
@@ -98,7 +100,7 @@ impl<'db> ObligationStorage<'db> {
let goal = o.as_goal();
let result = <&SolverContext<'db>>::from(infcx).evaluate_root_goal(
goal,
- Span::dummy(),
+ o.cause.span(),
stalled_on.take(),
);
matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))
@@ -120,24 +122,22 @@ impl<'db> FulfillmentCtxt<'db> {
}
impl<'db> FulfillmentCtxt<'db> {
- #[tracing::instrument(level = "trace", skip(self, _infcx))]
+ #[tracing::instrument(level = "trace", skip(self, infcx))]
pub(crate) fn register_predicate_obligation(
&mut self,
- _infcx: &InferCtxt<'db>,
+ infcx: &InferCtxt<'db>,
obligation: PredicateObligation<'db>,
) {
- // FIXME: See the comment in `try_evaluate_obligations()`.
- // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
+ assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
self.obligations.register(obligation, None);
}
pub(crate) fn register_predicate_obligations(
&mut self,
- _infcx: &InferCtxt<'db>,
+ infcx: &InferCtxt<'db>,
obligations: impl IntoIterator<Item = PredicateObligation<'db>>,
) {
- // FIXME: See the comment in `try_evaluate_obligations()`.
- // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
+ assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
obligations.into_iter().for_each(|obligation| self.obligations.register(obligation, None));
}
@@ -157,11 +157,7 @@ impl<'db> FulfillmentCtxt<'db> {
&mut self,
infcx: &InferCtxt<'db>,
) -> Vec<NextSolverError<'db>> {
- // FIXME(next-solver): We should bring this assertion back. Currently it panics because
- // there are places which use `InferenceTable` and open a snapshot and register obligations
- // and select. They should use a different `ObligationCtxt` instead. Then we'll be also able
- // to not put the obligations queue in `InferenceTable`'s snapshots.
- // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
+ assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
self.try_evaluate_obligations_scratch.clear();
let mut errors = Vec::new();
loop {
@@ -176,7 +172,9 @@ impl<'db> FulfillmentCtxt<'db> {
let goal = obligation.as_goal();
let delegate = <&SolverContext<'db>>::from(infcx);
- if let Some(certainty) = delegate.compute_goal_fast_path(goal, Span::dummy()) {
+ if let Some(certainty) =
+ delegate.compute_goal_fast_path(goal, obligation.cause.span())
+ {
match certainty {
Certainty::Yes => {}
Certainty::Maybe { .. } => {
@@ -186,9 +184,11 @@ impl<'db> FulfillmentCtxt<'db> {
continue;
}
- let result = delegate.evaluate_root_goal(goal, Span::dummy(), stalled_on);
+ let result = delegate.evaluate_root_goal(goal, obligation.cause.span(), stalled_on);
infcx.inspect_evaluated_obligation(&obligation, &result, || {
- Some(delegate.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1)
+ Some(
+ delegate.evaluate_root_goal_for_proof_tree(goal, obligation.cause.span()).1,
+ )
});
let GoalEvaluation { goal: _, certainty, has_changed, stalled_on } = match result {
Ok(result) => result,
@@ -243,7 +243,7 @@ impl<'db> FulfillmentCtxt<'db> {
&mut self,
infcx: &InferCtxt<'db>,
) -> PredicateObligations<'db> {
- let stalled_coroutines = match infcx.typing_mode() {
+ let stalled_coroutines = match infcx.typing_mode_raw().assert_not_erased() {
TypingMode::Analysis { defining_opaque_types_and_generators } => {
defining_opaque_types_and_generators
}
@@ -266,6 +266,7 @@ impl<'db> FulfillmentCtxt<'db> {
obl.as_goal(),
&mut StalledOnCoroutines {
stalled_coroutines,
+ span: obl.cause.span(),
cache: Default::default(),
},
)
@@ -287,12 +288,17 @@ impl<'db> FulfillmentCtxt<'db> {
/// so we want to keep this visitor *precise* too.
pub struct StalledOnCoroutines<'a, 'db> {
pub stalled_coroutines: &'a [SolverDefId],
+ pub span: Span,
pub cache: FxHashSet<Ty<'db>>,
}
impl<'db> ProofTreeVisitor<'db> for StalledOnCoroutines<'_, 'db> {
type Result = ControlFlow<()>;
+ fn span(&self) -> Span {
+ self.span
+ }
+
fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'db>) -> Self::Result {
inspect_goal.goal().predicate.visit_with(self)?;
@@ -324,7 +330,7 @@ impl<'db> TypeVisitor<DbInterner<'db>> for StalledOnCoroutines<'_, 'db> {
}
}
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub enum NextSolverError<'db> {
TrueError(PredicateObligation<'db>),
Ambiguity(PredicateObligation<'db>),
diff --git a/crates/hir-ty/src/next_solver/generic_arg.rs b/crates/hir-ty/src/next_solver/generic_arg.rs
index 72cf2f9f07..51f070cd64 100644
--- a/crates/hir-ty/src/next_solver/generic_arg.rs
+++ b/crates/hir-ty/src/next_solver/generic_arg.rs
@@ -8,6 +8,7 @@
use std::{hint::unreachable_unchecked, marker::PhantomData, ptr::NonNull};
+use arrayvec::ArrayVec;
use hir_def::{GenericDefId, GenericParamId};
use intern::InternedRef;
use rustc_type_ir::{
@@ -18,7 +19,6 @@ use rustc_type_ir::{
relate::{Relate, VarianceDiagInfo},
walk::TypeWalker,
};
-use smallvec::SmallVec;
use crate::next_solver::{
ConstInterned, RegionInterned, TyInterned, impl_foldable_for_interned_slice, interned_slice,
@@ -194,6 +194,25 @@ impl std::fmt::Debug for StoredGenericArg {
}
}
+impl<'db> TypeVisitable<DbInterner<'db>> for StoredGenericArg {
+ fn visit_with<V: TypeVisitor<DbInterner<'db>>>(&self, visitor: &mut V) -> V::Result {
+ self.as_ref().visit_with(visitor)
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for StoredGenericArg {
+ fn try_fold_with<F: FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(self.as_ref().try_fold_with(folder)?.store())
+ }
+
+ fn fold_with<F: TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ self.as_ref().fold_with(folder).store()
+ }
+}
+
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct GenericArg<'db> {
ptr: GenericArgImpl<'db>,
@@ -457,7 +476,66 @@ impl_foldable_for_interned_slice!(GenericArgs);
impl<'db> rustc_type_ir::inherent::GenericArg<DbInterner<'db>> for GenericArg<'db> {}
+impl<'db> TypeVisitable<DbInterner<'db>> for StoredGenericArgs {
+ fn visit_with<V: TypeVisitor<DbInterner<'db>>>(&self, visitor: &mut V) -> V::Result {
+ self.as_ref().visit_with(visitor)
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for StoredGenericArgs {
+ fn try_fold_with<F: FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(self.as_ref().try_fold_with(folder)?.store())
+ }
+
+ fn fold_with<F: TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ self.as_ref().fold_with(folder).store()
+ }
+}
+
+trait GenericArgsBuilder<'db>: AsRef<[GenericArg<'db>]> {
+ fn push(&mut self, arg: GenericArg<'db>);
+}
+
+impl<'db, const N: usize> GenericArgsBuilder<'db> for ArrayVec<GenericArg<'db>, N> {
+ fn push(&mut self, arg: GenericArg<'db>) {
+ self.push(arg);
+ }
+}
+
+impl<'db> GenericArgsBuilder<'db> for Vec<GenericArg<'db>> {
+ fn push(&mut self, arg: GenericArg<'db>) {
+ self.push(arg);
+ }
+}
+
impl<'db> GenericArgs<'db> {
+ #[inline(always)]
+ fn fill_builder<F>(
+ args: &mut impl GenericArgsBuilder<'db>,
+ defs: &Generics<'db>,
+ mut mk_kind: F,
+ ) where
+ F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>,
+ {
+ defs.iter_id().enumerate().for_each(|(idx, param_id)| {
+ let new_arg = mk_kind(idx as u32, param_id, args.as_ref());
+ args.push(new_arg);
+ });
+ }
+
+ #[cold]
+ fn fill_vec_builder<F>(defs: &Generics<'db>, count: usize, mk_kind: F) -> GenericArgs<'db>
+ where
+ F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>,
+ {
+ let mut args = Vec::with_capacity(count);
+ Self::fill_builder(&mut args, defs, mk_kind);
+ GenericArgs::new_from_slice(&args)
+ }
+
/// Creates an `GenericArgs` for generic parameter definitions,
/// by calling closures to obtain each kind.
/// The closures get to observe the `GenericArgs` as they're
@@ -466,7 +544,7 @@ impl<'db> GenericArgs<'db> {
pub fn for_item<F>(
interner: DbInterner<'db>,
def_id: SolverDefId,
- mut mk_kind: F,
+ mk_kind: F,
) -> GenericArgs<'db>
where
F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>,
@@ -475,12 +553,14 @@ impl<'db> GenericArgs<'db> {
let count = defs.count();
if count == 0 {
- return Default::default();
+ GenericArgs::default()
+ } else if count <= 10 {
+ let mut args = ArrayVec::<_, 10>::new();
+ Self::fill_builder(&mut args, &defs, mk_kind);
+ GenericArgs::new_from_slice(&args)
+ } else {
+ Self::fill_vec_builder(&defs, count, mk_kind)
}
-
- let mut args = SmallVec::with_capacity(count);
- Self::fill_item(&mut args, interner, defs, &mut mk_kind);
- interner.mk_args(&args)
}
/// Creates an all-error `GenericArgs`.
@@ -499,7 +579,7 @@ impl<'db> GenericArgs<'db> {
{
let defaults = interner.db.generic_defaults(def_id);
Self::for_item(interner, def_id.into(), |idx, id, prev| match defaults.get(idx as usize) {
- Some(default) => default.instantiate(interner, prev),
+ Some(default) => default.instantiate(interner, prev).skip_norm_wip(),
None => fallback(idx, id, prev),
})
}
@@ -534,37 +614,11 @@ impl<'db> GenericArgs<'db> {
Self::fill_rest(interner, def_id.into(), first, |idx, id, prev| {
defaults
.get(idx as usize)
- .map(|default| default.instantiate(interner, prev))
+ .map(|default| default.instantiate(interner, prev).skip_norm_wip())
.unwrap_or_else(|| fallback(idx, id, prev))
})
}
- fn fill_item<F>(
- args: &mut SmallVec<[GenericArg<'db>; 8]>,
- interner: DbInterner<'_>,
- defs: Generics,
- mk_kind: &mut F,
- ) where
- F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>,
- {
- if let Some(def_id) = defs.parent {
- let parent_defs = interner.generics_of(def_id.into());
- Self::fill_item(args, interner, parent_defs, mk_kind);
- }
- Self::fill_single(args, &defs, mk_kind);
- }
-
- fn fill_single<F>(args: &mut SmallVec<[GenericArg<'db>; 8]>, defs: &Generics, mk_kind: &mut F)
- where
- F: FnMut(u32, GenericParamId, &[GenericArg<'db>]) -> GenericArg<'db>,
- {
- args.reserve(defs.own_params.len());
- for param in &defs.own_params {
- let kind = mk_kind(args.len() as u32, param.id, args);
- args.push(kind);
- }
- }
-
pub fn types(self) -> impl Iterator<Item = Ty<'db>> {
self.iter().filter_map(|it| it.as_type())
}
diff --git a/crates/hir-ty/src/next_solver/generics.rs b/crates/hir-ty/src/next_solver/generics.rs
index f31de21796..a798582cb9 100644
--- a/crates/hir-ty/src/next_solver/generics.rs
+++ b/crates/hir-ty/src/next_solver/generics.rs
@@ -1,117 +1,77 @@
//! Things related to generics in the next-trait-solver.
-use hir_def::{
- ConstParamId, GenericDefId, GenericParamId, LifetimeParamId, TypeOrConstParamId, TypeParamId,
- hir::generics::{GenericParams, TypeOrConstParamData},
-};
-use rustc_type_ir::inherent::GenericsOf;
+use hir_def::{GenericDefId, GenericParamId};
-use crate::generics::parent_generic_def;
+use crate::db::HirDatabase;
use super::SolverDefId;
use super::DbInterner;
-pub(crate) fn generics(interner: DbInterner<'_>, def: SolverDefId) -> Generics {
- let mk_lt = |parent, index, local_id| {
- let id = GenericParamId::LifetimeParamId(LifetimeParamId { parent, local_id });
- GenericParamDef { index, id }
- };
- let mk_ty = |parent, index, local_id, p: &TypeOrConstParamData| {
- let id = TypeOrConstParamId { parent, local_id };
- let id = match p {
- TypeOrConstParamData::TypeParamData(_) => {
- GenericParamId::TypeParamId(TypeParamId::from_unchecked(id))
- }
- TypeOrConstParamData::ConstParamData(_) => {
- GenericParamId::ConstParamId(ConstParamId::from_unchecked(id))
- }
- };
- GenericParamDef { index, id }
- };
- let own_params_for_generic_params = |parent, params: &GenericParams| {
- let mut result = Vec::with_capacity(params.len());
- let mut type_and_consts = params.iter_type_or_consts();
- let mut index = 0;
- if let Some(self_param) = params.trait_self_param() {
- result.push(mk_ty(parent, 0, self_param, &params[self_param]));
- type_and_consts.next();
- index += 1;
- }
- result.extend(params.iter_lt().map(|(local_id, _data)| {
- let lt = mk_lt(parent, index, local_id);
- index += 1;
- lt
- }));
- result.extend(type_and_consts.map(|(local_id, data)| {
- let ty = mk_ty(parent, index, local_id, data);
- index += 1;
- ty
- }));
- result
- };
-
+pub(crate) fn generics(interner: DbInterner<'_>, def: SolverDefId) -> Generics<'_> {
let db = interner.db;
- let (parent, own_params) = match (def.try_into(), def) {
- (Ok(def), _) => (
- parent_generic_def(db, def),
- own_params_for_generic_params(def, GenericParams::of(db, def)),
- ),
- (_, SolverDefId::InternedOpaqueTyId(id)) => {
- match db.lookup_intern_impl_trait_id(id) {
- crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => {
- // The opaque type itself does not have generics - only the parent function
- (Some(GenericDefId::FunctionId(function_id)), vec![])
- }
- crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => {
- (Some(type_alias_id.into()), Vec::new())
- }
- }
- }
+ let def = match (def.try_into(), def) {
+ (Ok(def), _) => def,
+ (_, SolverDefId::InternedOpaqueTyId(id)) => match id.loc(db) {
+ crate::ImplTraitId::ReturnTypeImplTrait(function_id, _) => function_id.into(),
+ crate::ImplTraitId::TypeAliasImplTrait(type_alias_id, _) => type_alias_id.into(),
+ },
(_, SolverDefId::BuiltinDeriveImplId(id)) => {
return crate::builtin_derive::generics_of(interner, id);
}
+ (_, SolverDefId::AnonConstId(id)) => {
+ let loc = id.loc(db);
+ let generic_def = loc.owner.generic_def(db);
+ return if loc.allow_using_generic_params {
+ Generics::from_generic_def(db, generic_def)
+ } else {
+ #[expect(
+ deprecated,
+ reason = "`Generics` only exposes an iterator over `GenericParamId`, \
+ so you cannot exploit the erroneous `crate::generics::Generics`"
+ )]
+ Generics {
+ generics: crate::generics::Generics::empty(generic_def),
+ additional_param: None,
+ }
+ };
+ }
_ => panic!("No generics for {def:?}"),
};
- let parent_generics = parent.map(|def| Box::new(generics(interner, def.into())));
- Generics {
- parent,
- parent_count: parent_generics.map_or(0, |g| g.parent_count + g.own_params.len()),
- own_params,
- }
+ Generics::from_generic_def(db, def)
}
#[derive(Debug)]
-pub struct Generics {
- pub parent: Option<GenericDefId>,
- pub parent_count: usize,
- pub own_params: Vec<GenericParamDef>,
+pub struct Generics<'db> {
+ generics: crate::generics::Generics<'db>,
+ /// This is used for builtin derives, specifically `CoercePointee`.
+ additional_param: Option<GenericParamId>,
}
-impl Generics {
- pub(crate) fn push_param(&mut self, id: GenericParamId) {
- let index = self.count() as u32;
- self.own_params.push(GenericParamDef { index, id });
+impl<'db> Generics<'db> {
+ pub(crate) fn from_generic_def(db: &'db dyn HirDatabase, def: GenericDefId) -> Generics<'db> {
+ Generics { generics: crate::generics::generics(db, def), additional_param: None }
}
-}
-#[derive(Debug)]
-pub struct GenericParamDef {
- index: u32,
- pub(crate) id: GenericParamId,
-}
+ pub(crate) fn from_generic_def_plus_one(
+ db: &'db dyn HirDatabase,
+ def: GenericDefId,
+ additional_param: GenericParamId,
+ ) -> Generics<'db> {
+ Generics {
+ generics: crate::generics::generics(db, def),
+ additional_param: Some(additional_param),
+ }
+ }
-impl GenericParamDef {
- /// Returns the index of the param on the self generics only
- /// (i.e. not including parent generics)
- pub fn index(&self) -> u32 {
- self.index
+ pub(super) fn iter_id(&self) -> impl Iterator<Item = GenericParamId> {
+ self.generics.iter_id().chain(self.additional_param)
}
}
-impl<'db> rustc_type_ir::inherent::GenericsOf<DbInterner<'db>> for Generics {
+impl<'db> rustc_type_ir::inherent::GenericsOf<DbInterner<'db>> for Generics<'db> {
fn count(&self) -> usize {
- self.parent_count + self.own_params.len()
+ self.generics.len() + usize::from(self.additional_param.is_some())
}
}
diff --git a/crates/hir-ty/src/next_solver/infer/at.rs b/crates/hir-ty/src/next_solver/infer/at.rs
index dc0b584084..f63200a2e0 100644
--- a/crates/hir-ty/src/next_solver/infer/at.rs
+++ b/crates/hir-ty/src/next_solver/infer/at.rs
@@ -28,13 +28,12 @@
use rustc_type_ir::{
FnSig, GenericArgKind, TypeFoldable, TypingMode, Variance,
error::ExpectedFound,
- inherent::Span as _,
relate::{Relate, TypeRelation, solver_relating::RelateExt},
};
use crate::next_solver::{
AliasTerm, AliasTy, Binder, Const, DbInterner, GenericArg, Goal, ParamEnv,
- PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Span, Term,
+ PolyExistentialProjection, PolyExistentialTraitRef, PolyFnSig, Predicate, Region, Term,
TraitRef, Ty,
fulfill::NextSolverError,
infer::relate::lattice::{LatticeOp, LatticeOpKind},
@@ -109,7 +108,7 @@ impl<'a, 'db> At<'a, 'db> {
expected,
Variance::Contravariant,
actual,
- Span::dummy(),
+ self.cause.span(),
)
.map(|goals| self.goals_to_obligations(goals))
}
@@ -125,7 +124,7 @@ impl<'a, 'db> At<'a, 'db> {
expected,
Variance::Covariant,
actual,
- Span::dummy(),
+ self.cause.span(),
)
.map(|goals| self.goals_to_obligations(goals))
}
@@ -141,7 +140,7 @@ impl<'a, 'db> At<'a, 'db> {
expected,
Variance::Invariant,
actual,
- Span::dummy(),
+ self.cause.span(),
)
.map(|goals| self.goals_to_obligations(goals))
}
@@ -200,7 +199,7 @@ impl<'a, 'db> At<'a, 'db> {
.map(|goal| {
Obligation::new(
self.infcx.interner,
- self.cause.clone(),
+ *self.cause,
goal.param_env,
goal.predicate,
)
@@ -213,7 +212,7 @@ impl<'a, 'db> At<'a, 'db> {
impl<'db> ToTrace<'db> for Ty<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
}
}
@@ -221,14 +220,14 @@ impl<'db> ToTrace<'db> for Ty<'db> {
impl<'db> ToTrace<'db> for Region<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
- TypeTrace { cause: cause.clone(), values: ValuePairs::Regions(ExpectedFound::new(a, b)) }
+ TypeTrace { cause: *cause, values: ValuePairs::Regions(ExpectedFound::new(a, b)) }
}
}
impl<'db> ToTrace<'db> for Const<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
}
}
@@ -237,7 +236,7 @@ impl<'db> ToTrace<'db> for Const<'db> {
impl<'db> ToTrace<'db> for GenericArg<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: match (a.kind(), b.kind()) {
(GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => {
ValuePairs::Regions(ExpectedFound::new(a, b))
@@ -256,20 +255,20 @@ impl<'db> ToTrace<'db> for GenericArg<'db> {
impl<'db> ToTrace<'db> for Term<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
- TypeTrace { cause: cause.clone(), values: ValuePairs::Terms(ExpectedFound::new(a, b)) }
+ TypeTrace { cause: *cause, values: ValuePairs::Terms(ExpectedFound::new(a, b)) }
}
}
impl<'db> ToTrace<'db> for TraitRef<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
- TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
+ TypeTrace { cause: *cause, values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
}
}
impl<'db> ToTrace<'db> for AliasTy<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::Aliases(ExpectedFound::new(a.into(), b.into())),
}
}
@@ -277,14 +276,14 @@ impl<'db> ToTrace<'db> for AliasTy<'db> {
impl<'db> ToTrace<'db> for AliasTerm<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
- TypeTrace { cause: cause.clone(), values: ValuePairs::Aliases(ExpectedFound::new(a, b)) }
+ TypeTrace { cause: *cause, values: ValuePairs::Aliases(ExpectedFound::new(a, b)) }
}
}
impl<'db> ToTrace<'db> for FnSig<DbInterner<'db>> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::PolySigs(ExpectedFound::new(Binder::dummy(a), Binder::dummy(b))),
}
}
@@ -292,14 +291,14 @@ impl<'db> ToTrace<'db> for FnSig<DbInterner<'db>> {
impl<'db> ToTrace<'db> for PolyFnSig<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
- TypeTrace { cause: cause.clone(), values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) }
+ TypeTrace { cause: *cause, values: ValuePairs::PolySigs(ExpectedFound::new(a, b)) }
}
}
impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a, b)),
}
}
@@ -308,7 +307,7 @@ impl<'db> ToTrace<'db> for PolyExistentialTraitRef<'db> {
impl<'db> ToTrace<'db> for PolyExistentialProjection<'db> {
fn to_trace(cause: &ObligationCause, a: Self, b: Self) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::ExistentialProjection(ExpectedFound::new(a, b)),
}
}
diff --git a/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
index 1738552a8e..6e20375500 100644
--- a/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
+++ b/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
@@ -404,13 +404,17 @@ impl<'db> InferCtxt<'db> {
if kind.universe() != UniverseIndex::ROOT {
// A variable from inside a binder of the query. While ideally these shouldn't
// exist at all, we have to deal with them for now.
- self.instantiate_canonical_var(kind, var_values, |u| universe_map[u.as_usize()])
+ self.instantiate_canonical_var(cause.span(), kind, var_values, |u| {
+ universe_map[u.as_usize()]
+ })
} else if kind.is_existential() {
match opt_values[BoundVar::new(var_values.len())] {
Some(k) => k,
- None => self.instantiate_canonical_var(kind, var_values, |u| {
- universe_map[u.as_usize()]
- }),
+ None => {
+ self.instantiate_canonical_var(cause.span(), kind, var_values, |u| {
+ universe_map[u.as_usize()]
+ })
+ }
}
} else {
// For placeholders which were already part of the input, we simply map this
@@ -433,7 +437,7 @@ impl<'db> InferCtxt<'db> {
// the generic args of the opaque with the generic params of its hidden type version.
obligations.extend(
self.at(cause, param_env)
- .eq(Ty::new_opaque(self.interner, a.def_id, a.args), b)?
+ .eq(Ty::new_opaque(self.interner, a.def_id.0, a.args), b)?
.obligations,
);
}
diff --git a/crates/hir-ty/src/next_solver/infer/canonical/mod.rs b/crates/hir-ty/src/next_solver/infer/canonical/mod.rs
index 1fefc0f265..a5e29e7836 100644
--- a/crates/hir-ty/src/next_solver/infer/canonical/mod.rs
+++ b/crates/hir-ty/src/next_solver/infer/canonical/mod.rs
@@ -21,10 +21,13 @@
//!
//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html
-use crate::next_solver::{
- ArgOutlivesPredicate, Canonical, CanonicalVarValues, Const, DbInterner, GenericArg,
- OpaqueTypeKey, PlaceholderConst, PlaceholderRegion, PlaceholderType, Region, Ty, TyKind,
- infer::InferCtxt,
+use crate::{
+ Span,
+ next_solver::{
+ ArgOutlivesPredicate, Canonical, CanonicalVarValues, Const, DbInterner, GenericArg,
+ OpaqueTypeKey, PlaceholderConst, PlaceholderRegion, PlaceholderType, Region, Ty, TyKind,
+ infer::InferCtxt,
+ },
};
use instantiate::CanonicalExt;
use macros::{TypeFoldable, TypeVisitable};
@@ -50,6 +53,7 @@ impl<'db> InferCtxt<'db> {
/// for each of the canonical inputs to your query.
pub fn instantiate_canonical<T>(
&self,
+ span: Span,
canonical: &Canonical<'db, T>,
) -> (T, CanonicalVarValues<'db>)
where
@@ -71,7 +75,9 @@ impl<'db> InferCtxt<'db> {
let var_values = CanonicalVarValues::instantiate(
self.interner,
canonical.var_kinds,
- |var_values, info| self.instantiate_canonical_var(info, var_values, |ui| universes[ui]),
+ |var_values, info| {
+ self.instantiate_canonical_var(span, info, var_values, |ui| universes[ui])
+ },
);
let result = canonical.instantiate(self.interner, &var_values);
(result, var_values)
@@ -87,13 +93,14 @@ impl<'db> InferCtxt<'db> {
/// We should somehow deduplicate all of this.
pub fn instantiate_canonical_var(
&self,
+ span: Span,
cv_info: CanonicalVarKind<DbInterner<'db>>,
previous_var_values: &[GenericArg<'db>],
universe_map: impl Fn(UniverseIndex) -> UniverseIndex,
) -> GenericArg<'db> {
match cv_info {
CanonicalVarKind::Ty { ui, sub_root } => {
- let vid = self.next_ty_var_id_in_universe(universe_map(ui));
+ let vid = self.next_ty_var_id_in_universe(universe_map(ui), span);
// If this inference variable is related to an earlier variable
// via subtyping, we need to add that info to the inference context.
if let Some(prev) = previous_var_values.get(sub_root.as_usize()) {
@@ -117,7 +124,7 @@ impl<'db> InferCtxt<'db> {
}
CanonicalVarKind::Region(ui) => {
- self.next_region_var_in_universe(universe_map(ui)).into()
+ self.next_region_var_in_universe(universe_map(ui), span).into()
}
CanonicalVarKind::PlaceholderRegion(PlaceholderRegion { universe, bound, .. }) => {
@@ -126,7 +133,9 @@ impl<'db> InferCtxt<'db> {
Region::new_placeholder(self.interner, placeholder_mapped).into()
}
- CanonicalVarKind::Const(ui) => self.next_const_var_in_universe(universe_map(ui)).into(),
+ CanonicalVarKind::Const(ui) => {
+ self.next_const_var_in_universe(universe_map(ui), span).into()
+ }
CanonicalVarKind::PlaceholderConst(PlaceholderConst { universe, bound, .. }) => {
let universe_mapped = universe_map(universe);
let placeholder_mapped = PlaceholderConst::new(universe_mapped, bound);
diff --git a/crates/hir-ty/src/next_solver/infer/context.rs b/crates/hir-ty/src/next_solver/infer/context.rs
index 397986e2ed..1e2a3ff0c2 100644
--- a/crates/hir-ty/src/next_solver/infer/context.rs
+++ b/crates/hir-ty/src/next_solver/infer/context.rs
@@ -5,12 +5,16 @@ use rustc_type_ir::{
RegionVid, TyVid, TypeFoldable, TypingMode, UniverseIndex,
inherent::{Const as _, IntoKind, Ty as _},
relate::combine::PredicateEmittingRelation,
+ solve::VisibleForLeakCheck,
};
-use crate::next_solver::{
- Binder, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, OpaqueTypeKey, Region,
- SolverDefId, Span, Ty, TyKind,
- infer::opaque_types::{OpaqueHiddenType, table::OpaqueTypeStorageEntries},
+use crate::{
+ Span,
+ next_solver::{
+ Binder, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, OpaqueTypeKey, Region,
+ SolverDefId, Ty, TyKind,
+ infer::opaque_types::{OpaqueHiddenType, table::OpaqueTypeStorageEntries},
+ },
};
use super::{BoundRegionConversionTime, InferCtxt, relate::RelateResult};
@@ -26,8 +30,12 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> {
true
}
- fn typing_mode(&self) -> TypingMode<DbInterner<'db>> {
- self.typing_mode()
+ fn disable_trait_solver_fast_paths(&self) -> bool {
+ false
+ }
+
+ fn typing_mode_raw(&self) -> TypingMode<DbInterner<'db>> {
+ self.typing_mode_raw()
}
fn universe(&self) -> UniverseIndex {
@@ -139,26 +147,30 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> {
}
fn next_ty_infer(&self) -> Ty<'db> {
- self.next_ty_var()
+ self.next_ty_var(Span::Dummy)
}
fn next_region_infer(&self) -> <Self::Interner as rustc_type_ir::Interner>::Region {
- self.next_region_var()
+ self.next_region_var(Span::Dummy)
}
fn next_const_infer(&self) -> Const<'db> {
- self.next_const_var()
+ self.next_const_var(Span::Dummy)
}
fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> {
- self.fresh_args_for_item(def_id)
+ self.fresh_args_for_item(Span::Dummy, def_id)
}
fn instantiate_binder_with_infer<T: TypeFoldable<DbInterner<'db>> + Clone>(
&self,
value: Binder<'db, T>,
) -> T {
- self.instantiate_binder_with_fresh_vars(BoundRegionConversionTime::HigherRankedType, value)
+ self.instantiate_binder_with_fresh_vars(
+ Span::Dummy,
+ BoundRegionConversionTime::HigherRankedType,
+ value,
+ )
}
fn enter_forall<T: TypeFoldable<DbInterner<'db>> + Clone, U>(
@@ -250,11 +262,23 @@ impl<'db> rustc_type_ir::InferCtxtLike for InferCtxt<'db> {
self.probe(|_| probe())
}
- fn sub_regions(&self, sub: Region<'db>, sup: Region<'db>, _span: Span) {
+ fn sub_regions(
+ &self,
+ sub: Region<'db>,
+ sup: Region<'db>,
+ _vis: VisibleForLeakCheck,
+ _span: Span,
+ ) {
self.inner.borrow_mut().unwrap_region_constraints().make_subregion(sub, sup);
}
- fn equate_regions(&self, a: Region<'db>, b: Region<'db>, _span: Span) {
+ fn equate_regions(
+ &self,
+ a: Region<'db>,
+ b: Region<'db>,
+ _vis: VisibleForLeakCheck,
+ _span: Span,
+ ) {
self.inner.borrow_mut().unwrap_region_constraints().make_eqregion(a, b);
}
diff --git a/crates/hir-ty/src/next_solver/infer/errors.rs b/crates/hir-ty/src/next_solver/infer/errors.rs
new file mode 100644
index 0000000000..7d3f111f66
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/infer/errors.rs
@@ -0,0 +1,702 @@
+use std::{fmt, ops::ControlFlow};
+
+use hir_def::attrs::AttrFlags;
+use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt};
+use rustc_type_ir::{
+ AliasRelationDirection, AliasTermKind, PredicatePolarity,
+ error::ExpectedFound,
+ inherent::IntoKind as _,
+ solve::{CandidateSource, Certainty, GoalSource, MaybeCause, MaybeInfo, NoSolution},
+};
+use tracing::{instrument, trace};
+
+use crate::{
+ Span,
+ db::GeneralConstId,
+ next_solver::{
+ AliasTerm, AnyImplId, Binder, ClauseKind, Const, ConstKind, DbInterner,
+ HostEffectPredicate, PolyTraitPredicate, PredicateKind, SolverContext, Term,
+ TraitPredicate, Ty, TyKind, TypeError,
+ fulfill::NextSolverError,
+ infer::{
+ InferCtxt,
+ select::SelectionError,
+ traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations},
+ },
+ inspect::{self, ProofTreeVisitor},
+ normalize::deeply_normalize_for_diagnostics,
+ },
+};
+
+#[derive(Debug)]
+pub struct FulfillmentError<'db> {
+ pub obligation: PredicateObligation<'db>,
+ pub code: FulfillmentErrorCode<'db>,
+ /// Diagnostics only: the 'root' obligation which resulted in
+ /// the failure to process `obligation`. This is the obligation
+ /// that was initially passed to `register_predicate_obligation`
+ pub root_obligation: PredicateObligation<'db>,
+}
+
+impl<'db> FulfillmentError<'db> {
+ pub fn new(
+ obligation: PredicateObligation<'db>,
+ code: FulfillmentErrorCode<'db>,
+ root_obligation: PredicateObligation<'db>,
+ ) -> FulfillmentError<'db> {
+ FulfillmentError { obligation, code, root_obligation }
+ }
+
+ pub fn is_true_error(&self) -> bool {
+ match self.code {
+ FulfillmentErrorCode::Select(_)
+ | FulfillmentErrorCode::Project(_)
+ | FulfillmentErrorCode::Subtype(_, _)
+ | FulfillmentErrorCode::ConstEquate(_, _) => true,
+ FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => {
+ false
+ }
+ }
+ }
+}
+
+#[derive(Clone)]
+pub enum FulfillmentErrorCode<'db> {
+ /// Inherently impossible to fulfill; this trait is implemented if and only
+ /// if it is already implemented.
+ Cycle(PredicateObligations<'db>),
+ Select(SelectionError<'db>),
+ Project(MismatchedProjectionTypes<'db>),
+ Subtype(ExpectedFound<Ty<'db>>, TypeError<'db>), // always comes from a SubtypePredicate
+ ConstEquate(ExpectedFound<Const<'db>>, TypeError<'db>),
+ Ambiguity {
+ /// Overflow is only `Some(suggest_recursion_limit)` when using the next generation
+ /// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by
+ /// emitting a fatal error instead.
+ overflow: Option<bool>,
+ },
+}
+
+impl<'db> fmt::Debug for FulfillmentErrorCode<'db> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ FulfillmentErrorCode::Select(ref e) => write!(f, "{e:?}"),
+ FulfillmentErrorCode::Project(ref e) => write!(f, "{e:?}"),
+ FulfillmentErrorCode::Subtype(ref a, ref b) => {
+ write!(f, "CodeSubtypeError({a:?}, {b:?})")
+ }
+ FulfillmentErrorCode::ConstEquate(ref a, ref b) => {
+ write!(f, "CodeConstEquateError({a:?}, {b:?})")
+ }
+ FulfillmentErrorCode::Ambiguity { overflow: None } => write!(f, "Ambiguity"),
+ FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
+ write!(f, "Overflow({suggest_increasing_limit})")
+ }
+ FulfillmentErrorCode::Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"),
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct MismatchedProjectionTypes<'db> {
+ pub err: TypeError<'db>,
+}
+
+impl<'db> fmt::Debug for MismatchedProjectionTypes<'db> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "MismatchedProjectionTypes({:?})", self.err)
+ }
+}
+
+impl<'db> NextSolverError<'db> {
+ pub fn into_fulfillment_error(self, infcx: &InferCtxt<'db>) -> FulfillmentError<'db> {
+ match self {
+ NextSolverError::TrueError(obligation) => {
+ fulfillment_error_for_no_solution(infcx, obligation)
+ }
+ NextSolverError::Ambiguity(obligation) => {
+ fulfillment_error_for_stalled(infcx, obligation)
+ }
+ NextSolverError::Overflow(obligation) => {
+ fulfillment_error_for_overflow(infcx, obligation)
+ }
+ }
+ }
+}
+
+fn fulfillment_error_for_no_solution<'db>(
+ infcx: &InferCtxt<'db>,
+ root_obligation: PredicateObligation<'db>,
+) -> FulfillmentError<'db> {
+ let interner = infcx.interner;
+ let db = interner.db;
+ let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
+
+ let code = match obligation.predicate.kind().skip_binder() {
+ PredicateKind::Clause(ClauseKind::Projection(_)) => {
+ FulfillmentErrorCode::Project(
+ // FIXME: This could be a `Sorts` if the term is a type
+ MismatchedProjectionTypes { err: TypeError::Mismatch },
+ )
+ }
+ PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, expected_ty)) => {
+ let ct_ty = match ct.kind() {
+ ConstKind::Unevaluated(uv) => {
+ let ct_ty = match uv.def.0 {
+ GeneralConstId::ConstId(konst) => db.value_ty(konst.into()).unwrap(),
+ GeneralConstId::StaticId(statik) => db.value_ty(statik.into()).unwrap(),
+ GeneralConstId::AnonConstId(konst) => konst.loc(db).ty.get(),
+ };
+ ct_ty.instantiate(interner, uv.args).skip_norm_wip()
+ }
+ ConstKind::Param(param_ct) => param_ct.find_const_ty_from_env(obligation.param_env),
+ ConstKind::Value(cv) => cv.ty,
+ kind => panic!(
+ "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
+ ),
+ };
+ FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
+ ct,
+ ct_ty,
+ expected_ty,
+ })
+ }
+ PredicateKind::NormalizesTo(..) => {
+ FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
+ }
+ PredicateKind::AliasRelate(_, _, _) => {
+ FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
+ }
+ PredicateKind::Subtype(pred) => {
+ let (a, b) = infcx.enter_forall_and_leak_universe(
+ obligation.predicate.kind().rebind((pred.a, pred.b)),
+ );
+ let expected_found = ExpectedFound::new(a, b);
+ FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
+ }
+ PredicateKind::Coerce(pred) => {
+ let (a, b) = infcx.enter_forall_and_leak_universe(
+ obligation.predicate.kind().rebind((pred.a, pred.b)),
+ );
+ let expected_found = ExpectedFound::new(b, a);
+ FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
+ }
+ PredicateKind::Clause(_) | PredicateKind::DynCompatible(_) | PredicateKind::Ambiguous => {
+ FulfillmentErrorCode::Select(SelectionError::Unimplemented)
+ }
+ PredicateKind::ConstEquate(..) => {
+ panic!("unexpected goal: {obligation:?}")
+ }
+ };
+
+ FulfillmentError { obligation, code, root_obligation }
+}
+
+fn fulfillment_error_for_stalled<'db>(
+ infcx: &InferCtxt<'db>,
+ root_obligation: PredicateObligation<'db>,
+) -> FulfillmentError<'db> {
+ let (code, refine_obligation) = infcx.probe(|_| {
+ match <&SolverContext<'db>>::from(infcx).evaluate_root_goal(
+ root_obligation.as_goal(),
+ root_obligation.cause.span(),
+ None,
+ ) {
+ Ok(GoalEvaluation {
+ certainty: Certainty::Maybe(MaybeInfo { cause: MaybeCause::Ambiguity, .. }),
+ ..
+ }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true),
+ Ok(GoalEvaluation {
+ certainty:
+ Certainty::Maybe(MaybeInfo {
+ cause:
+ MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ },
+ ..
+ }),
+ ..
+ }) => (
+ FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
+ // Don't look into overflows because we treat overflows weirdly anyways.
+ // We discard the inference constraints from overflowing goals, so
+ // recomputing the goal again during `find_best_leaf_obligation` may apply
+ // inference guidance that makes other goals go from ambig -> pass, for example.
+ //
+ // FIXME: We should probably just look into overflows here.
+ false,
+ ),
+ Ok(GoalEvaluation { certainty: Certainty::Yes, .. }) => {
+ panic!(
+ "did not expect successful goal when collecting ambiguity errors for `{:?}`",
+ infcx.resolve_vars_if_possible(root_obligation.predicate),
+ )
+ }
+ Err(_) => {
+ panic!(
+ "did not expect selection error when collecting ambiguity errors for `{:?}`",
+ infcx.resolve_vars_if_possible(root_obligation.predicate),
+ )
+ }
+ }
+ });
+
+ FulfillmentError {
+ obligation: if refine_obligation {
+ find_best_leaf_obligation(infcx, &root_obligation, true)
+ } else {
+ root_obligation.clone()
+ },
+ code,
+ root_obligation,
+ }
+}
+
+fn fulfillment_error_for_overflow<'db>(
+ infcx: &InferCtxt<'db>,
+ root_obligation: PredicateObligation<'db>,
+) -> FulfillmentError<'db> {
+ FulfillmentError {
+ obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
+ code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
+ root_obligation,
+ }
+}
+
+#[instrument(level = "debug", skip(infcx), ret)]
+fn find_best_leaf_obligation<'db>(
+ infcx: &InferCtxt<'db>,
+ obligation: &PredicateObligation<'db>,
+ consider_ambiguities: bool,
+) -> PredicateObligation<'db> {
+ let obligation = infcx.resolve_vars_if_possible(obligation.clone());
+ // FIXME: we use a probe here as the `BestObligation` visitor does not
+ // check whether it uses candidates which get shadowed by where-bounds.
+ //
+ // We should probably fix the visitor to not do so instead, as this also
+ // means the leaf obligation may be incorrect.
+ let obligation = infcx
+ .fudge_inference_if_ok(|| {
+ infcx
+ .visit_proof_tree(
+ obligation.as_goal(),
+ &mut BestObligation { obligation: obligation.clone(), consider_ambiguities },
+ )
+ .break_value()
+ .ok_or(())
+ // walk around the fact that the cause in `Obligation` is ignored by folders so that
+ // we can properly fudge the infer vars in cause code.
+ .map(|o| (o.cause, o))
+ })
+ .map(|(cause, o)| PredicateObligation { cause, ..o })
+ .unwrap_or(obligation);
+ deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation)
+}
+
+struct BestObligation<'db> {
+ obligation: PredicateObligation<'db>,
+ consider_ambiguities: bool,
+}
+
+impl<'db> BestObligation<'db> {
+ fn with_derived_obligation(
+ &mut self,
+ derived_obligation: PredicateObligation<'db>,
+ and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'db>>::Result,
+ ) -> <Self as ProofTreeVisitor<'db>>::Result {
+ let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
+ let res = and_then(self);
+ self.obligation = old_obligation;
+ res
+ }
+
+ /// Filter out the candidates that aren't interesting to visit for the
+ /// purposes of reporting errors. For ambiguities, we only consider
+ /// candidates that may hold. For errors, we only consider candidates that
+ /// *don't* hold and which have impl-where clauses that also don't hold.
+ fn non_trivial_candidates<'a>(
+ &self,
+ goal: &'a inspect::InspectGoal<'a, 'db>,
+ ) -> Vec<inspect::InspectCandidate<'a, 'db>> {
+ let mut candidates = goal.candidates();
+ match self.consider_ambiguities {
+ true => {
+ // If we have an ambiguous obligation, we must consider *all* candidates
+ // that hold, or else we may guide inference causing other goals to go
+ // from ambig -> pass/fail.
+ candidates.retain(|candidate| candidate.result().is_ok());
+ }
+ false => {
+ // We always handle rigid alias candidates separately as we may not add them for
+ // aliases whose trait bound doesn't hold.
+ candidates.retain(|c| !matches!(c.kind(), inspect::ProbeKind::RigidAlias { .. }));
+ // If we have >1 candidate, one may still be due to "boring" reasons, like
+ // an alias-relate that failed to hold when deeply evaluated. We really
+ // don't care about reasons like this.
+ if candidates.len() > 1 {
+ candidates.retain(|candidate| {
+ goal.infcx().probe(|_| {
+ candidate.instantiate_nested_goals(self.span()).iter().any(
+ |nested_goal| {
+ matches!(
+ nested_goal.source(),
+ GoalSource::ImplWhereBound
+ | GoalSource::AliasBoundConstCondition
+ | GoalSource::AliasWellFormed
+ ) && nested_goal.result().is_err()
+ },
+ )
+ })
+ });
+ }
+ }
+ }
+
+ candidates
+ }
+
+ /// HACK: We walk the nested obligations for a well-formed arg manually,
+ /// since there's nontrivial logic in `wf.rs` to set up an obligation cause.
+ /// Ideally we'd be able to track this better.
+ fn visit_well_formed_goal(
+ &mut self,
+ candidate: &inspect::InspectCandidate<'_, 'db>,
+ term: Term<'db>,
+ ) -> ControlFlow<PredicateObligation<'db>> {
+ let _ = (candidate, term);
+ // FIXME: rustc does this, but we don't process WF obligations yet:
+ // let infcx = candidate.goal().infcx();
+ // let param_env = candidate.goal().goal().param_env;
+ // let body_id = self.obligation.cause.body_id;
+
+ // for obligation in wf::unnormalized_obligations(infcx, param_env, term, self.span(), body_id)
+ // .into_iter()
+ // .flatten()
+ // {
+ // let nested_goal = candidate.instantiate_proof_tree_for_nested_goal(
+ // GoalSource::Misc,
+ // obligation.as_goal(),
+ // self.span(),
+ // );
+ // // Skip nested goals that aren't the *reason* for our goal's failure.
+ // match (self.consider_ambiguities, nested_goal.result()) {
+ // (true, Ok(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. }))
+ // | (false, Err(_)) => {}
+ // _ => continue,
+ // }
+
+ // self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
+ // }
+
+ ControlFlow::Break(self.obligation.clone())
+ }
+
+ /// If a normalization of an associated item or a trait goal fails without trying any
+ /// candidates it's likely that normalizing its self type failed. We manually detect
+ /// such cases here.
+ fn detect_error_in_self_ty_normalization(
+ &mut self,
+ goal: &inspect::InspectGoal<'_, 'db>,
+ self_ty: Ty<'db>,
+ ) -> ControlFlow<PredicateObligation<'db>> {
+ assert!(!self.consider_ambiguities);
+ let interner = goal.infcx().interner;
+ if let TyKind::Alias(..) = self_ty.kind() {
+ let infer_term = goal.infcx().next_ty_var(self.obligation.cause.span());
+ let pred = PredicateKind::AliasRelate(
+ self_ty.into(),
+ infer_term.into(),
+ AliasRelationDirection::Equate,
+ );
+ let obligation =
+ Obligation::new(interner, self.obligation.cause, goal.goal().param_env, pred);
+ self.with_derived_obligation(obligation, |this| {
+ goal.infcx().visit_proof_tree_at_depth(
+ goal.goal().with(interner, pred),
+ goal.depth() + 1,
+ this,
+ )
+ })
+ } else {
+ ControlFlow::Continue(())
+ }
+ }
+
+ /// When a higher-ranked projection goal fails, check that the corresponding
+ /// higher-ranked trait goal holds or not. This is because the process of
+ /// instantiating and then re-canonicalizing the binder of the projection goal
+ /// forces us to be unable to see that the leak check failed in the nested
+ /// `NormalizesTo` goal, so we don't fall back to the rigid projection check
+ /// that should catch when a projection goal fails due to an unsatisfied trait
+ /// goal.
+ fn detect_trait_error_in_higher_ranked_projection(
+ &mut self,
+ goal: &inspect::InspectGoal<'_, 'db>,
+ ) -> ControlFlow<PredicateObligation<'db>> {
+ let interner = goal.infcx().interner;
+ if let Some(projection_clause) = goal.goal().predicate.as_projection_clause()
+ && !projection_clause.bound_vars().is_empty()
+ {
+ let pred = projection_clause.map_bound(|proj| proj.projection_term.trait_ref(interner));
+ let obligation = Obligation::new(
+ interner,
+ self.obligation.cause,
+ goal.goal().param_env,
+ deeply_normalize_for_diagnostics(goal.infcx(), goal.goal().param_env, pred),
+ );
+ self.with_derived_obligation(obligation, |this| {
+ goal.infcx().visit_proof_tree_at_depth(
+ goal.goal().with(interner, pred),
+ goal.depth() + 1,
+ this,
+ )
+ })
+ } else {
+ ControlFlow::Continue(())
+ }
+ }
+
+ /// It is likely that `NormalizesTo` failed without any applicable candidates
+ /// because the alias is not well-formed.
+ ///
+ /// As we only enter `RigidAlias` candidates if the trait bound of the associated type
+ /// holds, we discard these candidates in `non_trivial_candidates` and always manually
+ /// check this here.
+ fn detect_non_well_formed_assoc_item(
+ &mut self,
+ goal: &inspect::InspectGoal<'_, 'db>,
+ alias: AliasTerm<'db>,
+ ) -> ControlFlow<PredicateObligation<'db>> {
+ let interner = goal.infcx().interner;
+ let obligation = Obligation::new(
+ interner,
+ self.obligation.cause,
+ goal.goal().param_env,
+ alias.trait_ref(interner),
+ );
+ self.with_derived_obligation(obligation, |this| {
+ goal.infcx().visit_proof_tree_at_depth(
+ goal.goal().with(interner, alias.trait_ref(interner)),
+ goal.depth() + 1,
+ this,
+ )
+ })
+ }
+
+ /// If we have no candidates, then it's likely that there is a
+ /// non-well-formed alias in the goal.
+ fn detect_error_from_empty_candidates(
+ &mut self,
+ goal: &inspect::InspectGoal<'_, 'db>,
+ ) -> ControlFlow<PredicateObligation<'db>> {
+ let interner = goal.infcx().interner;
+ let pred_kind = goal.goal().predicate.kind();
+
+ match pred_kind.no_bound_vars() {
+ Some(PredicateKind::Clause(ClauseKind::Trait(pred))) => {
+ self.detect_error_in_self_ty_normalization(goal, pred.self_ty())?;
+ }
+ Some(PredicateKind::NormalizesTo(pred))
+ if let AliasTermKind::ProjectionTy { .. }
+ | AliasTermKind::ProjectionConst { .. } = pred.alias.kind(interner) =>
+ {
+ self.detect_error_in_self_ty_normalization(goal, pred.alias.self_ty())?;
+ self.detect_non_well_formed_assoc_item(goal, pred.alias)?;
+ }
+ Some(_) | None => {}
+ }
+
+ ControlFlow::Break(self.obligation.clone())
+ }
+}
+
+impl<'db> ProofTreeVisitor<'db> for BestObligation<'db> {
+ type Result = ControlFlow<PredicateObligation<'db>>;
+
+ fn span(&self) -> Span {
+ self.obligation.cause.span()
+ }
+
+ #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
+ fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'db>) -> Self::Result {
+ let interner = goal.infcx().interner;
+ // Skip goals that aren't the *reason* for our goal's failure.
+ match (self.consider_ambiguities, goal.result()) {
+ (true, Ok(Certainty::Maybe(MaybeInfo { cause: MaybeCause::Ambiguity, .. })))
+ | (false, Err(_)) => {}
+ _ => return ControlFlow::Continue(()),
+ }
+
+ let pred = goal.goal().predicate;
+
+ let candidates = self.non_trivial_candidates(goal);
+ let candidate = match candidates.as_slice() {
+ [candidate] => candidate,
+ [] => return self.detect_error_from_empty_candidates(goal),
+ _ => return ControlFlow::Break(self.obligation.clone()),
+ };
+
+ // Don't walk into impls that have `do_not_recommend`.
+ if let inspect::ProbeKind::TraitCandidate {
+ source: CandidateSource::Impl(impl_def_id),
+ result: _,
+ } = candidate.kind()
+ && let AnyImplId::ImplId(impl_def_id) = impl_def_id
+ && AttrFlags::query(interner.db, impl_def_id.into())
+ .contains(AttrFlags::DIAGNOSTIC_DO_NOT_RECOMMEND)
+ {
+ trace!("#[diagnostic::do_not_recommend] -> exit");
+ return ControlFlow::Break(self.obligation.clone());
+ }
+
+ // FIXME: Also, what about considering >1 layer up the stack? May be necessary
+ // for normalizes-to.
+ let child_mode = match pred.kind().skip_binder() {
+ PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => {
+ ChildMode::Trait(pred.kind().rebind(trait_pred))
+ }
+ PredicateKind::Clause(ClauseKind::HostEffect(host_pred)) => {
+ ChildMode::Host(pred.kind().rebind(host_pred))
+ }
+ PredicateKind::NormalizesTo(normalizes_to)
+ if matches!(
+ normalizes_to.alias.kind(interner),
+ AliasTermKind::ProjectionTy { .. } | AliasTermKind::ProjectionConst { .. }
+ ) =>
+ {
+ ChildMode::Trait(pred.kind().rebind(TraitPredicate {
+ trait_ref: normalizes_to.alias.trait_ref(interner),
+ polarity: PredicatePolarity::Positive,
+ }))
+ }
+ PredicateKind::Clause(ClauseKind::WellFormed(term)) => {
+ return self.visit_well_formed_goal(candidate, term);
+ }
+ _ => ChildMode::PassThrough,
+ };
+
+ let nested_goals = candidate.instantiate_nested_goals(self.span());
+
+ // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
+ // an actual candidate, instead we should treat them as if the impl was never considered to
+ // have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written
+ // instead of `impl<T: FnPtr> Trait for T`.
+ //
+ // We do this as a separate loop so that we do not choose to tell the user about some nested
+ // goal before we encounter a `T: FnPtr` nested goal.
+ for nested_goal in &nested_goals {
+ if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
+ && Some(poly_trait_pred.def_id().0) == interner.lang_items().FnPtrTrait
+ && let Err(NoSolution) = nested_goal.result()
+ {
+ return ControlFlow::Break(self.obligation.clone());
+ }
+ }
+
+ let mut impl_where_bound_count = 0;
+ for nested_goal in nested_goals {
+ trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
+
+ let nested_pred = nested_goal.goal().predicate;
+
+ let make_obligation = |cause| Obligation {
+ cause,
+ param_env: nested_goal.goal().param_env,
+ predicate: nested_pred,
+ recursion_depth: self.obligation.recursion_depth + 1,
+ };
+
+ let obligation;
+ match (child_mode, nested_goal.source()) {
+ (
+ ChildMode::Trait(_) | ChildMode::Host(_),
+ GoalSource::Misc | GoalSource::TypeRelating | GoalSource::NormalizeGoal(_),
+ ) => {
+ continue;
+ }
+ (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
+ obligation = make_obligation(derive_cause(
+ interner,
+ candidate.kind(),
+ self.obligation.cause,
+ impl_where_bound_count,
+ parent_trait_pred,
+ ));
+ impl_where_bound_count += 1;
+ }
+ (
+ ChildMode::Host(parent_host_pred),
+ GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
+ ) => {
+ obligation = make_obligation(derive_host_cause(
+ interner,
+ candidate.kind(),
+ self.obligation.cause,
+ impl_where_bound_count,
+ parent_host_pred,
+ ));
+ impl_where_bound_count += 1;
+ }
+ (ChildMode::PassThrough, _)
+ | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
+ obligation = make_obligation(self.obligation.cause);
+ }
+ }
+
+ self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
+ }
+
+ // alias-relate may fail because the lhs or rhs can't be normalized,
+ // and therefore is treated as rigid.
+ if let Some(PredicateKind::AliasRelate(lhs, rhs, _)) = pred.kind().no_bound_vars() {
+ goal.infcx().visit_proof_tree_at_depth(
+ goal.goal().with(interner, ClauseKind::WellFormed(lhs)),
+ goal.depth() + 1,
+ self,
+ )?;
+ goal.infcx().visit_proof_tree_at_depth(
+ goal.goal().with(interner, ClauseKind::WellFormed(rhs)),
+ goal.depth() + 1,
+ self,
+ )?;
+ }
+
+ self.detect_trait_error_in_higher_ranked_projection(goal)?;
+
+ ControlFlow::Break(self.obligation.clone())
+ }
+}
+
+#[derive(Debug, Copy, Clone)]
+enum ChildMode<'db> {
+ // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
+ // and skip all `GoalSource::Misc`, which represent useless obligations
+ // such as alias-eq which may not hold.
+ Trait(PolyTraitPredicate<'db>),
+ // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
+ // and skip all `GoalSource::Misc`, which represent useless obligations
+ // such as alias-eq which may not hold.
+ Host(Binder<'db, HostEffectPredicate<'db>>),
+ // Skip trying to derive an `ObligationCause` from this obligation, and
+ // report *all* sub-obligations as if they came directly from the parent
+ // obligation.
+ PassThrough,
+}
+
+fn derive_cause<'db>(
+ _interner: DbInterner<'db>,
+ _candidate_kind: inspect::ProbeKind<DbInterner<'db>>,
+ cause: ObligationCause,
+ _idx: usize,
+ _parent_trait_pred: PolyTraitPredicate<'db>,
+) -> ObligationCause {
+ cause
+}
+
+fn derive_host_cause<'db>(
+ _interner: DbInterner<'db>,
+ _candidate_kind: inspect::ProbeKind<DbInterner<'db>>,
+ cause: ObligationCause,
+ _idx: usize,
+ _parent_host_pred: Binder<'db, HostEffectPredicate<'db>>,
+) -> ObligationCause {
+ cause
+}
diff --git a/crates/hir-ty/src/next_solver/infer/mod.rs b/crates/hir-ty/src/next_solver/infer/mod.rs
index a6352c7899..839bdf17e7 100644
--- a/crates/hir-ty/src/next_solver/infer/mod.rs
+++ b/crates/hir-ty/src/next_solver/infer/mod.rs
@@ -7,12 +7,10 @@ use std::sync::Arc;
pub use BoundRegionConversionTime::*;
use ena::unify as ut;
-use hir_def::GenericParamId;
+use hir_def::{GenericParamId, TraitId};
use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage};
use region_constraints::{RegionConstraintCollector, RegionConstraintStorage};
use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt};
-use rustc_pattern_analysis::Captures;
-use rustc_type_ir::solve::{NoSolution, inspect};
use rustc_type_ir::{
ClosureKind, ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy,
IntVarValue, IntVid, OutlivesPredicate, RegionVid, TermKind, TyVid, TypeFoldable, TypeFolder,
@@ -22,19 +20,25 @@ use rustc_type_ir::{
Const as _, GenericArg as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _,
},
};
+use rustc_type_ir::{
+ Upcast,
+ solve::{NoSolution, inspect},
+};
use snapshot::undo_log::InferCtxtUndoLogs;
use tracing::{debug, instrument};
use traits::{ObligationCause, PredicateObligations};
-use type_variable::TypeVariableOrigin;
-use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey};
+use unify_key::{ConstVariableValue, ConstVidKey};
pub use crate::next_solver::infer::traits::ObligationInspector;
-use crate::next_solver::{
- ArgOutlivesPredicate, BoundConst, BoundRegion, BoundTy, BoundVariableKind, Goal, Predicate,
- SolverContext,
- fold::BoundVarReplacerDelegate,
- infer::{at::ToTrace, select::EvaluationResult, traits::PredicateObligation},
- obligation_ctxt::ObligationCtxt,
+use crate::{
+ Span,
+ next_solver::{
+ ArgOutlivesPredicate, BoundConst, BoundRegion, BoundTy, BoundVariableKind, Goal, Predicate,
+ SolverContext,
+ fold::BoundVarReplacerDelegate,
+ infer::{at::ToTrace, select::EvaluationResult, traits::PredicateObligation},
+ obligation_ctxt::ObligationCtxt,
+ },
};
use super::{
@@ -48,6 +52,7 @@ use super::{
pub mod at;
pub mod canonical;
mod context;
+pub mod errors;
pub mod opaque_types;
mod outlives;
pub mod region_constraints;
@@ -55,7 +60,7 @@ pub mod relate;
pub mod resolve;
pub mod select;
pub(crate) mod snapshot;
-pub(crate) mod traits;
+pub mod traits;
mod type_variable;
mod unify_key;
@@ -361,13 +366,14 @@ impl<'db> InferCtxtBuilder<'db> {
/// (in other words, `S(C) = V`).
pub fn build_with_canonical<T>(
mut self,
+ span: Span,
input: &CanonicalQueryInput<'db, T>,
) -> (InferCtxt<'db>, T, CanonicalVarValues<'db>)
where
T: TypeFoldable<DbInterner<'db>>,
{
let infcx = self.build(input.typing_mode.0);
- let (value, args) = infcx.instantiate_canonical(&input.canonical);
+ let (value, args) = infcx.instantiate_canonical(span, &input.canonical);
(infcx, value, args)
}
@@ -396,7 +402,7 @@ impl<'db> InferOk<'db, ()> {
impl<'db> InferCtxt<'db> {
#[inline(always)]
- pub fn typing_mode(&self) -> TypingMode<'db> {
+ pub fn typing_mode_raw(&self) -> TypingMode<'db> {
self.typing_mode
}
@@ -441,7 +447,11 @@ impl<'db> InferCtxt<'db> {
return ty;
}
- if ty.is_ty_error() { self.infcx.next_ty_var() } else { ty.super_fold_with(self) }
+ if ty.is_ty_error() {
+ self.infcx.next_ty_var(Span::Dummy)
+ } else {
+ ty.super_fold_with(self)
+ }
}
fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
@@ -450,14 +460,14 @@ impl<'db> InferCtxt<'db> {
}
if ct.is_ct_error() {
- self.infcx.next_const_var()
+ self.infcx.next_const_var(Span::Dummy)
} else {
ct.super_fold_with(self)
}
}
fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
- if r.is_error() { self.infcx.next_region_var() } else { r }
+ if r.is_error() { self.infcx.next_region_var(Span::Dummy) } else { r }
}
}
@@ -491,8 +501,7 @@ impl<'db> InferCtxt<'db> {
/// check::<&'_ T>();
/// }
/// ```
- #[expect(dead_code, reason = "this is used in rustc")]
- fn predicate_must_hold_considering_regions(
+ pub fn predicate_must_hold_considering_regions(
&self,
obligation: &PredicateObligation<'db>,
) -> bool {
@@ -504,11 +513,59 @@ impl<'db> InferCtxt<'db> {
/// not entirely accurate if inference variables are involved.
///
/// This version ignores all outlives constraints.
- #[expect(dead_code, reason = "this is used in rustc")]
- fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'db>) -> bool {
+ pub fn predicate_must_hold_modulo_regions(
+ &self,
+ obligation: &PredicateObligation<'db>,
+ ) -> bool {
self.evaluate_obligation(obligation).must_apply_modulo_regions()
}
+ /// Check whether a `ty` implements given trait(trait_def_id) without side-effects.
+ ///
+ /// The inputs are:
+ ///
+ /// - the def-id of the trait
+ /// - the type parameters of the trait, including the self-type
+ /// - the parameter environment
+ ///
+ /// Invokes `evaluate_obligation`, so in the event that evaluating
+ /// `Ty: Trait` causes overflow, EvaluatedToAmbigStackDependent will be returned.
+ ///
+ /// `type_implements_trait` is a convenience function for simple cases like
+ ///
+ /// ```ignore (illustrative)
+ /// let copy_trait = infcx.tcx.require_lang_item(LangItem::Copy, span);
+ /// let implements_copy = infcx.type_implements_trait(copy_trait, [ty], param_env)
+ /// .must_apply_modulo_regions();
+ /// ```
+ ///
+ /// In most cases you should instead create an [Obligation] and check whether
+ /// it holds via [`evaluate_obligation`] or one of its helper functions like
+ /// [`predicate_must_hold_modulo_regions`], because it properly handles higher ranked traits
+ /// and it is more convenient and safer when your `params` are inside a [`Binder`].
+ ///
+ /// [Obligation]: traits::Obligation
+ /// [`evaluate_obligation`]: InferCtxt::evaluate_obligation
+ /// [`predicate_must_hold_modulo_regions`]: InferCtxt::predicate_must_hold_modulo_regions
+ /// [`Binder`]: rustc_type_ir::Binder
+ #[instrument(level = "debug", skip(self, params), ret)]
+ pub fn type_implements_trait(
+ &self,
+ trait_def_id: TraitId,
+ params: impl IntoIterator<Item: Into<GenericArg<'db>>>,
+ param_env: ParamEnv<'db>,
+ ) -> EvaluationResult {
+ let trait_ref = TraitRef::new(self.interner, trait_def_id.into(), params);
+
+ let obligation = traits::Obligation {
+ cause: traits::ObligationCause::dummy(),
+ param_env,
+ recursion_depth: 0,
+ predicate: trait_ref.upcast(self.interner),
+ };
+ self.evaluate_obligation(&obligation)
+ }
+
/// Evaluate a given predicate, capturing overflow and propagating it back.
fn evaluate_obligation(&self, obligation: &PredicateObligation<'db>) -> EvaluationResult {
self.probe(|snapshot| {
@@ -561,6 +618,13 @@ impl<'db> InferCtxt<'db> {
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id)
}
+ pub fn type_is_sized_modulo_regions(&self, param_env: ParamEnv<'db>, ty: Ty<'db>) -> bool {
+ let Some(sized_def_id) = self.interner.lang_items().Sized else {
+ return true;
+ };
+ traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, sized_def_id)
+ }
+
pub fn type_is_use_cloned_modulo_regions(&self, param_env: ParamEnv<'db>, ty: Ty<'db>) -> bool {
let ty = self.resolve_vars_if_possible(ty);
@@ -680,80 +744,49 @@ impl<'db> InferCtxt<'db> {
self.inner.borrow_mut().type_variables().num_vars()
}
- pub fn next_var_for_param(&self, id: GenericParamId) -> GenericArg<'db> {
- match id {
- GenericParamId::TypeParamId(_) => self.next_ty_var().into(),
- GenericParamId::ConstParamId(_) => self.next_const_var().into(),
- GenericParamId::LifetimeParamId(_) => self.next_region_var().into(),
- }
+ pub fn next_ty_var(&self, span: Span) -> Ty<'db> {
+ let vid = self.next_ty_vid(span);
+ Ty::new_var(self.interner, vid)
}
- pub fn next_ty_var(&self) -> Ty<'db> {
- self.next_ty_var_with_origin(TypeVariableOrigin { param_def_id: None })
+ pub fn next_ty_vid(&self, span: Span) -> TyVid {
+ self.next_ty_var_id_in_universe(self.universe(), span)
}
- pub fn next_ty_vid(&self) -> TyVid {
- self.inner
- .borrow_mut()
- .type_variables()
- .new_var(self.universe(), TypeVariableOrigin { param_def_id: None })
+ pub fn next_ty_var_id_in_universe(&self, universe: UniverseIndex, span: Span) -> TyVid {
+ self.inner.borrow_mut().type_variables().new_var(universe, span)
}
- pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'db> {
- let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin);
+ pub fn next_ty_var_in_universe(&self, universe: UniverseIndex, span: Span) -> Ty<'db> {
+ let vid = self.next_ty_var_id_in_universe(universe, span);
Ty::new_var(self.interner, vid)
}
- pub fn next_ty_var_id_in_universe(&self, universe: UniverseIndex) -> TyVid {
- let origin = TypeVariableOrigin { param_def_id: None };
- self.inner.borrow_mut().type_variables().new_var(universe, origin)
- }
-
- pub fn next_ty_var_in_universe(&self, universe: UniverseIndex) -> Ty<'db> {
- let vid = self.next_ty_var_id_in_universe(universe);
- Ty::new_var(self.interner, vid)
+ pub fn next_const_var(&self, span: Span) -> Const<'db> {
+ let vid = self.next_const_vid(span);
+ Const::new_var(self.interner, vid)
}
- pub fn next_const_var(&self) -> Const<'db> {
- self.next_const_var_with_origin(ConstVariableOrigin {})
+ pub fn next_const_vid(&self, span: Span) -> ConstVid {
+ self.next_const_vid_in_universe(self.universe(), span)
}
- pub fn next_const_vid(&self) -> ConstVid {
+ pub fn next_const_vid_in_universe(&self, universe: UniverseIndex, span: Span) -> ConstVid {
self.inner
.borrow_mut()
.const_unification_table()
- .new_key(ConstVariableValue::Unknown {
- origin: ConstVariableOrigin {},
- universe: self.universe(),
- })
+ .new_key(ConstVariableValue::Unknown { span, universe })
.vid
}
- pub fn next_const_var_with_origin(&self, origin: ConstVariableOrigin) -> Const<'db> {
- let vid = self
- .inner
- .borrow_mut()
- .const_unification_table()
- .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() })
- .vid;
- Const::new_var(self.interner, vid)
- }
-
- pub fn next_const_var_in_universe(&self, universe: UniverseIndex) -> Const<'db> {
- let origin = ConstVariableOrigin {};
- let vid = self
- .inner
- .borrow_mut()
- .const_unification_table()
- .new_key(ConstVariableValue::Unknown { origin, universe })
- .vid;
+ pub fn next_const_var_in_universe(&self, universe: UniverseIndex, span: Span) -> Const<'db> {
+ let vid = self.next_const_vid_in_universe(universe, span);
Const::new_var(self.interner, vid)
}
pub fn next_int_var(&self) -> Ty<'db> {
- let next_int_var_id =
- self.inner.borrow_mut().int_unification_table().new_key(IntVarValue::Unknown);
- Ty::new_int_var(self.interner, next_int_var_id)
+ let vid = self.next_int_vid();
+ Ty::new_int_var(self.interner, vid)
}
pub fn next_int_vid(&self) -> IntVid {
@@ -771,27 +804,27 @@ impl<'db> InferCtxt<'db> {
/// Creates a fresh region variable with the next available index.
/// The variable will be created in the maximum universe created
/// thus far, allowing it to name any region created thus far.
- pub fn next_region_var(&self) -> Region<'db> {
- self.next_region_var_in_universe(self.universe())
+ pub fn next_region_var(&self, span: Span) -> Region<'db> {
+ self.next_region_var_in_universe(self.universe(), span)
}
- pub fn next_region_vid(&self) -> RegionVid {
- self.inner.borrow_mut().unwrap_region_constraints().new_region_var(self.universe())
+ pub fn next_region_vid(&self, span: Span) -> RegionVid {
+ self.inner.borrow_mut().unwrap_region_constraints().new_region_var(self.universe(), span)
}
/// Creates a fresh region variable with the next available index
/// in the given universe; typically, you can use
/// `next_region_var` and just use the maximal universe.
- pub fn next_region_var_in_universe(&self, universe: UniverseIndex) -> Region<'db> {
+ pub fn next_region_var_in_universe(&self, universe: UniverseIndex, span: Span) -> Region<'db> {
let region_var =
- self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe);
+ self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe, span);
Region::new_var(self.interner, region_var)
}
- pub fn next_term_var_of_kind(&self, term: Term<'db>) -> Term<'db> {
+ pub fn next_term_var_of_kind(&self, term: Term<'db>, span: Span) -> Term<'db> {
match term.kind() {
- TermKind::Ty(_) => self.next_ty_var().into(),
- TermKind::Const(_) => self.next_const_var().into(),
+ TermKind::Ty(_) => self.next_ty_var(span).into(),
+ TermKind::Const(_) => self.next_const_var(span).into(),
}
}
@@ -809,24 +842,12 @@ impl<'db> InferCtxt<'db> {
self.inner.borrow_mut().unwrap_region_constraints().num_region_vars()
}
- /// Just a convenient wrapper of `next_region_var` for using during NLL.
- #[instrument(skip(self), level = "debug")]
- pub fn next_nll_region_var(&self) -> Region<'db> {
- self.next_region_var()
- }
-
- /// Just a convenient wrapper of `next_region_var` for using during NLL.
- #[instrument(skip(self), level = "debug")]
- pub fn next_nll_region_var_in_universe(&self, universe: UniverseIndex) -> Region<'db> {
- self.next_region_var_in_universe(universe)
- }
-
- fn var_for_def(&self, id: GenericParamId) -> GenericArg<'db> {
+ pub fn var_for_def(&self, id: GenericParamId, span: Span) -> GenericArg<'db> {
match id {
GenericParamId::LifetimeParamId(_) => {
// Create a region inference variable for the given
// region parameter definition.
- self.next_region_var().into()
+ self.next_region_var(span).into()
}
GenericParamId::TypeParamId(_) => {
// Create a type inference variable for the given
@@ -837,41 +858,27 @@ impl<'db> InferCtxt<'db> {
// used in a path such as `Foo::<T, U>::new()` will
// use an inference variable for `C` with `[T, U]`
// as the generic parameters for the default, `(T, U)`.
- let ty_var_id = self
- .inner
- .borrow_mut()
- .type_variables()
- .new_var(self.universe(), TypeVariableOrigin { param_def_id: None });
-
- Ty::new_var(self.interner, ty_var_id).into()
- }
- GenericParamId::ConstParamId(_) => {
- let origin = ConstVariableOrigin {};
- let const_var_id = self
- .inner
- .borrow_mut()
- .const_unification_table()
- .new_key(ConstVariableValue::Unknown { origin, universe: self.universe() })
- .vid;
- Const::new_var(self.interner, const_var_id).into()
+ self.next_ty_var(span).into()
}
+ GenericParamId::ConstParamId(_) => self.next_const_var(span).into(),
}
}
/// Given a set of generics defined on a type or impl, returns the generic parameters mapping
/// each type/region parameter to a fresh inference variable.
- pub fn fresh_args_for_item(&self, def_id: SolverDefId) -> GenericArgs<'db> {
- GenericArgs::for_item(self.interner, def_id, |_index, kind, _| self.var_for_def(kind))
+ pub fn fresh_args_for_item(&self, span: Span, def_id: SolverDefId) -> GenericArgs<'db> {
+ GenericArgs::for_item(self.interner, def_id, |_index, kind, _| self.var_for_def(kind, span))
}
/// Like `fresh_args_for_item()`, but first uses the args from `first`.
pub fn fill_rest_fresh_args(
&self,
+ span: Span,
def_id: SolverDefId,
first: impl IntoIterator<Item = GenericArg<'db>>,
) -> GenericArgs<'db> {
GenericArgs::fill_rest(self.interner, def_id, first, |_index, kind, _| {
- self.var_for_def(kind)
+ self.var_for_def(kind, span)
})
}
@@ -922,7 +929,7 @@ impl<'db> InferCtxt<'db> {
#[inline(always)]
pub fn can_define_opaque_ty(&self, id: impl Into<SolverDefId>) -> bool {
- match self.typing_mode_unchecked() {
+ match self.typing_mode_raw().assert_not_erased() {
TypingMode::Analysis { defining_opaque_types_and_generators } => {
defining_opaque_types_and_generators.contains(&id.into())
}
@@ -938,8 +945,8 @@ impl<'db> InferCtxt<'db> {
use self::type_variable::TypeVariableValue;
match self.inner.borrow_mut().type_variables().probe(vid) {
- TypeVariableValue::Known { value } => Ok(value),
- TypeVariableValue::Unknown { universe } => Err(universe),
+ TypeVariableValue::Known { value, .. } => Ok(value),
+ TypeVariableValue::Unknown { universe, .. } => Err(universe),
}
}
@@ -1007,6 +1014,13 @@ impl<'db> InferCtxt<'db> {
}
}
+ pub fn shallow_resolve_term(&self, term: Term<'db>) -> Term<'db> {
+ match term.kind() {
+ TermKind::Ty(ty) => self.shallow_resolve(ty).into(),
+ TermKind::Const(ct) => self.shallow_resolve_const(ct).into(),
+ }
+ }
+
pub fn root_var(&self, var: TyVid) -> TyVid {
self.inner.borrow_mut().type_variables().root_var(var)
}
@@ -1084,7 +1098,22 @@ impl<'db> InferCtxt<'db> {
pub fn probe_const_var(&self, vid: ConstVid) -> Result<Const<'db>, UniverseIndex> {
match self.inner.borrow_mut().const_unification_table().probe_value(vid) {
ConstVariableValue::Known { value } => Ok(value),
- ConstVariableValue::Unknown { origin: _, universe } => Err(universe),
+ ConstVariableValue::Unknown { span: _, universe } => Err(universe),
+ }
+ }
+
+ /// Returns the span of the type variable identified by `vid`.
+ ///
+ /// No attempt is made to resolve `vid` to its root variable.
+ pub fn type_var_span(&self, vid: TyVid) -> Span {
+ self.inner.borrow_mut().type_variables().var_span(vid)
+ }
+
+ /// Returns the span of the const variable identified by `vid`
+ pub fn const_var_span(&self, vid: ConstVid) -> Option<Span> {
+ match self.inner.borrow_mut().const_unification_table().probe_value(vid) {
+ ConstVariableValue::Known { .. } => None,
+ ConstVariableValue::Unknown { span, .. } => Some(span),
}
}
@@ -1097,6 +1126,7 @@ impl<'db> InferCtxt<'db> {
// use [`InferCtxt::enter_forall`] instead.
pub fn instantiate_binder_with_fresh_vars<T>(
&self,
+ span: Span,
_lbrct: BoundRegionConversionTime,
value: Binder<'db, T>,
) -> T
@@ -1112,9 +1142,9 @@ impl<'db> InferCtxt<'db> {
for bound_var_kind in bound_vars {
let arg: GenericArg<'db> = match bound_var_kind {
- BoundVariableKind::Ty(_) => self.next_ty_var().into(),
- BoundVariableKind::Region(_) => self.next_region_var().into(),
- BoundVariableKind::Const => self.next_const_var().into(),
+ BoundVariableKind::Ty(_) => self.next_ty_var(span).into(),
+ BoundVariableKind::Region(_) => self.next_region_var(span).into(),
+ BoundVariableKind::Const => self.next_const_var(span).into(),
};
args.push(arg);
}
@@ -1169,7 +1199,7 @@ impl<'db> InferCtxt<'db> {
#[inline]
pub fn is_ty_infer_var_definitely_unchanged<'a>(
&'a self,
- ) -> impl Fn(TyOrConstInferVar) -> bool + Captures<'db> + 'a {
+ ) -> impl Fn(TyOrConstInferVar) -> bool + use<'a, 'db> {
// This hoists the borrow/release out of the loop body.
let inner = self.inner.try_borrow();
@@ -1321,7 +1351,7 @@ impl TyOrConstInferVar {
impl<'db> TypeTrace<'db> {
pub fn types(cause: &ObligationCause, a: Ty<'db>, b: Ty<'db>) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
}
}
@@ -1331,12 +1361,12 @@ impl<'db> TypeTrace<'db> {
a: TraitRef<'db>,
b: TraitRef<'db>,
) -> TypeTrace<'db> {
- TypeTrace { cause: cause.clone(), values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
+ TypeTrace { cause: *cause, values: ValuePairs::TraitRefs(ExpectedFound::new(a, b)) }
}
pub fn consts(cause: &ObligationCause, a: Const<'db>, b: Const<'db>) -> TypeTrace<'db> {
TypeTrace {
- cause: cause.clone(),
+ cause: *cause,
values: ValuePairs::Terms(ExpectedFound::new(a.into(), b.into())),
}
}
diff --git a/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs
index 7bb39519f5..544d79daf0 100644
--- a/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs
+++ b/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs
@@ -15,9 +15,12 @@ use self::CombineMapType::*;
use self::UndoLog::*;
use super::MemberConstraint;
use super::unify_key::RegionVidKey;
-use crate::next_solver::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot};
use crate::next_solver::infer::unify_key::RegionVariableValue;
use crate::next_solver::{AliasTy, Binder, DbInterner, ParamTy, PlaceholderType, Region, Ty};
+use crate::{
+ Span,
+ next_solver::infer::snapshot::undo_log::{InferCtxtUndoLogs, Snapshot},
+};
#[derive(Debug, Clone, Default)]
pub struct RegionConstraintStorage<'db> {
@@ -281,6 +284,7 @@ pub struct RegionVariableInfo {
// This would be currently unsound as it would cause us to drop the universe
// changes in `lexical_region_resolve`.
pub universe: UniverseIndex,
+ pub span: Span,
}
pub(crate) struct RegionSnapshot {
@@ -350,8 +354,12 @@ impl<'db> RegionConstraintCollector<'db, '_> {
*any_unifications = false;
// Manually inlined `self.unification_table_mut()` as `self` is used in the closure.
ut::UnificationTable::with_log(&mut self.storage.unification_table, &mut self.undo_log)
- .reset_unifications(|key| RegionVariableValue::Unknown {
- universe: self.storage.var_infos[key.vid].universe,
+ .reset_unifications(|key| {
+ let var_info = &self.storage.var_infos[key.vid];
+ RegionVariableValue::Unknown {
+ universe: var_info.universe,
+ span: var_info.span,
+ }
});
}
@@ -372,10 +380,11 @@ impl<'db> RegionConstraintCollector<'db, '_> {
self.storage.any_unifications = snapshot.any_unifications;
}
- pub(super) fn new_region_var(&mut self, universe: UniverseIndex) -> RegionVid {
- let vid = self.storage.var_infos.push(RegionVariableInfo { universe });
+ pub(super) fn new_region_var(&mut self, universe: UniverseIndex, span: Span) -> RegionVid {
+ let vid = self.storage.var_infos.push(RegionVariableInfo { universe, span });
- let u_vid = self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe });
+ let u_vid =
+ self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe, span });
assert_eq!(vid, u_vid.vid);
self.undo_log.push(AddVar(vid));
debug!("created new region variable {:?} in {:?}", vid, universe);
@@ -409,7 +418,7 @@ impl<'db> RegionConstraintCollector<'db, '_> {
debug!("make_eqregion: unifying {:?} with {:?}", vid, b);
if self
.unification_table_mut()
- .unify_var_value(vid, RegionVariableValue::Known { value: b })
+ .unify_var_value(vid, RegionVariableValue::Known { value: b, span: None })
.is_ok()
{
self.storage.any_unifications = true;
@@ -419,7 +428,7 @@ impl<'db> RegionConstraintCollector<'db, '_> {
debug!("make_eqregion: unifying {:?} with {:?}", a, vid);
if self
.unification_table_mut()
- .unify_var_value(vid, RegionVariableValue::Known { value: a })
+ .unify_var_value(vid, RegionVariableValue::Known { value: a, span: None })
.is_ok()
{
self.storage.any_unifications = true;
@@ -459,6 +468,7 @@ impl<'db> RegionConstraintCollector<'db, '_> {
pub(super) fn lub_regions(
&mut self,
db: DbInterner<'db>,
+ origin: Span,
a: Region<'db>,
b: Region<'db>,
) -> Region<'db> {
@@ -470,13 +480,14 @@ impl<'db> RegionConstraintCollector<'db, '_> {
} else if a == b {
a // LUB(a,a) = a
} else {
- self.combine_vars(db, Lub, a, b)
+ self.combine_vars(db, Lub, a, b, origin)
}
}
pub(super) fn glb_regions(
&mut self,
db: DbInterner<'db>,
+ origin: Span,
a: Region<'db>,
b: Region<'db>,
) -> Region<'db> {
@@ -490,7 +501,7 @@ impl<'db> RegionConstraintCollector<'db, '_> {
} else if a == b {
a // GLB(a,a) = a
} else {
- self.combine_vars(db, Glb, a, b)
+ self.combine_vars(db, Glb, a, b, origin)
}
}
@@ -504,15 +515,15 @@ impl<'db> RegionConstraintCollector<'db, '_> {
let mut ut = self.unification_table_mut();
let root_vid = ut.find(vid).vid;
match ut.probe_value(root_vid) {
- RegionVariableValue::Known { value } => value,
+ RegionVariableValue::Known { value, .. } => value,
RegionVariableValue::Unknown { .. } => Region::new_var(cx, root_vid),
}
}
pub fn probe_value(&mut self, vid: RegionVid) -> Result<Region<'db>, UniverseIndex> {
match self.unification_table_mut().probe_value(vid) {
- RegionVariableValue::Known { value } => Ok(value),
- RegionVariableValue::Unknown { universe } => Err(universe),
+ RegionVariableValue::Known { value, .. } => Ok(value),
+ RegionVariableValue::Unknown { universe, .. } => Err(universe),
}
}
@@ -529,6 +540,7 @@ impl<'db> RegionConstraintCollector<'db, '_> {
t: CombineMapType,
a: Region<'db>,
b: Region<'db>,
+ origin: Span,
) -> Region<'db> {
let vars = TwoRegions { a, b };
if let Some(c) = self.combine_map(t.clone()).get(&vars) {
@@ -537,7 +549,7 @@ impl<'db> RegionConstraintCollector<'db, '_> {
let a_universe = self.universe(a);
let b_universe = self.universe(b);
let c_universe = cmp::max(a_universe, b_universe);
- let c = self.new_region_var(c_universe);
+ let c = self.new_region_var(c_universe, origin);
self.combine_map(t.clone()).insert(vars.clone(), c);
self.undo_log.push(AddCombination(t.clone(), vars));
let new_r = Region::new_var(cx, c);
@@ -567,8 +579,15 @@ impl<'db> RegionConstraintCollector<'db, '_> {
}
}
- pub fn vars_since_snapshot(&self, value_count: usize) -> Range<RegionVid> {
- RegionVid::from(value_count)..RegionVid::from(self.storage.unification_table.len())
+ pub fn vars_since_snapshot(&self, value_count: usize) -> (Range<RegionVid>, Vec<Span>) {
+ let range =
+ RegionVid::from(value_count)..RegionVid::from(self.storage.unification_table.len());
+ (
+ range.clone(),
+ (range.start.as_usize()..range.end.as_usize())
+ .map(|index| self.storage.var_infos[RegionVid::from_usize(index)].span)
+ .collect(),
+ )
}
/// See `InferCtxt::region_constraints_added_in_snapshot`.
diff --git a/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/crates/hir-ty/src/next_solver/infer/relate/generalize.rs
index d621dd4906..e55e43a4cd 100644
--- a/crates/hir-ty/src/next_solver/infer/relate/generalize.rs
+++ b/crates/hir-ty/src/next_solver/infer/relate/generalize.rs
@@ -16,7 +16,6 @@ use tracing::{debug, instrument, warn};
use super::{
PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation,
};
-use crate::next_solver::infer::unify_key::ConstVariableValue;
use crate::next_solver::infer::{InferCtxt, relate};
use crate::next_solver::util::MaxUniverse;
use crate::next_solver::{
@@ -24,6 +23,7 @@ use crate::next_solver::{
Term, TermVid, Ty, TyKind, TypingMode, UnevaluatedConst,
};
use crate::next_solver::{GenericArgs, infer::type_variable::TypeVariableValue};
+use crate::{Span, next_solver::infer::unify_key::ConstVariableValue};
impl<'db> InferCtxt<'db> {
/// The idea is that we should ensure that the type variable `target_vid`
@@ -60,6 +60,7 @@ impl<'db> InferCtxt<'db> {
// `?1 <: ?3`.
let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } = self
.generalize(
+ relation.span(),
relation.structurally_relate_aliases(),
target_vid,
instantiation_variance,
@@ -179,6 +180,7 @@ impl<'db> InferCtxt<'db> {
// constants and generic expressions are not yet handled correctly.
let Generalization { value_may_be_infer: generalized_ct, has_unconstrained_ty_var } = self
.generalize(
+ relation.span(),
relation.structurally_relate_aliases(),
target_vid,
Variance::Invariant,
@@ -220,6 +222,7 @@ impl<'db> InferCtxt<'db> {
/// This checks for cycles -- that is, whether `source_term` references `target_vid`.
fn generalize<T: Into<Term<'db>> + Relate<DbInterner<'db>>>(
&self,
+ span: Span,
structurally_relate_aliases: StructurallyRelateAliases,
target_vid: impl Into<TermVid>,
ambient_variance: Variance,
@@ -238,6 +241,7 @@ impl<'db> InferCtxt<'db> {
let mut generalizer = Generalizer {
infcx: self,
+ span,
structurally_relate_aliases,
root_vid,
for_universe,
@@ -270,6 +274,8 @@ impl<'db> InferCtxt<'db> {
struct Generalizer<'me, 'db> {
infcx: &'me InferCtxt<'db>,
+ span: Span,
+
/// Whether aliases should be related structurally. If not, we have to
/// be careful when generalizing aliases.
structurally_relate_aliases: StructurallyRelateAliases,
@@ -318,7 +324,7 @@ impl<'db> Generalizer<'_, 'db> {
/// if we're currently in a bivariant context.
fn next_ty_var_for_alias(&mut self) -> Ty<'db> {
self.has_unconstrained_ty_var |= self.ambient_variance == Variance::Bivariant;
- self.infcx.next_ty_var_in_universe(self.for_universe)
+ self.infcx.next_ty_var_in_universe(self.for_universe, self.span)
}
/// An occurs check failure inside of an alias does not mean
@@ -453,11 +459,11 @@ impl<'db> TypeRelation<DbInterner<'db>> for Generalizer<'_, 'db> {
} else {
let probe = inner.type_variables().probe(vid);
match probe {
- TypeVariableValue::Known { value: u } => {
+ TypeVariableValue::Known { value: u, .. } => {
drop(inner);
self.relate(u, u)
}
- TypeVariableValue::Unknown { universe } => {
+ TypeVariableValue::Unknown { universe, .. } => {
match self.ambient_variance {
// Invariant: no need to make a fresh type variable
// if we can name the universe.
@@ -477,7 +483,7 @@ impl<'db> TypeRelation<DbInterner<'db>> for Generalizer<'_, 'db> {
Variance::Covariant | Variance::Contravariant => (),
}
- let origin = inner.type_variables().var_origin(vid);
+ let origin = inner.type_variables().var_span(vid);
let new_var_id =
inner.type_variables().new_var(self.for_universe, origin);
// If we're in the new solver and create a new inference
@@ -579,7 +585,7 @@ impl<'db> TypeRelation<DbInterner<'db>> for Generalizer<'_, 'db> {
}
}
- Ok(self.infcx.next_region_var_in_universe(self.for_universe))
+ Ok(self.infcx.next_region_var_in_universe(self.for_universe, self.span))
}
#[instrument(level = "debug", skip(self, c2), ret)]
@@ -605,13 +611,13 @@ impl<'db> TypeRelation<DbInterner<'db>> for Generalizer<'_, 'db> {
drop(inner);
self.relate(u, u)
}
- ConstVariableValue::Unknown { origin, universe } => {
+ ConstVariableValue::Unknown { span, universe } => {
if self.for_universe.can_name(universe) {
Ok(c)
} else {
let new_var_id = variable_table
.new_key(ConstVariableValue::Unknown {
- origin,
+ span,
universe: self.for_universe,
})
.vid;
diff --git a/crates/hir-ty/src/next_solver/infer/relate/lattice.rs b/crates/hir-ty/src/next_solver/infer/relate/lattice.rs
index 3522827a9e..f3af697feb 100644
--- a/crates/hir-ty/src/next_solver/infer/relate/lattice.rs
+++ b/crates/hir-ty/src/next_solver/infer/relate/lattice.rs
@@ -19,7 +19,7 @@
use rustc_type_ir::{
AliasRelationDirection, Interner, TypeVisitableExt, Upcast, Variance,
- inherent::{IntoKind, Span as _},
+ inherent::IntoKind,
relate::{
Relate, StructurallyRelateAliases, TypeRelation, VarianceDiagInfo,
combine::{
@@ -28,13 +28,16 @@ use rustc_type_ir::{
},
};
-use crate::next_solver::{
- AliasTy, Binder, Const, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, PredicateKind,
- Region, SolverDefId, Span, Ty, TyKind,
- infer::{
- InferCtxt, TypeTrace,
- relate::RelateResult,
- traits::{Obligation, PredicateObligations},
+use crate::{
+ Span,
+ next_solver::{
+ AliasTy, Binder, Const, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, PredicateKind,
+ Region, SolverDefId, Ty, TyKind,
+ infer::{
+ InferCtxt, TypeTrace,
+ relate::RelateResult,
+ traits::{Obligation, PredicateObligations},
+ },
},
};
@@ -154,12 +157,12 @@ impl<'db> TypeRelation<DbInterner<'db>> for LatticeOp<'_, 'db> {
// iterate on the subtype obligations that are returned, but I
// think this suffices. -nmatsakis
(TyKind::Infer(rustc_type_ir::TyVar(..)), _) => {
- let v = infcx.next_ty_var();
+ let v = infcx.next_ty_var(self.span());
self.relate_bound(v, b, a)?;
Ok(v)
}
(_, TyKind::Infer(rustc_type_ir::TyVar(..))) => {
- let v = infcx.next_ty_var();
+ let v = infcx.next_ty_var(self.span());
self.relate_bound(v, a, b)?;
Ok(v)
}
@@ -178,10 +181,10 @@ impl<'db> TypeRelation<DbInterner<'db>> for LatticeOp<'_, 'db> {
let mut constraints = inner.unwrap_region_constraints();
Ok(match self.kind {
// GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8
- LatticeOpKind::Glb => constraints.lub_regions(self.cx(), a, b),
+ LatticeOpKind::Glb => constraints.lub_regions(self.cx(), self.span(), a, b),
// LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8
- LatticeOpKind::Lub => constraints.glb_regions(self.cx(), a, b),
+ LatticeOpKind::Lub => constraints.glb_regions(self.cx(), self.span(), a, b),
})
}
@@ -239,7 +242,7 @@ impl<'infcx, 'db> LatticeOp<'infcx, 'db> {
impl<'db> PredicateEmittingRelation<InferCtxt<'db>> for LatticeOp<'_, 'db> {
fn span(&self) -> Span {
- Span::dummy()
+ self.trace.cause.span()
}
fn structurally_relate_aliases(&self) -> StructurallyRelateAliases {
@@ -255,18 +258,13 @@ impl<'db> PredicateEmittingRelation<InferCtxt<'db>> for LatticeOp<'_, 'db> {
preds: impl IntoIterator<Item: Upcast<DbInterner<'db>, Predicate<'db>>>,
) {
self.obligations.extend(preds.into_iter().map(|pred| {
- Obligation::new(self.infcx.interner, self.trace.cause.clone(), self.param_env, pred)
+ Obligation::new(self.infcx.interner, self.trace.cause, self.param_env, pred)
}))
}
fn register_goals(&mut self, goals: impl IntoIterator<Item = Goal<'db, Predicate<'db>>>) {
self.obligations.extend(goals.into_iter().map(|goal| {
- Obligation::new(
- self.infcx.interner,
- self.trace.cause.clone(),
- goal.param_env,
- goal.predicate,
- )
+ Obligation::new(self.infcx.interner, self.trace.cause, goal.param_env, goal.predicate)
}))
}
diff --git a/crates/hir-ty/src/next_solver/infer/select.rs b/crates/hir-ty/src/next_solver/infer/select.rs
index bd407fd157..d6f0379c11 100644
--- a/crates/hir-ty/src/next_solver/infer/select.rs
+++ b/crates/hir-ty/src/next_solver/infer/select.rs
@@ -10,6 +10,7 @@ use rustc_type_ir::{
};
use crate::{
+ Span,
db::InternedOpaqueTyId,
next_solver::{
AnyImplId, Const, ErrorGuaranteed, GenericArgs, Goal, TraitRef, Ty, TypeError,
@@ -63,7 +64,7 @@ pub enum NotConstEvaluatable {
/// so they are noops when unioned with a definite error, and within
/// the categories it's easy to see that the unions are correct.
#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
-pub(crate) enum EvaluationResult {
+pub enum EvaluationResult {
/// Evaluation successful.
EvaluatedToOk,
/// Evaluation successful, but there were unevaluated region obligations.
@@ -263,18 +264,24 @@ impl<'db> InferCtxt<'db> {
) -> SelectionResult<'db, Selection<'db>> {
self.visit_proof_tree(
Goal::new(self.interner, obligation.param_env, obligation.predicate),
- &mut Select {},
+ &mut Select { span: obligation.cause.span() },
)
.break_value()
.unwrap()
}
}
-struct Select {}
+struct Select {
+ span: Span,
+}
impl<'db> ProofTreeVisitor<'db> for Select {
type Result = ControlFlow<SelectionResult<'db, Selection<'db>>>;
+ fn span(&self) -> Span {
+ self.span
+ }
+
fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result {
let mut candidates = goal.candidates();
candidates.retain(|cand| cand.result().is_ok());
@@ -286,7 +293,10 @@ impl<'db> ProofTreeVisitor<'db> for Select {
// One candidate, no need to winnow.
if candidates.len() == 1 {
- return ControlFlow::Break(Ok(to_selection(candidates.into_iter().next().unwrap())));
+ return ControlFlow::Break(Ok(to_selection(
+ self.span,
+ candidates.into_iter().next().unwrap(),
+ )));
}
// Don't winnow until `Certainty::Yes` -- we don't need to winnow until
@@ -311,7 +321,7 @@ impl<'db> ProofTreeVisitor<'db> for Select {
}
}
- ControlFlow::Break(Ok(to_selection(candidates.into_iter().next().unwrap())))
+ ControlFlow::Break(Ok(to_selection(self.span, candidates.into_iter().next().unwrap())))
}
}
@@ -368,7 +378,7 @@ fn candidate_should_be_dropped_in_favor_of<'db>(
}
}
-fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option<Selection<'db>> {
+fn to_selection<'db>(span: Span, cand: InspectCandidate<'_, 'db>) -> Option<Selection<'db>> {
if let Certainty::Maybe { .. } = cand.shallow_certainty() {
return None;
}
@@ -376,7 +386,7 @@ fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option<Selection<'db>>
let nested = match cand.result().expect("expected positive result") {
Certainty::Yes => Vec::new(),
Certainty::Maybe { .. } => cand
- .instantiate_nested_goals()
+ .instantiate_nested_goals(span)
.into_iter()
.map(|nested| {
Obligation::new(
@@ -396,7 +406,7 @@ fn to_selection<'db>(cand: InspectCandidate<'_, 'db>) -> Option<Selection<'db>>
// For impl candidates, we do the rematch manually to compute the args.
ImplSource::UserDefined(ImplSourceUserDefinedData {
impl_def_id,
- args: cand.instantiate_impl_args(),
+ args: cand.instantiate_impl_args(span),
nested,
})
}
diff --git a/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs b/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs
index 5902f8043b..7cb3ab09d4 100644
--- a/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs
+++ b/crates/hir-ty/src/next_solver/infer/snapshot/fudge.rs
@@ -5,17 +5,19 @@ use ena::{
unify::{self as ut, UnifyKey},
};
use rustc_type_ir::{
- ConstVid, FloatVid, IntVid, RegionKind, RegionVid, TyVid, TypeFoldable, TypeFolder,
- TypeSuperFoldable, TypeVisitableExt, inherent::IntoKind,
+ ConstVid, FloatVid, IntVid, RegionVid, TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable,
+ TypeVisitableExt, inherent::IntoKind,
};
-use crate::next_solver::{
- Const, ConstKind, DbInterner, Region, Ty, TyKind,
- infer::{
- InferCtxt, UnificationTable, iter_idx_range,
- snapshot::VariableLengths,
- type_variable::TypeVariableOrigin,
- unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey},
+use crate::{
+ Span,
+ next_solver::{
+ Const, ConstKind, DbInterner, Region, RegionKind, Ty, TyKind,
+ infer::{
+ InferCtxt, UnificationTable, iter_idx_range,
+ snapshot::VariableLengths,
+ unify_key::{ConstVariableValue, ConstVidKey},
+ },
},
};
@@ -33,7 +35,7 @@ where
fn const_vars_since_snapshot<'db>(
table: &mut UnificationTable<'_, 'db, ConstVidKey<'db>>,
snapshot_var_len: usize,
-) -> (Range<ConstVid>, Vec<ConstVariableOrigin>) {
+) -> (Range<ConstVid>, Vec<Span>) {
let range = vars_since_snapshot(table, snapshot_var_len);
let range = range.start.vid..range.end.vid;
@@ -41,8 +43,8 @@ fn const_vars_since_snapshot<'db>(
range.clone(),
iter_idx_range(range)
.map(|index| match table.probe_value(index) {
- ConstVariableValue::Known { value: _ } => ConstVariableOrigin {},
- ConstVariableValue::Unknown { origin, universe: _ } => origin,
+ ConstVariableValue::Known { value: _ } => Span::Dummy,
+ ConstVariableValue::Unknown { span, universe: _ } => span,
})
.collect(),
)
@@ -128,11 +130,11 @@ impl<'db> InferCtxt<'db> {
}
struct SnapshotVarData {
- region_vars: Range<RegionVid>,
- type_vars: (Range<TyVid>, Vec<TypeVariableOrigin>),
+ region_vars: (Range<RegionVid>, Vec<Span>),
+ type_vars: (Range<TyVid>, Vec<Span>),
int_vars: Range<IntVid>,
float_vars: Range<FloatVid>,
- const_vars: (Range<ConstVid>, Vec<ConstVariableOrigin>),
+ const_vars: (Range<ConstVid>, Vec<Span>),
}
impl SnapshotVarData {
@@ -156,7 +158,7 @@ impl SnapshotVarData {
fn is_empty(&self) -> bool {
let SnapshotVarData { region_vars, type_vars, int_vars, float_vars, const_vars } = self;
- region_vars.is_empty()
+ region_vars.0.is_empty()
&& type_vars.0.is_empty()
&& int_vars.is_empty()
&& float_vars.is_empty()
@@ -182,8 +184,8 @@ impl<'a, 'db> TypeFolder<DbInterner<'db>> for InferenceFudger<'a, 'db> {
// This variable was created during the fudging.
// Recreate it with a fresh variable here.
let idx = vid.as_usize() - self.snapshot_vars.type_vars.0.start.as_usize();
- let origin = self.snapshot_vars.type_vars.1[idx];
- self.infcx.next_ty_var_with_origin(origin)
+ let span = self.snapshot_vars.type_vars.1[idx];
+ self.infcx.next_ty_var(span)
} else {
// This variable was created before the
// "fudging". Since we refresh all type
@@ -225,8 +227,10 @@ impl<'a, 'db> TypeFolder<DbInterner<'db>> for InferenceFudger<'a, 'db> {
fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
if let RegionKind::ReVar(vid) = r.kind() {
- if self.snapshot_vars.region_vars.contains(&vid) {
- self.infcx.next_region_var()
+ if self.snapshot_vars.region_vars.0.contains(&vid) {
+ let idx = vid.index() - self.snapshot_vars.region_vars.0.start.index();
+ let span = self.snapshot_vars.region_vars.1[idx];
+ self.infcx.next_region_var(span)
} else {
r
}
@@ -241,8 +245,8 @@ impl<'a, 'db> TypeFolder<DbInterner<'db>> for InferenceFudger<'a, 'db> {
rustc_type_ir::InferConst::Var(vid) => {
if self.snapshot_vars.const_vars.0.contains(&vid) {
let idx = vid.index() - self.snapshot_vars.const_vars.0.start.index();
- let origin = self.snapshot_vars.const_vars.1[idx];
- self.infcx.next_const_var_with_origin(origin)
+ let span = self.snapshot_vars.const_vars.1[idx];
+ self.infcx.next_const_var(span)
} else {
ct
}
diff --git a/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs
index 705aa43fb1..39c8a37adb 100644
--- a/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs
+++ b/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs
@@ -47,7 +47,7 @@ impl<'db> InferCtxt<'db> {
UndoLogs::<UndoLog<'db>>::num_open_snapshots(&self.inner.borrow_mut().undo_log)
}
- pub(crate) fn start_snapshot(&self) -> CombinedSnapshot {
+ pub fn start_snapshot(&self) -> CombinedSnapshot {
debug!("start_snapshot()");
let mut inner = self.inner.borrow_mut();
@@ -60,7 +60,7 @@ impl<'db> InferCtxt<'db> {
}
#[instrument(skip(self, snapshot), level = "debug")]
- pub(crate) fn rollback_to(&self, snapshot: CombinedSnapshot) {
+ pub fn rollback_to(&self, snapshot: CombinedSnapshot) {
let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot;
self.universe.set(universe);
@@ -71,7 +71,7 @@ impl<'db> InferCtxt<'db> {
}
#[instrument(skip(self, snapshot), level = "debug")]
- fn commit_from(&self, snapshot: CombinedSnapshot) {
+ pub fn commit_from(&self, snapshot: CombinedSnapshot) {
let CombinedSnapshot { undo_snapshot, region_constraints_snapshot: _, universe: _ } =
snapshot;
diff --git a/crates/hir-ty/src/next_solver/infer/traits.rs b/crates/hir-ty/src/next_solver/infer/traits.rs
index dde6234836..4584b35796 100644
--- a/crates/hir-ty/src/next_solver/infer/traits.rs
+++ b/crates/hir-ty/src/next_solver/infer/traits.rs
@@ -16,49 +16,37 @@ use rustc_type_ir::{
};
use tracing::debug;
-use crate::next_solver::{
- Clause, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, Span, TraitPredicate,
- TraitRef, Ty,
+use crate::{
+ Span,
+ next_solver::{
+ Clause, DbInterner, Goal, ParamEnv, PolyTraitPredicate, Predicate, TraitPredicate,
+ TraitRef, Ty,
+ },
};
use super::InferCtxt;
/// The reason why we incurred this obligation; used for error reporting.
-///
-/// Non-misc `ObligationCauseCode`s are stored on the heap. This gives the
-/// best trade-off between keeping the type small (which makes copies cheaper)
-/// while not doing too many heap allocations.
-///
-/// We do not want to intern this as there are a lot of obligation causes which
-/// only live for a short period of time.
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeVisitable, TypeFoldable)]
pub struct ObligationCause {
- // FIXME: This should contain an `ExprId`/`PatId` etc., and a cause code. But for now we
- // don't report trait solving diagnostics, so this is irrelevant.
- _private: (),
+ #[type_visitable(ignore)]
+ span: Span,
}
impl ObligationCause {
#[inline]
- pub fn new() -> ObligationCause {
- ObligationCause { _private: () }
+ pub fn new<S: Into<Span>>(span: S) -> ObligationCause {
+ ObligationCause { span: span.into() }
}
#[inline]
pub fn dummy() -> ObligationCause {
- ObligationCause::new()
+ ObligationCause::new(Span::Dummy)
}
#[inline]
- pub fn misc() -> ObligationCause {
- ObligationCause::new()
- }
-}
-
-impl Default for ObligationCause {
- #[inline]
- fn default() -> Self {
- Self::new()
+ pub(crate) fn span(&self) -> Span {
+ self.span
}
}
@@ -107,7 +95,7 @@ impl<'db> Elaboratable<DbInterner<'db>> for PredicateObligation<'db> {
fn child(&self, clause: Clause<'db>) -> Self {
Obligation {
- cause: self.cause.clone(),
+ cause: self.cause,
param_env: self.param_env,
recursion_depth: 0,
predicate: clause.as_predicate(),
@@ -117,11 +105,11 @@ impl<'db> Elaboratable<DbInterner<'db>> for PredicateObligation<'db> {
fn child_with_derived_cause(
&self,
clause: Clause<'db>,
- _span: Span,
+ span: Span,
_parent_trait_pred: PolyTraitPredicate<'db>,
_index: usize,
) -> Self {
- let cause = ObligationCause::new();
+ let cause = ObligationCause::new(span);
Obligation {
cause,
param_env: self.param_env,
@@ -175,7 +163,7 @@ impl<'db> PredicateObligation<'db> {
/// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`.
pub fn flip_polarity(&self, _interner: DbInterner<'db>) -> Option<PredicateObligation<'db>> {
Some(PredicateObligation {
- cause: self.cause.clone(),
+ cause: self.cause,
param_env: self.param_env,
predicate: self.predicate.flip_polarity()?,
recursion_depth: self.recursion_depth,
@@ -185,12 +173,12 @@ impl<'db> PredicateObligation<'db> {
impl<'db, O> Obligation<'db, O> {
pub fn new(
- tcx: DbInterner<'db>,
+ interner: DbInterner<'db>,
cause: ObligationCause,
param_env: ParamEnv<'db>,
predicate: impl Upcast<DbInterner<'db>, O>,
) -> Obligation<'db, O> {
- Self::with_depth(tcx, cause, 0, param_env, predicate)
+ Self::with_depth(interner, cause, 0, param_env, predicate)
}
/// We often create nested obligations without setting the correct depth.
@@ -202,13 +190,13 @@ impl<'db, O> Obligation<'db, O> {
}
pub fn with_depth(
- tcx: DbInterner<'db>,
+ interner: DbInterner<'db>,
cause: ObligationCause,
recursion_depth: usize,
param_env: ParamEnv<'db>,
predicate: impl Upcast<DbInterner<'db>, O>,
) -> Obligation<'db, O> {
- let predicate = predicate.upcast(tcx);
+ let predicate = predicate.upcast(interner);
Obligation { cause, param_env, recursion_depth, predicate }
}
@@ -217,7 +205,7 @@ impl<'db, O> Obligation<'db, O> {
tcx: DbInterner<'db>,
value: impl Upcast<DbInterner<'db>, P>,
) -> Obligation<'db, P> {
- Obligation::with_depth(tcx, self.cause.clone(), self.recursion_depth, self.param_env, value)
+ Obligation::with_depth(tcx, self.cause, self.recursion_depth, self.param_env, value)
}
}
diff --git a/crates/hir-ty/src/next_solver/infer/type_variable.rs b/crates/hir-ty/src/next_solver/infer/type_variable.rs
index 29e7b883c9..6b3936ba80 100644
--- a/crates/hir-ty/src/next_solver/infer/type_variable.rs
+++ b/crates/hir-ty/src/next_solver/infer/type_variable.rs
@@ -7,13 +7,12 @@ use std::ops::Range;
use ena::snapshot_vec as sv;
use ena::undo_log::Rollback;
use ena::unify as ut;
-use rustc_index::IndexVec;
use rustc_type_ir::TyVid;
use rustc_type_ir::UniverseIndex;
use rustc_type_ir::inherent::Ty as _;
use tracing::debug;
-use crate::next_solver::SolverDefId;
+use crate::Span;
use crate::next_solver::Ty;
use crate::next_solver::infer::{InferCtxtUndoLogs, iter_idx_range};
@@ -61,8 +60,6 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for TypeVariableStorage<'tcx> {
#[derive(Debug, Clone, Default)]
pub(crate) struct TypeVariableStorage<'db> {
- /// The origins of each type variable.
- values: IndexVec<TyVid, TypeVariableData>,
/// Two variables are unified in `eq_relations` when we have a
/// constraint `?X == ?Y`. This table also stores, for each key,
/// the known value.
@@ -94,20 +91,7 @@ pub(crate) struct TypeVariableTable<'a, 'db> {
undo_log: &'a mut InferCtxtUndoLogs<'db>,
}
-#[derive(Copy, Clone, Debug)]
-pub struct TypeVariableOrigin {
- /// `DefId` of the type parameter this was instantiated for, if any.
- ///
- /// This should only be used for diagnostics.
- pub param_def_id: Option<SolverDefId>,
-}
-
-#[derive(Debug, Clone)]
-pub(crate) struct TypeVariableData {
- origin: TypeVariableOrigin,
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug)]
pub(crate) enum TypeVariableValue<'db> {
Known { value: Ty<'db> },
Unknown { universe: UniverseIndex },
@@ -119,7 +103,7 @@ impl<'db> TypeVariableValue<'db> {
pub(crate) fn known(&self) -> Option<Ty<'db>> {
match self {
TypeVariableValue::Unknown { .. } => None,
- TypeVariableValue::Known { value } => Some(*value),
+ TypeVariableValue::Known { value, .. } => Some(*value),
}
}
@@ -145,19 +129,14 @@ impl<'db> TypeVariableStorage<'db> {
&self.eq_relations
}
- pub(super) fn finalize_rollback(&mut self) {
- debug_assert!(self.values.len() >= self.eq_relations.len());
- self.values.truncate(self.eq_relations.len());
- }
+ pub(super) fn finalize_rollback(&mut self) {}
}
impl<'db> TypeVariableTable<'_, 'db> {
- /// Returns the origin that was given when `vid` was created.
- ///
- /// Note that this function does not return care whether
- /// `vid` has been unified with something else or not.
- pub(crate) fn var_origin(&self, vid: TyVid) -> TypeVariableOrigin {
- self.storage.values[vid].origin
+ pub(crate) fn var_span(&mut self, vid: TyVid) -> Span {
+ // We return the span from unification and not equation, since when equating we also unify,
+ // and we want to prevent duplicate diagnostics from vars that were unified.
+ self.sub_unification_table().probe_value(vid).span
}
/// Records that `a == b`, depending on `dir`.
@@ -195,33 +174,20 @@ impl<'db> TypeVariableTable<'_, 'db> {
self.eq_relations().union_value(vid, TypeVariableValue::Known { value: ty });
}
- /// Creates a new type variable.
- ///
- /// - `diverging`: indicates if this is a "diverging" type
- /// variable, e.g., one created as the type of a `return`
- /// expression. The code in this module doesn't care if a
- /// variable is diverging, but the main Rust type-checker will
- /// sometimes "unify" such variables with the `!` or `()` types.
- /// - `origin`: indicates *why* the type variable was created.
- /// The code in this module doesn't care, but it can be useful
- /// for improving error messages.
- pub(crate) fn new_var(&mut self, universe: UniverseIndex, origin: TypeVariableOrigin) -> TyVid {
+ pub(crate) fn new_var(&mut self, universe: UniverseIndex, span: Span) -> TyVid {
let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe });
- let sub_key = self.sub_unification_table().new_key(());
+ let sub_key = self.sub_unification_table().new_key(TypeVariableSubValue { span });
debug_assert_eq!(eq_key.vid, sub_key.vid);
- let index = self.storage.values.push(TypeVariableData { origin });
- debug_assert_eq!(eq_key.vid, index);
-
- debug!("new_var(index={:?}, universe={:?}, origin={:?})", eq_key.vid, universe, origin);
+ debug!("new_var(index={:?}, universe={:?}, span={:?})", eq_key.vid, universe, span);
- index
+ eq_key.vid
}
/// Returns the number of type variables created thus far.
pub(crate) fn num_vars(&self) -> usize {
- self.storage.values.len()
+ self.storage.eq_relations.len()
}
/// Returns the "root" variable of `vid` in the `eq_relations`
@@ -268,12 +234,9 @@ impl<'db> TypeVariableTable<'_, 'db> {
}
/// Returns a range of the type variables created during the snapshot.
- pub(crate) fn vars_since_snapshot(
- &mut self,
- value_count: usize,
- ) -> (Range<TyVid>, Vec<TypeVariableOrigin>) {
+ pub(crate) fn vars_since_snapshot(&mut self, value_count: usize) -> (Range<TyVid>, Vec<Span>) {
let range = TyVid::from_usize(value_count)..TyVid::from_usize(self.num_vars());
- (range.clone(), iter_idx_range(range).map(|index| self.var_origin(index)).collect())
+ (range.clone(), iter_idx_range(range).map(|index| self.var_span(index)).collect())
}
/// Returns indices of all variables that are not yet
@@ -324,9 +287,6 @@ impl<'db> ut::UnifyKey for TyVidEqKey<'db> {
fn tag() -> &'static str {
"TyVidEqKey"
}
- fn order_roots(a: Self, _: &Self::Value, b: Self, _: &Self::Value) -> Option<(Self, Self)> {
- if a.vid.as_u32() < b.vid.as_u32() { Some((a, b)) } else { Some((b, a)) }
- }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -341,8 +301,13 @@ impl From<TyVid> for TyVidSubKey {
}
}
+#[derive(Debug, Clone, Copy)]
+pub(crate) struct TypeVariableSubValue {
+ span: Span,
+}
+
impl ut::UnifyKey for TyVidSubKey {
- type Value = ();
+ type Value = TypeVariableSubValue;
#[inline]
fn index(&self) -> u32 {
self.vid.as_u32()
@@ -356,6 +321,14 @@ impl ut::UnifyKey for TyVidSubKey {
}
}
+impl ut::UnifyValue for TypeVariableSubValue {
+ type Error = ut::NoError;
+
+ fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
+ Ok(TypeVariableSubValue { span: Span::pick_best(value1.span, value2.span) })
+ }
+}
+
impl<'db> ut::UnifyValue for TypeVariableValue<'db> {
type Error = ut::NoError;
@@ -369,11 +342,9 @@ impl<'db> ut::UnifyValue for TypeVariableValue<'db> {
}
// If one side is known, prefer that one.
- (&TypeVariableValue::Known { .. }, &TypeVariableValue::Unknown { .. }) => {
- Ok(value1.clone())
- }
- (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { .. }) => {
- Ok(value2.clone())
+ (&TypeVariableValue::Known { value }, &TypeVariableValue::Unknown { .. })
+ | (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { value }) => {
+ Ok(TypeVariableValue::Known { value })
}
// If both sides are *unknown*, it hardly matters, does it?
diff --git a/crates/hir-ty/src/next_solver/infer/unify_key.rs b/crates/hir-ty/src/next_solver/infer/unify_key.rs
index a09f65f082..061b8531d3 100644
--- a/crates/hir-ty/src/next_solver/infer/unify_key.rs
+++ b/crates/hir-ty/src/next_solver/infer/unify_key.rs
@@ -6,12 +6,15 @@ use std::marker::PhantomData;
use ena::unify::{NoError, UnifyKey, UnifyValue};
use rustc_type_ir::{ConstVid, RegionKind, RegionVid, UniverseIndex, inherent::IntoKind};
-use crate::next_solver::{Const, Region};
+use crate::{
+ Span,
+ next_solver::{Const, Region},
+};
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug)]
pub(crate) enum RegionVariableValue<'db> {
- Known { value: Region<'db> },
- Unknown { universe: UniverseIndex },
+ Known { value: Region<'db>, span: Option<Span> },
+ Unknown { universe: UniverseIndex, span: Span },
}
#[derive(PartialEq, Copy, Clone, Debug)]
@@ -51,9 +54,15 @@ impl<'db> UnifyValue for RegionVariableValue<'db> {
Err(RegionUnificationError)
}
- (RegionVariableValue::Known { value }, RegionVariableValue::Unknown { universe })
- | (RegionVariableValue::Unknown { universe }, RegionVariableValue::Known { value }) => {
- let universe_of_value = match (*value).kind() {
+ (
+ &RegionVariableValue::Known { value, span: span_known },
+ &RegionVariableValue::Unknown { universe, span: span_unknown },
+ )
+ | (
+ &RegionVariableValue::Unknown { universe, span: span_unknown },
+ &RegionVariableValue::Known { value, span: span_known },
+ ) => {
+ let universe_of_value = match value.kind() {
RegionKind::ReStatic
| RegionKind::ReErased
| RegionKind::ReLateParam(..)
@@ -65,23 +74,28 @@ impl<'db> UnifyValue for RegionVariableValue<'db> {
}
};
+ let span = match span_known {
+ Some(span_known) => Span::pick_best(span_known, span_unknown),
+ None => span_unknown,
+ };
if universe.can_name(universe_of_value) {
- Ok(RegionVariableValue::Known { value: *value })
+ Ok(RegionVariableValue::Known { value, span: Some(span) })
} else {
Err(RegionUnificationError)
}
}
(
- RegionVariableValue::Unknown { universe: a },
- RegionVariableValue::Unknown { universe: b },
+ &RegionVariableValue::Unknown { universe: a, span: span1 },
+ &RegionVariableValue::Unknown { universe: b, span: span2 },
) => {
// If we unify two unconstrained regions then whatever
// value they wind up taking (which must be the same value) must
// be nameable by both universes. Therefore, the resulting
// universe is the minimum of the two universes, because that is
// the one which contains the fewest names in scope.
- Ok(RegionVariableValue::Unknown { universe: (*a).min(*b) })
+ let span = Span::pick_best(span1, span2);
+ Ok(RegionVariableValue::Unknown { universe: a.min(b), span })
}
}
}
@@ -89,13 +103,10 @@ impl<'db> UnifyValue for RegionVariableValue<'db> {
// Generic consts.
-#[derive(Copy, Clone, Debug)]
-pub struct ConstVariableOrigin {}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug)]
pub(crate) enum ConstVariableValue<'db> {
Known { value: Const<'db> },
- Unknown { origin: ConstVariableOrigin, universe: UniverseIndex },
+ Unknown { span: Span, universe: UniverseIndex },
}
impl<'db> ConstVariableValue<'db> {
@@ -134,9 +145,6 @@ impl<'db> UnifyKey for ConstVidKey<'db> {
fn tag() -> &'static str {
"ConstVidKey"
}
- fn order_roots(a: Self, _: &Self::Value, b: Self, _: &Self::Value) -> Option<(Self, Self)> {
- if a.vid.as_u32() < b.vid.as_u32() { Some((a, b)) } else { Some((b, a)) }
- }
}
impl<'db> UnifyValue for ConstVariableValue<'db> {
@@ -149,25 +157,22 @@ impl<'db> UnifyValue for ConstVariableValue<'db> {
}
// If one side is known, prefer that one.
- (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => {
- Ok(value1.clone())
- }
- (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => {
- Ok(value2.clone())
- }
+ (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => Ok(*value1),
+ (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => Ok(*value2),
// If both sides are *unknown*, it hardly matters, does it?
(
- ConstVariableValue::Unknown { origin, universe: universe1 },
- ConstVariableValue::Unknown { origin: _, universe: universe2 },
+ &ConstVariableValue::Unknown { span: span1, universe: universe1 },
+ &ConstVariableValue::Unknown { span: span2, universe: universe2 },
) => {
// If we unify two unbound variables, ?T and ?U, then whatever
// value they wind up taking (which must be the same value) must
// be nameable by both universes. Therefore, the resulting
// universe is the minimum of the two universes, because that is
// the one which contains the fewest names in scope.
- let universe = cmp::min(*universe1, *universe2);
- Ok(ConstVariableValue::Unknown { origin: *origin, universe })
+ let universe = cmp::min(universe1, universe2);
+ let span = Span::pick_best(span1, span2);
+ Ok(ConstVariableValue::Unknown { span, universe })
}
}
}
diff --git a/crates/hir-ty/src/next_solver/inspect.rs b/crates/hir-ty/src/next_solver/inspect.rs
index 63a225b98f..7e2dfb7112 100644
--- a/crates/hir-ty/src/next_solver/inspect.rs
+++ b/crates/hir-ty/src/next_solver/inspect.rs
@@ -6,21 +6,26 @@ use rustc_next_trait_solver::{
};
use rustc_type_ir::{
VisitorResult,
- inherent::{IntoKind, Span as _},
- solve::{Certainty, GoalSource, MaybeCause, NoSolution},
+ inherent::IntoKind,
+ solve::{Certainty, GoalSource, MaybeCause, MaybeInfo, NoSolution},
};
-use crate::next_solver::{
- DbInterner, GenericArg, GenericArgs, Goal, NormalizesTo, ParamEnv, Predicate, PredicateKind,
- QueryResult, SolverContext, Span, Term,
- fulfill::NextSolverError,
- infer::{
- InferCtxt,
- traits::{Obligation, ObligationCause},
+use crate::{
+ Span,
+ next_solver::{
+ DbInterner, GenericArg, GenericArgs, Goal, NormalizesTo, ParamEnv, Predicate,
+ PredicateKind, QueryResult, SolverContext, Term,
+ fulfill::NextSolverError,
+ infer::{
+ InferCtxt,
+ traits::{Obligation, ObligationCause},
+ },
+ obligation_ctxt::ObligationCtxt,
},
- obligation_ctxt::ObligationCtxt,
};
+pub(crate) use rustc_next_trait_solver::solve::inspect::*;
+
pub(crate) struct InspectConfig {
pub(crate) max_depth: usize,
}
@@ -142,7 +147,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> {
&self,
visitor: &mut V,
) -> V::Result {
- for goal in self.instantiate_nested_goals() {
+ for goal in self.instantiate_nested_goals(visitor.span()) {
try_visit!(goal.visit_with(visitor));
}
@@ -153,7 +158,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> {
/// inference constraints. This function modifies the state of the `infcx`.
///
/// See [`Self::instantiate_impl_args`] if you need the impl args too.
- pub(crate) fn instantiate_nested_goals(&self) -> Vec<InspectGoal<'a, 'db>> {
+ pub(crate) fn instantiate_nested_goals(&self, span: Span) -> Vec<InspectGoal<'a, 'db>> {
let infcx = self.goal.infcx;
let param_env = self.goal.goal.param_env;
let mut orig_values = self.goal.orig_values.to_vec();
@@ -163,13 +168,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> {
match **step {
inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push((
source,
- instantiate_canonical_state(
- infcx,
- Span::dummy(),
- param_env,
- &mut orig_values,
- goal,
- ),
+ instantiate_canonical_state(infcx, span, param_env, &mut orig_values, goal),
)),
inspect::ProbeStep::RecordImplArgs { .. } => {}
inspect::ProbeStep::MakeCanonicalResponse { .. }
@@ -177,13 +176,8 @@ impl<'a, 'db> InspectCandidate<'a, 'db> {
}
}
- let () = instantiate_canonical_state(
- infcx,
- Span::dummy(),
- param_env,
- &mut orig_values,
- self.final_state,
- );
+ let () =
+ instantiate_canonical_state(infcx, span, param_env, &mut orig_values, self.final_state);
if let Some(term_hack) = &self.goal.normalizes_to_term_hack {
// FIXME: We ignore the expected term of `NormalizesTo` goals
@@ -194,14 +188,14 @@ impl<'a, 'db> InspectCandidate<'a, 'db> {
instantiated_goals
.into_iter()
- .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal))
+ .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span))
.collect()
}
/// Instantiate the args of an impl if this candidate came from a
/// `CandidateSource::Impl`. This function modifies the state of the
/// `infcx`.
- pub(crate) fn instantiate_impl_args(&self) -> GenericArgs<'db> {
+ pub(crate) fn instantiate_impl_args(&self, span: Span) -> GenericArgs<'db> {
let infcx = self.goal.infcx;
let param_env = self.goal.goal.param_env;
let mut orig_values = self.goal.orig_values.to_vec();
@@ -211,7 +205,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> {
inspect::ProbeStep::RecordImplArgs { impl_args } => {
let impl_args = instantiate_canonical_state(
infcx,
- Span::dummy(),
+ span,
param_env,
&mut orig_values,
impl_args,
@@ -219,7 +213,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> {
let () = instantiate_canonical_state(
infcx,
- Span::dummy(),
+ span,
param_env,
&mut orig_values,
self.final_state,
@@ -246,11 +240,12 @@ impl<'a, 'db> InspectCandidate<'a, 'db> {
&self,
source: GoalSource,
goal: Goal<'db, Predicate<'db>>,
+ span: Span,
) -> InspectGoal<'a, 'db> {
let infcx = self.goal.infcx;
match goal.predicate.kind().no_bound_vars() {
Some(PredicateKind::NormalizesTo(NormalizesTo { alias, term })) => {
- let unconstrained_term = infcx.next_term_var_of_kind(term);
+ let unconstrained_term = infcx.next_term_var_of_kind(term, span);
let goal =
goal.with(infcx.interner, NormalizesTo { alias, term: unconstrained_term });
// We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
@@ -265,8 +260,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> {
// considering the constrained RHS, and pass the resulting certainty to
// `InspectGoal::new` so that the goal has the right result (and maintains
// the impression that we don't do this normalizes-to infer hack at all).
- let (nested, proof_tree) =
- infcx.evaluate_root_goal_for_proof_tree(goal, Span::dummy());
+ let (nested, proof_tree) = infcx.evaluate_root_goal_for_proof_tree(goal, span);
let nested_goals_result = nested.and_then(|nested| {
normalizes_to_term_hack.constrain_and(
infcx,
@@ -300,7 +294,7 @@ impl<'a, 'db> InspectCandidate<'a, 'db> {
// constraints, we get an ICE if we already applied the constraints
// from the chosen candidate.
let proof_tree =
- infcx.probe(|_| infcx.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1);
+ infcx.probe(|_| infcx.evaluate_root_goal_for_proof_tree(goal, span).1);
InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
}
}
@@ -327,6 +321,10 @@ impl<'a, 'db> InspectGoal<'a, 'db> {
self.result
}
+ pub(crate) fn source(&self) -> GoalSource {
+ self.source
+ }
+
pub(crate) fn depth(&self) -> usize {
self.depth
}
@@ -346,7 +344,10 @@ impl<'a, 'db> InspectGoal<'a, 'db> {
inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => {
assert!(matches!(
shallow_certainty.replace(c),
- None | Some(Certainty::Maybe { cause: MaybeCause::Ambiguity, .. })
+ None | Some(Certainty::Maybe(MaybeInfo {
+ cause: MaybeCause::Ambiguity,
+ ..
+ }))
));
}
inspect::ProbeStep::NestedProbe(ref probe) => {
@@ -469,6 +470,8 @@ impl<'a, 'db> InspectGoal<'a, 'db> {
pub(crate) trait ProofTreeVisitor<'db> {
type Result: VisitorResult;
+ fn span(&self) -> Span;
+
fn config(&self) -> InspectConfig {
InspectConfig { max_depth: 10 }
}
@@ -496,7 +499,7 @@ impl<'db> InferCtxt<'db> {
visitor: &mut V,
) -> V::Result {
let (_, proof_tree) = <&SolverContext<'db>>::from(self)
- .evaluate_root_goal_for_proof_tree(goal, Span::dummy());
+ .evaluate_root_goal_for_proof_tree(goal, visitor.span());
visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc))
}
}
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs
index cfb55e2e00..b3d31dd40b 100644
--- a/crates/hir-ty/src/next_solver/interner.rs
+++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -4,49 +4,53 @@ use std::{fmt, ops::ControlFlow};
use intern::{Interned, InternedRef, InternedSliceRef, impl_internable};
use macros::GenericTypeVisitable;
+use rustc_abi::ReprOptions;
use rustc_ast_ir::{FloatTy, IntTy, UintTy};
pub use tls_cache::clear_tls_solver_cache;
pub use tls_db::{attach_db, attach_db_allow_change, with_attached_db};
use base_db::Crate;
use hir_def::{
- AdtId, CallableDefId, DefWithBodyId, EnumVariantId, ExpressionStoreOwnerId, HasModule,
- ItemContainerId, StructId, UnionId, VariantId,
+ AdtId, CallableDefId, EnumId, HasModule, ItemContainerId, StructId, TraitId, TypeAliasId,
+ UnionId, VariantId,
attrs::AttrFlags,
- expr_store::{Body, ExpressionStore},
+ expr_store::{ExpressionStore, StoreVisitor},
+ hir::{ClosureKind as HirClosureKind, CoroutineKind as HirCoroutineKind, ExprId, PatId},
lang_item::LangItems,
signatures::{
- EnumSignature, FieldData, FnFlags, FunctionSignature, ImplFlags, ImplSignature,
+ EnumFlags, EnumSignature, FnFlags, FunctionSignature, ImplFlags, ImplSignature,
StructFlags, StructSignature, TraitFlags, TraitSignature, UnionSignature,
},
};
-use la_arena::Idx;
-use rustc_abi::{ReprFlags, ReprOptions};
+use rustc_abi::ExternAbi;
use rustc_hash::FxHashSet;
use rustc_index::bit_set::DenseBitSet;
use rustc_type_ir::{
- AliasTermKind, AliasTy, AliasTyKind, BoundVar, CoroutineWitnessTypes, DebruijnIndex,
- EarlyBinder, FlagComputation, Flags, GenericArgKind, GenericTypeVisitable, ImplPolarity,
- InferTy, Interner, TraitRef, TypeFlags, TypeVisitableExt, Upcast, Variance,
+ AliasTy, BoundVar, CoroutineWitnessTypes, DebruijnIndex, EarlyBinder, FlagComputation, Flags,
+ FnSigKind, GenericArgKind, GenericTypeVisitable, ImplPolarity, InferTy, Interner, TraitRef,
+ TypeFlags, TypeVisitableExt, Upcast, Variance,
elaborate::elaborate,
error::TypeError,
fast_reject,
inherent::{self, Const as _, GenericsOf, IntoKind, SliceLike as _, Span as _, Ty as _},
- lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem},
+ lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem},
solve::{AdtDestructorKind, SizedTraitKind},
};
use crate::{
- FnAbi,
+ InferBodyId, Span,
db::{HirDatabase, InternedClosure, InternedCoroutineId},
lower::GenericPredicates,
method_resolution::TraitImpls,
next_solver::{
- AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
- Consts, CoroutineClosureIdWrapper, CoroutineIdWrapper, Ctor, FnSig, FxIndexMap,
- GeneralConstIdWrapper, LateParamRegion, OpaqueTypeKey, RegionAssumptions, ScalarInt,
- SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper,
- UnevaluatedConst,
+ AdtIdWrapper, AliasTermKind, AliasTyKind, AnyImplId, BoundConst, CallableIdWrapper,
+ CanonicalVarKind, ClosureIdWrapper, Consts, CoroutineClosureIdWrapper, CoroutineIdWrapper,
+ Ctor, FnSig, FreeConstAliasId, FreeTermAliasId, FreeTyAliasId, FxIndexMap,
+ GeneralConstIdWrapper, ImplOrTraitAssocConstId, ImplOrTraitAssocTermId,
+ ImplOrTraitAssocTyId, InherentAssocConstId, InherentAssocTermId, InherentAssocTyId,
+ LateParamRegion, OpaqueTyIdWrapper, OpaqueTypeKey, RegionAssumptions, ScalarInt,
+ SimplifiedType, SolverContext, SolverDefIds, TermId, TraitAssocConstId, TraitAssocTermId,
+ TraitAssocTyId, TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, Unnormalized,
util::{explicit_item_bounds, explicit_item_self_bounds},
},
};
@@ -382,13 +386,9 @@ impl<'db> DbInterner<'db> {
}
}
-// This is intentionally left as `()`
-#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
-pub struct Span(());
-
impl<'db> inherent::Span<DbInterner<'db>> for Span {
fn dummy() -> Self {
- Span(())
+ Span::Dummy
}
}
@@ -422,266 +422,167 @@ pub struct AllocId;
interned_slice!(VariancesOfStorage, VariancesOf, StoredVariancesOf, variances, Variance, Variance);
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct VariantIdx(usize);
-
-// FIXME: could/should store actual data?
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum VariantDef {
- Struct(StructId),
- Union(UnionId),
- Enum(EnumVariantId),
-}
-
-impl VariantDef {
- pub fn id(&self) -> VariantId {
- match self {
- VariantDef::Struct(struct_id) => VariantId::StructId(*struct_id),
- VariantDef::Union(union_id) => VariantId::UnionId(*union_id),
- VariantDef::Enum(enum_variant_id) => VariantId::EnumVariantId(*enum_variant_id),
- }
- }
-
- pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Idx<FieldData>, FieldData)> {
- let id: VariantId = match self {
- VariantDef::Struct(it) => (*it).into(),
- VariantDef::Union(it) => (*it).into(),
- VariantDef::Enum(it) => (*it).into(),
- };
- id.fields(db).fields().iter().map(|(id, data)| (id, data.clone())).collect()
+bitflags::bitflags! {
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+ struct AdtFlags: u8 {
+ const IS_FUNDAMENTAL = 1 << 0;
+ const IS_PACKED = 1 << 1;
+ const HAS_REPR = 1 << 2;
+ const IS_PHANTOM_DATA = 1 << 3;
+ const IS_MANUALLY_DROP = 1 << 4;
+ const IS_BOX = 1 << 5;
}
}
-/*
-/// Definition of a variant -- a struct's fields or an enum variant.
-#[derive(Debug, StableHash, TyEncodable, TyDecodable)]
-pub struct VariantDef {
- /// `DefId` that identifies the variant itself.
- /// If this variant belongs to a struct or union, then this is a copy of its `DefId`.
- pub def_id: DefId,
- /// `DefId` that identifies the variant's constructor.
- /// If this variant is a struct variant, then this is `None`.
- pub ctor: Option<(CtorKind, DefId)>,
- /// Variant or struct name, maybe empty for anonymous adt (struct or union).
- pub name: Symbol,
- /// Discriminant of this variant.
- pub discr: VariantDiscr,
- /// Fields of this variant.
- pub fields: IndexVec<FieldIdx, FieldDef>,
- /// The error guarantees from parser, if any.
- tainted: Option<ErrorGuaranteed>,
- /// Flags of the variant (e.g. is field list non-exhaustive)?
- flags: VariantFlags,
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+enum AdtDefInner {
+ Struct { id: StructId, flags: AdtFlags },
+ Union { id: UnionId, flags: AdtFlags },
+ Enum { id: EnumId, flags: AdtFlags },
}
-*/
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct AdtFlags {
- is_enum: bool,
- is_union: bool,
- is_struct: bool,
- is_phantom_data: bool,
- is_fundamental: bool,
- is_box: bool,
- is_manually_drop: bool,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct AdtDefInner {
- pub id: AdtId,
- variants: Vec<(VariantIdx, VariantDef)>,
- flags: AdtFlags,
- repr: ReprOptions,
-}
-
-// We're gonna cheat a little bit and implement `Hash` on only the `DefId` and
-// accept there might be collisions for def ids from different crates (or across
-// different tests, oh my).
-impl std::hash::Hash for AdtDefInner {
- #[inline]
- fn hash<H: std::hash::Hasher>(&self, s: &mut H) {
- self.id.hash(s)
- }
-}
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct AdtDef(AdtDefInner);
-#[salsa::interned(no_lifetime, constructor = new_)]
-pub struct AdtDef {
- #[returns(ref)]
- data_: AdtDefInner,
-}
+const _: () = assert!(size_of::<AdtDef>() == 12);
impl AdtDef {
pub fn new<'db>(def_id: AdtId, interner: DbInterner<'db>) -> Self {
let db = interner.db();
- let (flags, variants, repr) = match def_id {
- AdtId::StructId(struct_id) => {
- let data = StructSignature::of(db, struct_id);
-
- let flags = AdtFlags {
- is_enum: false,
- is_union: false,
- is_struct: true,
- is_phantom_data: data.flags.contains(StructFlags::IS_PHANTOM_DATA),
- is_fundamental: data.flags.contains(StructFlags::FUNDAMENTAL),
- is_box: data.flags.contains(StructFlags::IS_BOX),
- is_manually_drop: data.flags.contains(StructFlags::IS_MANUALLY_DROP),
- };
-
- let variants = vec![(VariantIdx(0), VariantDef::Struct(struct_id))];
-
- let data_repr = data.repr(db, struct_id);
- let mut repr_flags = ReprFlags::empty();
- if flags.is_box {
- repr_flags.insert(ReprFlags::IS_LINEAR);
+ let inner = match def_id {
+ AdtId::StructId(id) => {
+ let data = StructSignature::of(db, id);
+ let mut flags = AdtFlags::empty();
+ if data.flags.contains(StructFlags::FUNDAMENTAL) {
+ flags.insert(AdtFlags::IS_FUNDAMENTAL);
}
- if data_repr.is_some_and(|r| r.c()) {
- repr_flags.insert(ReprFlags::IS_C);
+ if data.flags.contains(StructFlags::IS_PHANTOM_DATA) {
+ flags.insert(AdtFlags::IS_PHANTOM_DATA);
}
- if data_repr.is_some_and(|r| r.simd()) {
- repr_flags.insert(ReprFlags::IS_SIMD);
+ if data.flags.contains(StructFlags::IS_MANUALLY_DROP) {
+ flags.insert(AdtFlags::IS_MANUALLY_DROP);
}
- let repr = ReprOptions {
- align: data_repr.and_then(|r| r.align),
- pack: data_repr.and_then(|r| r.pack),
- int: data_repr.and_then(|r| r.int),
- flags: repr_flags,
- ..ReprOptions::default()
- };
-
- (flags, variants, repr)
- }
- AdtId::UnionId(union_id) => {
- let flags = AdtFlags {
- is_enum: false,
- is_union: true,
- is_struct: false,
- is_phantom_data: false,
- is_fundamental: false,
- is_box: false,
- is_manually_drop: false,
- };
-
- let variants = vec![(VariantIdx(0), VariantDef::Union(union_id))];
-
- let data_repr = AttrFlags::repr(db, union_id.into());
- let mut repr_flags = ReprFlags::empty();
- if flags.is_box {
- repr_flags.insert(ReprFlags::IS_LINEAR);
+ if data.flags.contains(StructFlags::IS_BOX) {
+ flags.insert(AdtFlags::IS_BOX);
}
- if data_repr.is_some_and(|r| r.c()) {
- repr_flags.insert(ReprFlags::IS_C);
+ if data.flags.contains(StructFlags::HAS_REPR) {
+ flags.insert(AdtFlags::HAS_REPR);
+ if data.repr(db, id).is_some_and(|repr| repr.packed()) {
+ flags.insert(AdtFlags::IS_PACKED);
+ }
}
- if data_repr.is_some_and(|r| r.simd()) {
- repr_flags.insert(ReprFlags::IS_SIMD);
+ AdtDefInner::Struct { id, flags }
+ }
+ AdtId::UnionId(id) => {
+ let data = UnionSignature::of(db, id);
+ let mut flags = AdtFlags::empty();
+ if data.flags.contains(StructFlags::FUNDAMENTAL) {
+ flags.insert(AdtFlags::IS_FUNDAMENTAL);
}
- let repr = ReprOptions {
- align: data_repr.and_then(|r| r.align),
- pack: data_repr.and_then(|r| r.pack),
- int: data_repr.and_then(|r| r.int),
- flags: repr_flags,
- ..ReprOptions::default()
- };
-
- (flags, variants, repr)
- }
- AdtId::EnumId(enum_id) => {
- let flags = AdtFlags {
- is_enum: true,
- is_union: false,
- is_struct: false,
- is_phantom_data: false,
- is_fundamental: false,
- is_box: false,
- is_manually_drop: false,
- };
-
- let variants = enum_id
- .enum_variants(db)
- .variants
- .iter()
- .enumerate()
- .map(|(idx, v)| (VariantIdx(idx), v))
- .map(|(idx, v)| (idx, VariantDef::Enum(v.0)))
- .collect();
-
- let data_repr = AttrFlags::repr(db, enum_id.into());
-
- let mut repr_flags = ReprFlags::empty();
- if flags.is_box {
- repr_flags.insert(ReprFlags::IS_LINEAR);
+ if data.flags.contains(StructFlags::HAS_REPR) {
+ flags.insert(AdtFlags::HAS_REPR);
+ if data.repr(db, id).is_some_and(|repr| repr.packed()) {
+ flags.insert(AdtFlags::IS_PACKED);
+ }
}
- if data_repr.is_some_and(|r| r.c()) {
- repr_flags.insert(ReprFlags::IS_C);
+ AdtDefInner::Union { id, flags }
+ }
+ AdtId::EnumId(id) => {
+ let data = EnumSignature::of(db, id);
+ let mut flags = AdtFlags::empty();
+ if data.flags.contains(EnumFlags::FUNDAMENTAL) {
+ flags.insert(AdtFlags::IS_FUNDAMENTAL);
}
- if data_repr.is_some_and(|r| r.simd()) {
- repr_flags.insert(ReprFlags::IS_SIMD);
+ if data.flags.contains(EnumFlags::HAS_REPR) {
+ flags.insert(AdtFlags::HAS_REPR);
+ if data.repr(db, id).is_some_and(|repr| repr.packed()) {
+ flags.insert(AdtFlags::IS_PACKED);
+ }
}
-
- let repr = ReprOptions {
- align: data_repr.and_then(|r| r.align),
- pack: data_repr.and_then(|r| r.pack),
- int: data_repr.and_then(|r| r.int),
- flags: repr_flags,
- ..ReprOptions::default()
- };
-
- (flags, variants, repr)
+ AdtDefInner::Enum { id, flags }
}
};
+ AdtDef(inner)
+ }
- AdtDef::new_(db, AdtDefInner { id: def_id, variants, flags, repr })
+ #[inline]
+ pub fn def_id(self) -> AdtId {
+ match self.0 {
+ AdtDefInner::Struct { id, .. } => AdtId::StructId(id),
+ AdtDefInner::Union { id, .. } => AdtId::UnionId(id),
+ AdtDefInner::Enum { id, .. } => AdtId::EnumId(id),
+ }
}
- pub fn inner(&self) -> &AdtDefInner {
- crate::with_attached_db(|db| {
- let inner = self.data_(db);
- // SAFETY: ¯\_(ツ)_/¯
- unsafe { std::mem::transmute(inner) }
- })
+ #[inline]
+ fn flags(self) -> AdtFlags {
+ match self.0 {
+ AdtDefInner::Struct { flags, .. }
+ | AdtDefInner::Union { flags, .. }
+ | AdtDefInner::Enum { flags, .. } => flags,
+ }
}
- pub fn is_enum(&self) -> bool {
- self.inner().flags.is_enum
+ #[inline]
+ pub fn is_struct(self) -> bool {
+ matches!(self.0, AdtDefInner::Struct { .. })
}
- pub fn is_box(&self) -> bool {
- self.inner().flags.is_box
+ #[inline]
+ pub fn is_union(self) -> bool {
+ matches!(self.0, AdtDefInner::Union { .. })
+ }
+
+ #[inline]
+ pub fn is_enum(self) -> bool {
+ matches!(self.0, AdtDefInner::Enum { .. })
}
#[inline]
- pub fn repr(self) -> ReprOptions {
- self.inner().repr
+ pub fn is_box(self) -> bool {
+ matches!(self.0, AdtDefInner::Struct { flags, .. } if flags.contains(AdtFlags::IS_BOX))
}
- /// Asserts this is a struct or union and returns its unique variant.
- pub fn non_enum_variant(self) -> VariantDef {
- assert!(self.inner().flags.is_struct || self.inner().flags.is_union);
- self.inner().variants[0].1.clone()
+ #[inline]
+ pub fn repr(self, db: &dyn HirDatabase) -> ReprOptions {
+ if self.flags().contains(AdtFlags::HAS_REPR) {
+ AttrFlags::repr_assume_has(db, self.def_id()).unwrap_or_default()
+ } else {
+ ReprOptions::default()
+ }
}
}
impl<'db> inherent::AdtDef<DbInterner<'db>> for AdtDef {
fn def_id(self) -> AdtIdWrapper {
- self.inner().id.into()
+ self.def_id().into()
}
fn is_struct(self) -> bool {
- self.inner().flags.is_struct
+ self.is_struct()
}
fn is_phantom_data(self) -> bool {
- self.inner().flags.is_phantom_data
+ matches!(self.0, AdtDefInner::Struct { flags, .. } if flags.contains(AdtFlags::IS_PHANTOM_DATA))
+ }
+
+ fn is_manually_drop(self) -> bool {
+ matches!(self.0, AdtDefInner::Struct { flags, .. } if flags.contains(AdtFlags::IS_MANUALLY_DROP))
+ }
+
+ fn is_packed(self) -> bool {
+ self.flags().contains(AdtFlags::IS_PACKED)
}
fn is_fundamental(self) -> bool {
- self.inner().flags.is_fundamental
+ self.flags().contains(AdtFlags::IS_FUNDAMENTAL)
}
fn struct_tail_ty(
self,
interner: DbInterner<'db>,
) -> Option<EarlyBinder<DbInterner<'db>, Ty<'db>>> {
- let hir_def::AdtId::StructId(struct_id) = self.inner().id else {
+ let hir_def::AdtId::StructId(struct_id) = self.def_id() else {
return None;
};
let id: VariantId = struct_id.into();
@@ -700,7 +601,7 @@ impl<'db> inherent::AdtDef<DbInterner<'db>> for AdtDef {
db.field_types(id).iter().map(|(_, ty)| ty.get().skip_binder()).collect::<Vec<_>>()
};
let field_tys = |_id: VariantId| vec![];
- let tys: Vec<_> = match self.inner().id {
+ let tys: Vec<_> = match self.def_id() {
hir_def::AdtId::StructId(id) => field_tys(id.into()),
hir_def::AdtId::UnionId(id) => field_tys(id.into()),
hir_def::AdtId::EnumId(id) => id
@@ -726,15 +627,7 @@ impl<'db> inherent::AdtDef<DbInterner<'db>> for AdtDef {
}
fn destructor(self, interner: DbInterner<'db>) -> Option<AdtDestructorKind> {
- crate::drop::destructor(interner.db, self.def_id().0).map(|_| AdtDestructorKind::NotConst)
- }
-
- fn is_manually_drop(self) -> bool {
- self.inner().flags.is_manually_drop
- }
-
- fn is_packed(self) -> bool {
- self.repr().packed()
+ crate::drop::destructor(interner.db, self.def_id()).map(|_| AdtDestructorKind::NotConst)
}
fn field_representing_type_info(
@@ -749,17 +642,17 @@ impl<'db> inherent::AdtDef<DbInterner<'db>> for AdtDef {
impl fmt::Debug for AdtDef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- crate::with_attached_db(|db| match self.inner().id {
- AdtId::StructId(struct_id) => {
- let data = StructSignature::of(db, struct_id);
+ crate::with_attached_db(|db| match self.0 {
+ AdtDefInner::Struct { id, .. } => {
+ let data = StructSignature::of(db, id);
f.write_str(data.name.as_str())
}
- AdtId::UnionId(union_id) => {
- let data = UnionSignature::of(db, union_id);
+ AdtDefInner::Union { id, .. } => {
+ let data = UnionSignature::of(db, id);
f.write_str(data.name.as_str())
}
- AdtId::EnumId(enum_id) => {
- let data = EnumSignature::of(db, enum_id);
+ AdtDefInner::Enum { id, .. } => {
+ let data = EnumSignature::of(db, id);
f.write_str(data.name.as_str())
}
})
@@ -778,6 +671,10 @@ impl<'db> inherent::Features<DbInterner<'db>> for Features {
false
}
+ fn generic_const_args(self) -> bool {
+ false
+ }
+
fn feature_bound_holds_in_crate(self, _symbol: Symbol) -> bool {
false
}
@@ -949,11 +846,7 @@ impl_foldable_for_interned_slice!(PatList);
macro_rules! as_lang_item {
(
- $solver_enum:ident, $self:ident, $def_id:expr;
-
- ignore = {
- $( $ignore:ident ),* $(,)?
- }
+ $solver_enum:ident, $self:ident, $def_id:expr, $id_ty:ty;
$( $variant:ident ),* $(,)?
) => {{
@@ -962,11 +855,10 @@ macro_rules! as_lang_item {
if let Some(it) = None::<$solver_enum> {
match it {
$( $solver_enum::$variant => {} )*
- $( $solver_enum::$ignore => {} )*
}
}
match $def_id {
- $( def_id if lang_items.$variant.is_some_and(|it| it == def_id) => Some($solver_enum::$variant), )*
+ $( def_id if let Some(it) = lang_items.$variant && <$id_ty>::from(it) == def_id => Some($solver_enum::$variant), )*
_ => None
}
}};
@@ -976,17 +868,12 @@ macro_rules! is_lang_item {
(
$solver_enum:ident, $self:ident, $def_id:expr, $expected_variant:ident;
- ignore = {
- $( $ignore:ident ),* $(,)?
- }
-
$( $variant:ident ),* $(,)?
) => {{
let lang_items = $self.lang_items();
let def_id = $def_id;
match $expected_variant {
$( $solver_enum::$variant => lang_items.$variant.is_some_and(|it| it == def_id), )*
- $( $solver_enum::$ignore => false, )*
}
}};
}
@@ -1004,6 +891,20 @@ impl<'db> Interner for DbInterner<'db> {
type AdtId = AdtIdWrapper;
type ImplId = AnyImplId;
type UnevaluatedConstId = GeneralConstIdWrapper;
+ type TraitAssocTyId = TraitAssocTyId;
+ type TraitAssocConstId = TraitAssocConstId;
+ type TraitAssocTermId = TraitAssocTermId;
+ type OpaqueTyId = OpaqueTyIdWrapper;
+ type LocalOpaqueTyId = OpaqueTyIdWrapper;
+ type FreeTyAliasId = FreeTyAliasId;
+ type FreeConstAliasId = FreeConstAliasId;
+ type FreeTermAliasId = FreeTermAliasId;
+ type ImplOrTraitAssocTyId = ImplOrTraitAssocTyId;
+ type ImplOrTraitAssocConstId = ImplOrTraitAssocConstId;
+ type ImplOrTraitAssocTermId = ImplOrTraitAssocTermId;
+ type InherentAssocTyId = InherentAssocTyId;
+ type InherentAssocConstId = InherentAssocConstId;
+ type InherentAssocTermId = InherentAssocTermId;
type Span = Span;
type GenericArgs = GenericArgs<'db>;
@@ -1057,7 +958,6 @@ impl<'db> Interner for DbInterner<'db> {
type Pat = Pattern<'db>;
type PatList = PatList<'db>;
type Safety = Safety;
- type Abi = FnAbi;
type Const = Const<'db>;
type ParamConst = ParamConst;
@@ -1078,7 +978,7 @@ impl<'db> Interner for DbInterner<'db> {
type Clause = Clause<'db>;
type Clauses = Clauses<'db>;
- type GenericsOf = Generics;
+ type GenericsOf = Generics<'db>;
type VariancesOf = VariancesOf<'db>;
@@ -1187,7 +1087,9 @@ impl<'db> Interner for DbInterner<'db> {
//
// We currently always use the type from HIR typeck which ignores regions. This
// should be fine.
- SolverDefId::InternedOpaqueTyId(_) => self.type_of_opaque_hir_typeck(def_id),
+ SolverDefId::InternedOpaqueTyId(def_id) => {
+ self.type_of_opaque_hir_typeck(def_id.into())
+ }
SolverDefId::FunctionId(id) => self.db.value_ty(id.into()).unwrap(),
SolverDefId::Ctor(id) => {
let id = match id {
@@ -1204,43 +1106,45 @@ impl<'db> Interner for DbInterner<'db> {
AdtDef::new(def_id.0, self)
}
- fn alias_term_kind(
- self,
- alias: rustc_type_ir::AliasTerm<Self>,
- ) -> rustc_type_ir::AliasTermKind {
- match alias.def_id {
- SolverDefId::InternedOpaqueTyId(_) => AliasTermKind::OpaqueTy,
+ fn alias_term_kind_from_def_id(self, def_id: SolverDefId) -> AliasTermKind<'db> {
+ match def_id {
+ SolverDefId::InternedOpaqueTyId(def_id) => {
+ AliasTermKind::OpaqueTy { def_id: def_id.into() }
+ }
SolverDefId::TypeAliasId(type_alias) => match type_alias.loc(self.db).container {
ItemContainerId::ImplId(impl_)
if ImplSignature::of(self.db, impl_).target_trait.is_none() =>
{
- AliasTermKind::InherentTy
+ AliasTermKind::InherentTy { def_id: type_alias.into() }
}
ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => {
- AliasTermKind::ProjectionTy
+ AliasTermKind::ProjectionTy { def_id: type_alias.into() }
}
- _ => AliasTermKind::FreeTy,
+ _ => AliasTermKind::FreeTy { def_id: type_alias.into() },
},
// rustc creates an `AnonConst` for consts, and evaluates them with CTFE (normalizing projections
// via selection, similar to ours `find_matching_impl()`, and not with the trait solver), so mimic it.
- SolverDefId::ConstId(_) | SolverDefId::AnonConstId(_) => {
- AliasTermKind::UnevaluatedConst
+ SolverDefId::ConstId(def_id) => {
+ AliasTermKind::UnevaluatedConst { def_id: GeneralConstIdWrapper(def_id.into()) }
+ }
+ SolverDefId::AnonConstId(def_id) => {
+ AliasTermKind::UnevaluatedConst { def_id: GeneralConstIdWrapper(def_id.into()) }
}
- _ => unimplemented!("Unexpected alias: {:?}", alias.def_id),
+ _ => unimplemented!("Unexpected alias: {:?}", def_id),
}
}
fn trait_ref_and_own_args_for_alias(
self,
- def_id: Self::DefId,
+ def_id: Self::TraitAssocTermId,
args: Self::GenericArgs,
) -> (rustc_type_ir::TraitRef<Self>, Self::GenericArgsSlice) {
- let trait_def_id = self.parent(def_id);
- let trait_generics = self.generics_of(trait_def_id);
- let trait_args =
- GenericArgs::new_from_slice(&args.as_slice()[0..trait_generics.own_params.len()]);
- let alias_args = &args.as_slice()[trait_generics.own_params.len()..];
- (TraitRef::new_from_args(self, trait_def_id.try_into().unwrap(), trait_args), alias_args)
+ let trait_def_id = self.projection_parent(def_id).0;
+ let trait_generics = crate::generics::generics(self.db, trait_def_id.into());
+ let trait_generics_len = trait_generics.len();
+ let trait_args = GenericArgs::new_from_slice(&args.as_slice()[..trait_generics_len]);
+ let alias_args = &args.as_slice()[trait_generics_len..];
+ (TraitRef::new_from_args(self, trait_def_id.into(), trait_args), alias_args)
}
fn check_args_compatible(self, _def_id: Self::DefId, _args: Self::GenericArgs) -> bool {
@@ -1265,37 +1169,43 @@ impl<'db> Interner for DbInterner<'db> {
Tys::new_from_iter(self, args)
}
- fn parent(self, def_id: Self::DefId) -> Self::DefId {
- use hir_def::Lookup;
+ fn projection_parent(self, def_id: Self::TraitAssocTermId) -> Self::TraitId {
+ let container = match def_id.0 {
+ TermId::TypeAliasId(def_id) => def_id.loc(self.db).container,
+ TermId::ConstId(def_id) => def_id.loc(self.db).container,
+ };
+ let ItemContainerId::TraitId(trait_) = container else {
+ panic!("a TraitAssocTermId can only come from a trait")
+ };
+ trait_.into()
+ }
- let container = match def_id {
- SolverDefId::FunctionId(it) => it.lookup(self.db()).container,
- SolverDefId::TypeAliasId(it) => it.lookup(self.db()).container,
- SolverDefId::ConstId(it) => it.lookup(self.db()).container,
- SolverDefId::InternedClosureId(it) => {
- return it.loc(self.db).0.generic_def(self.db()).into();
- }
- SolverDefId::InternedCoroutineId(it) => {
- return it.loc(self.db).0.generic_def(self.db()).into();
- }
- SolverDefId::InternedCoroutineClosureId(it) => {
- return it.loc(self.db).0.generic_def(self.db()).into();
- }
- SolverDefId::StaticId(_)
- | SolverDefId::AdtId(_)
- | SolverDefId::TraitId(_)
- | SolverDefId::ImplId(_)
- | SolverDefId::BuiltinDeriveImplId(_)
- | SolverDefId::EnumVariantId(..)
- | SolverDefId::Ctor(..)
- | SolverDefId::InternedOpaqueTyId(..)
- | SolverDefId::AnonConstId(_) => panic!(),
+ fn impl_or_trait_assoc_term_parent(self, def_id: Self::ImplOrTraitAssocTermId) -> Self::DefId {
+ let container = match def_id.0 {
+ TermId::TypeAliasId(def_id) => def_id.loc(self.db).container,
+ TermId::ConstId(def_id) => def_id.loc(self.db).container,
};
+ match container {
+ ItemContainerId::ImplId(impl_) => impl_.into(),
+ ItemContainerId::TraitId(trait_) => trait_.into(),
+ ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => {
+ panic!("only impl or trait can be the parent of ImplOrTraitAssocTermId")
+ }
+ }
+ }
+ fn inherent_alias_term_parent(self, def_id: Self::InherentAssocTermId) -> Self::ImplId {
+ let container = match def_id.0 {
+ TermId::TypeAliasId(def_id) => def_id.loc(self.db).container,
+ TermId::ConstId(def_id) => def_id.loc(self.db).container,
+ };
match container {
- ItemContainerId::ImplId(it) => it.into(),
- ItemContainerId::TraitId(it) => it.into(),
- ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => panic!(),
+ ItemContainerId::ImplId(impl_) => impl_.into(),
+ ItemContainerId::ExternBlockId(_)
+ | ItemContainerId::ModuleId(_)
+ | ItemContainerId::TraitId(_) => {
+ panic!("only impl can be the parent of InherentAliasTermId")
+ }
}
}
@@ -1303,6 +1213,10 @@ impl<'db> Interner for DbInterner<'db> {
50
}
+ fn is_type_const(self, _def_id: Self::DefId) -> bool {
+ false
+ }
+
fn features(self) -> Features {
Features
}
@@ -1315,28 +1229,35 @@ impl<'db> Interner for DbInterner<'db> {
}
fn coroutine_movability(self, def_id: Self::CoroutineId) -> rustc_ast_ir::Movability {
- // FIXME: Make this a query? I don't believe this can be accessed from bodies other than
- // the current infer query, except with revealed opaques - is it rare enough to not matter?
- let InternedClosure(owner, expr_id) = def_id.0.loc(self.db);
- let store = ExpressionStore::of(self.db, owner);
- let expr = &store[expr_id];
- match *expr {
- hir_def::hir::Expr::Closure { closure_kind, .. } => match closure_kind {
- hir_def::hir::ClosureKind::Coroutine(movability) => match movability {
- hir_def::hir::Movability::Static => rustc_ast_ir::Movability::Static,
- hir_def::hir::Movability::Movable => rustc_ast_ir::Movability::Movable,
- },
- hir_def::hir::ClosureKind::AsyncBlock { .. } => rustc_ast_ir::Movability::Static,
- _ => panic!("unexpected expression for a coroutine: {expr:?}"),
+ match def_id.0.loc(self.db).kind {
+ hir_def::hir::ClosureKind::OldCoroutine(movability) => match movability {
+ hir_def::hir::Movability::Static => rustc_ast_ir::Movability::Static,
+ hir_def::hir::Movability::Movable => rustc_ast_ir::Movability::Movable,
},
- _ => panic!("unexpected expression for a coroutine: {expr:?}"),
+ hir_def::hir::ClosureKind::Coroutine { .. } => rustc_ast_ir::Movability::Static,
+ kind => panic!("unexpected kind for a coroutine: {kind:?}"),
}
}
fn coroutine_for_closure(self, def_id: Self::CoroutineClosureId) -> Self::CoroutineId {
- let InternedClosure(owner, coroutine_closure_expr) = def_id.0.loc(self.db);
+ let InternedClosure { owner, expr: coroutine_closure_expr, kind: coroutine_closure_kind } =
+ def_id.0.loc(self.db);
+ let coroutine_closure_kind = match coroutine_closure_kind {
+ HirClosureKind::CoroutineClosure(it) => it,
+ _ => {
+ panic!("invalid kind closure kind {coroutine_closure_kind:?} for coroutine closure")
+ }
+ };
let coroutine_expr = ExpressionStore::coroutine_for_closure(coroutine_closure_expr);
- InternedCoroutineId::new(self.db, InternedClosure(owner, coroutine_expr)).into()
+ let coroutine_kind = hir_def::hir::ClosureKind::Coroutine {
+ kind: coroutine_closure_kind,
+ source: hir_def::hir::CoroutineSource::Closure,
+ };
+ InternedCoroutineId::new(
+ self.db,
+ InternedClosure { owner, expr: coroutine_expr, kind: coroutine_kind },
+ )
+ .into()
}
fn generics_require_sized_self(self, def_id: Self::DefId) -> bool {
@@ -1348,22 +1269,24 @@ impl<'db> Interner for DbInterner<'db> {
// Search for a predicate like `Self : Sized` amongst the trait bounds.
let predicates = self.predicates_of(def_id);
- elaborate(self, predicates.iter_identity()).any(|pred| match pred.kind().skip_binder() {
- ClauseKind::Trait(ref trait_pred) => {
- trait_pred.def_id() == sized_def_id
- && matches!(
- trait_pred.self_ty().kind(),
- TyKind::Param(ParamTy { index: 0, .. })
- )
+ elaborate(self, predicates.iter_identity().map(Unnormalized::skip_norm_wip)).any(|pred| {
+ match pred.kind().skip_binder() {
+ ClauseKind::Trait(ref trait_pred) => {
+ trait_pred.def_id() == sized_def_id
+ && matches!(
+ trait_pred.self_ty().kind(),
+ TyKind::Param(ParamTy { index: 0, .. })
+ )
+ }
+ ClauseKind::RegionOutlives(_)
+ | ClauseKind::TypeOutlives(_)
+ | ClauseKind::Projection(_)
+ | ClauseKind::ConstArgHasType(_, _)
+ | ClauseKind::WellFormed(_)
+ | ClauseKind::ConstEvaluatable(_)
+ | ClauseKind::HostEffect(..)
+ | ClauseKind::UnstableFeature(_) => false,
}
- ClauseKind::RegionOutlives(_)
- | ClauseKind::TypeOutlives(_)
- | ClauseKind::Projection(_)
- | ClauseKind::ConstArgHasType(_, _)
- | ClauseKind::WellFormed(_)
- | ClauseKind::ConstEvaluatable(_)
- | ClauseKind::HostEffect(..)
- | ClauseKind::UnstableFeature(_) => false,
})
}
@@ -1485,22 +1408,22 @@ impl<'db> Interner for DbInterner<'db> {
false
}
- fn require_lang_item(self, lang_item: SolverLangItem) -> Self::DefId {
+ fn require_projection_lang_item(
+ self,
+ lang_item: SolverProjectionLangItem,
+ ) -> Self::TraitAssocTyId {
let lang_items = self.lang_items();
let lang_item = match lang_item {
- SolverLangItem::AsyncFnKindUpvars => lang_items.AsyncFnKindUpvars,
- SolverLangItem::AsyncFnOnceOutput => lang_items.AsyncFnOnceOutput,
- SolverLangItem::CallOnceFuture => lang_items.CallOnceFuture,
- SolverLangItem::CallRefFuture => lang_items.CallRefFuture,
- SolverLangItem::CoroutineReturn => lang_items.CoroutineReturn,
- SolverLangItem::CoroutineYield => lang_items.CoroutineYield,
- SolverLangItem::FutureOutput => lang_items.FutureOutput,
- SolverLangItem::Metadata => lang_items.Metadata,
- SolverLangItem::DynMetadata => {
- return lang_items.DynMetadata.expect("Lang item required but not found.").into();
- }
- SolverLangItem::FieldBase => lang_items.FieldBase,
- SolverLangItem::FieldType => lang_items.FieldType,
+ SolverProjectionLangItem::AsyncFnKindUpvars => lang_items.AsyncFnKindUpvars,
+ SolverProjectionLangItem::AsyncFnOnceOutput => lang_items.AsyncFnOnceOutput,
+ SolverProjectionLangItem::CallOnceFuture => lang_items.CallOnceFuture,
+ SolverProjectionLangItem::CallRefFuture => lang_items.CallRefFuture,
+ SolverProjectionLangItem::CoroutineReturn => lang_items.CoroutineReturn,
+ SolverProjectionLangItem::CoroutineYield => lang_items.CoroutineYield,
+ SolverProjectionLangItem::FutureOutput => lang_items.FutureOutput,
+ SolverProjectionLangItem::Metadata => lang_items.Metadata,
+ SolverProjectionLangItem::FieldBase => lang_items.FieldBase,
+ SolverProjectionLangItem::FieldType => lang_items.FieldType,
};
lang_item.expect("Lang item required but not found.").into()
}
@@ -1512,9 +1435,6 @@ impl<'db> Interner for DbInterner<'db> {
SolverTraitLangItem::AsyncFnKindHelper => lang_items.AsyncFnKindHelper,
SolverTraitLangItem::AsyncFnMut => lang_items.AsyncFnMut,
SolverTraitLangItem::AsyncFnOnce => lang_items.AsyncFnOnce,
- SolverTraitLangItem::AsyncFnOnceOutput => unimplemented!(
- "This is incorrectly marked as `SolverTraitLangItem`, and is not used by the solver."
- ),
SolverTraitLangItem::AsyncIterator => lang_items.AsyncIterator,
SolverTraitLangItem::Clone => lang_items.Clone,
SolverTraitLangItem::Copy => lang_items.Copy,
@@ -1547,14 +1467,19 @@ impl<'db> Interner for DbInterner<'db> {
fn require_adt_lang_item(self, lang_item: SolverAdtLangItem) -> AdtIdWrapper {
let lang_items = self.lang_items();
let lang_item = match lang_item {
- SolverAdtLangItem::Option => lang_items.Option,
- SolverAdtLangItem::Poll => lang_items.Poll,
+ SolverAdtLangItem::Option => lang_items.Option.map(Into::into),
+ SolverAdtLangItem::Poll => lang_items.Poll.map(Into::into),
+ SolverAdtLangItem::DynMetadata => lang_items.DynMetadata.map(Into::into),
};
- AdtIdWrapper(lang_item.expect("Lang item required but not found.").into())
+ AdtIdWrapper(lang_item.expect("Lang item required but not found."))
}
- fn is_lang_item(self, def_id: Self::DefId, lang_item: SolverLangItem) -> bool {
- self.as_lang_item(def_id)
+ fn is_projection_lang_item(
+ self,
+ def_id: Self::TraitAssocTyId,
+ lang_item: SolverProjectionLangItem,
+ ) -> bool {
+ self.as_projection_lang_item(def_id)
.map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item))
}
@@ -1562,15 +1487,6 @@ impl<'db> Interner for DbInterner<'db> {
is_lang_item!(
SolverTraitLangItem, self, def_id.0, lang_item;
- ignore = {
- AsyncFnKindHelper,
- AsyncIterator,
- BikeshedGuaranteedNoDrop,
- FusedIterator,
- Field,
- AsyncFnOnceOutput, // This is incorrectly marked as `SolverTraitLangItem`, and is not used by the solver.
- }
-
Sized,
MetaSized,
PointeeSized,
@@ -1595,6 +1511,11 @@ impl<'db> Interner for DbInterner<'db> {
AsyncFnMut,
AsyncFnOnce,
TrivialClone,
+ AsyncFnKindHelper,
+ AsyncIterator,
+ BikeshedGuaranteedNoDrop,
+ FusedIterator,
+ Field,
)
}
@@ -1604,64 +1525,29 @@ impl<'db> Interner for DbInterner<'db> {
.map_or(false, |l| std::mem::discriminant(&l) == std::mem::discriminant(&lang_item))
}
- fn as_lang_item(self, def_id: Self::DefId) -> Option<SolverLangItem> {
- match def_id {
- SolverDefId::TypeAliasId(id) => {
- as_lang_item!(
- SolverLangItem, self, id;
-
- ignore = {
- AsyncFnKindUpvars,
- DynMetadata,
- FieldBase,
- FieldType,
- }
-
- Metadata,
- CoroutineReturn,
- CoroutineYield,
- FutureOutput,
- CallRefFuture,
- CallOnceFuture,
- AsyncFnOnceOutput,
- )
- }
- SolverDefId::AdtId(AdtId::StructId(id)) => {
- as_lang_item!(
- SolverLangItem, self, id;
-
- ignore = {
- AsyncFnKindUpvars,
- Metadata,
- CoroutineReturn,
- CoroutineYield,
- FutureOutput,
- CallRefFuture,
- CallOnceFuture,
- AsyncFnOnceOutput,
- FieldBase,
- FieldType,
- }
-
- DynMetadata,
- )
- }
- _ => panic!("Unexpected SolverDefId in as_lang_item"),
- }
+ fn as_projection_lang_item(
+ self,
+ def_id: Self::TraitAssocTyId,
+ ) -> Option<SolverProjectionLangItem> {
+ as_lang_item!(
+ SolverProjectionLangItem, self, def_id.0, TypeAliasId;
+
+ Metadata,
+ CoroutineReturn,
+ CoroutineYield,
+ FutureOutput,
+ CallRefFuture,
+ CallOnceFuture,
+ AsyncFnOnceOutput,
+ AsyncFnKindUpvars,
+ FieldBase,
+ FieldType,
+ )
}
fn as_trait_lang_item(self, def_id: Self::TraitId) -> Option<SolverTraitLangItem> {
as_lang_item!(
- SolverTraitLangItem, self, def_id.0;
-
- ignore = {
- AsyncFnKindHelper,
- AsyncIterator,
- BikeshedGuaranteedNoDrop,
- FusedIterator,
- Field,
- AsyncFnOnceOutput, // This is incorrectly marked as `SolverTraitLangItem`, and is not used by the solver.
- }
+ SolverTraitLangItem, self, def_id.0, TraitId;
Sized,
MetaSized,
@@ -1687,20 +1573,21 @@ impl<'db> Interner for DbInterner<'db> {
AsyncFnMut,
AsyncFnOnce,
TrivialClone,
+ AsyncFnKindHelper,
+ AsyncIterator,
+ BikeshedGuaranteedNoDrop,
+ FusedIterator,
+ Field,
)
}
fn as_adt_lang_item(self, def_id: Self::AdtId) -> Option<SolverAdtLangItem> {
- let AdtId::EnumId(def_id) = def_id.0 else {
- panic!("Unexpected SolverDefId in as_adt_lang_item");
- };
as_lang_item!(
- SolverAdtLangItem, self, def_id;
-
- ignore = {}
+ SolverAdtLangItem, self, def_id.0, AdtId;
Option,
Poll,
+ DynMetadata,
)
}
@@ -1869,7 +1756,7 @@ impl<'db> Interner for DbInterner<'db> {
});
}
- fn has_item_definition(self, _def_id: Self::DefId) -> bool {
+ fn has_item_definition(self, _def_id: Self::ImplOrTraitAssocTermId) -> bool {
// FIXME(next-solver): should check if the associated item has a value.
true
}
@@ -1940,41 +1827,28 @@ impl<'db> Interner for DbInterner<'db> {
}
fn is_general_coroutine(self, def_id: Self::CoroutineId) -> bool {
- // FIXME: Make this a query? I don't believe this can be accessed from bodies other than
- // the current infer query, except with revealed opaques - is it rare enough to not matter?
- let InternedClosure(owner, expr_id) = def_id.0.loc(self.db);
- let store = ExpressionStore::of(self.db, owner);
- matches!(
- store[expr_id],
- hir_def::hir::Expr::Closure {
- closure_kind: hir_def::hir::ClosureKind::Coroutine(_),
- ..
- }
- )
+ matches!(def_id.0.loc(self.db).kind, HirClosureKind::OldCoroutine(_))
}
fn coroutine_is_async(self, def_id: Self::CoroutineId) -> bool {
- // FIXME: Make this a query? I don't believe this can be accessed from bodies other than
- // the current infer query, except with revealed opaques - is it rare enough to not matter?
- let InternedClosure(owner, expr_id) = def_id.0.loc(self.db);
- let store = ExpressionStore::of(self.db, owner);
matches!(
- store[expr_id],
- hir_def::hir::Expr::Closure {
- closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. },
- ..
- }
+ def_id.0.loc(self.db).kind,
+ HirClosureKind::Coroutine { kind: HirCoroutineKind::Async, .. }
)
}
- fn coroutine_is_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool {
- // We don't handle gen coroutines yet.
- false
+ fn coroutine_is_gen(self, def_id: Self::CoroutineId) -> bool {
+ matches!(
+ def_id.0.loc(self.db).kind,
+ HirClosureKind::Coroutine { kind: HirCoroutineKind::Gen, .. }
+ )
}
- fn coroutine_is_async_gen(self, _coroutine_def_id: Self::CoroutineId) -> bool {
- // We don't handle gen coroutines yet.
- false
+ fn coroutine_is_async_gen(self, def_id: Self::CoroutineId) -> bool {
+ matches!(
+ def_id.0.loc(self.db).kind,
+ HirClosureKind::Coroutine { kind: HirCoroutineKind::AsyncGen, .. }
+ )
}
fn unsizing_params_for_adt(self, id: Self::AdtId) -> Self::UnsizingParams {
@@ -1994,16 +1868,21 @@ impl<'db> Interner for DbInterner<'db> {
};
// The last field of the structure has to exist and contain type/const parameters.
- let variant = def.non_enum_variant();
+ let variant = match def.def_id() {
+ AdtId::StructId(id) => VariantId::from(id),
+ AdtId::UnionId(id) => id.into(),
+ AdtId::EnumId(_) => panic!("expected a struct or a union"),
+ };
let fields = variant.fields(self.db());
- let Some((tail_field, prefix_fields)) = fields.split_last() else {
+ let mut prefix_fields = fields.fields().iter();
+ let Some(tail_field) = prefix_fields.next_back() else {
return UnsizingParams(DenseBitSet::new_empty(num_params));
};
- let field_types = self.db().field_types(variant.id());
+ let field_types = self.db().field_types(variant);
let mut unsizing_params = DenseBitSet::new_empty(num_params);
let ty = field_types[tail_field.0].get();
- for arg in ty.instantiate_identity().walk() {
+ for arg in ty.instantiate_identity().skip_norm_wip().walk() {
if let Some(i) = maybe_unsizing_param_idx(arg) {
unsizing_params.insert(i);
}
@@ -2012,7 +1891,7 @@ impl<'db> Interner for DbInterner<'db> {
// Ensure none of the other fields mention the parameters used
// in unsizing.
for field in prefix_fields {
- for arg in field_types[field.0].get().instantiate_identity().walk() {
+ for arg in field_types[field.0].get().instantiate_identity().skip_norm_wip().walk() {
if let Some(i) = maybe_unsizing_param_idx(arg) {
unsizing_params.remove(i);
}
@@ -2066,7 +1945,7 @@ impl<'db> Interner for DbInterner<'db> {
}
fn opaque_types_defined_by(self, def_id: Self::LocalDefId) -> Self::LocalDefIds {
- let Ok(def_id) = DefWithBodyId::try_from(def_id) else {
+ let Ok(def_id) = InferBodyId::try_from(def_id) else {
return SolverDefIds::default();
};
let mut result = Vec::new();
@@ -2075,33 +1954,54 @@ impl<'db> Interner for DbInterner<'db> {
}
fn opaque_types_and_coroutines_defined_by(self, def_id: Self::LocalDefId) -> Self::LocalDefIds {
- let Ok(def_id) = DefWithBodyId::try_from(def_id) else {
+ let db = self.db;
+
+ let Ok(def_id) = InferBodyId::try_from(def_id) else {
return SolverDefIds::default();
};
let mut result = Vec::new();
- crate::opaques::opaque_types_defined_by(self.db, def_id, &mut result);
+ crate::opaques::opaque_types_defined_by(db, def_id, &mut result);
// Collect coroutines.
- let body = Body::of(self.db, def_id);
- body.exprs().for_each(|(expr_id, expr)| {
- if matches!(
- expr,
- hir_def::hir::Expr::Closure {
- closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. }
- | hir_def::hir::ClosureKind::Coroutine(_),
+ let (store, root_expr) = def_id.store_and_root_expr(db);
+ // We can't just visit all exprs, since this may end up in unrelated anon consts.
+ CoroutinesVisitor { db: self.db, owner: def_id, store, coroutines: &mut result }
+ .on_expr(root_expr);
+
+ return SolverDefIds::new_from_slice(&result);
+
+ struct CoroutinesVisitor<'a> {
+ db: &'a dyn HirDatabase,
+ owner: InferBodyId,
+ store: &'a ExpressionStore,
+ coroutines: &'a mut Vec<SolverDefId>,
+ }
+
+ impl StoreVisitor for CoroutinesVisitor<'_> {
+ fn on_expr(&mut self, expr: ExprId) {
+ if let hir_def::hir::Expr::Closure {
+ closure_kind:
+ kind @ (hir_def::hir::ClosureKind::Coroutine { .. }
+ | hir_def::hir::ClosureKind::OldCoroutine(_)),
..
+ } = self.store[expr]
+ {
+ let coroutine = InternedCoroutineId::new(
+ self.db,
+ InternedClosure { owner: self.owner, expr, kind },
+ );
+ self.coroutines.push(coroutine.into());
}
- ) {
- let coroutine = InternedCoroutineId::new(
- self.db,
- InternedClosure(ExpressionStoreOwnerId::Body(def_id), expr_id),
- );
- result.push(coroutine.into());
- }
- });
- SolverDefIds::new_from_slice(&result)
+ self.store.visit_expr_children(expr, self);
+ }
+ fn on_pat(&mut self, pat: PatId) {
+ self.store.visit_pat_children(pat, self);
+ }
+ // Do not visit anon consts, they're separate bodies.
+ fn on_anon_const_expr(&mut self, _expr: ExprId) {}
+ }
}
fn alias_has_const_conditions(self, _def_id: Self::DefId) -> bool {
@@ -2134,26 +2034,23 @@ impl<'db> Interner for DbInterner<'db> {
fn opt_alias_variances(
self,
- _kind: impl Into<rustc_type_ir::AliasTermKind>,
- _def_id: Self::DefId,
+ _kind: impl Into<AliasTermKind<'db>>,
) -> Option<Self::VariancesOf> {
None
}
- fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) -> EarlyBinder<Self, Self::Ty> {
- match def_id {
- SolverDefId::InternedOpaqueTyId(opaque) => {
- let impl_trait_id = self.db().lookup_intern_impl_trait_id(opaque);
- match impl_trait_id {
- crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
- crate::opaques::rpit_hidden_types(self.db, func)[idx].get()
- }
- crate::ImplTraitId::TypeAliasImplTrait(type_alias, idx) => {
- crate::opaques::tait_hidden_types(self.db, type_alias)[idx].get()
- }
- }
+ fn type_of_opaque_hir_typeck(
+ self,
+ opaque: Self::LocalOpaqueTyId,
+ ) -> EarlyBinder<Self, Self::Ty> {
+ let impl_trait_id = opaque.0.loc(self.db);
+ match impl_trait_id {
+ crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
+ crate::opaques::rpit_hidden_types(self.db, func)[idx].get()
+ }
+ crate::ImplTraitId::TypeAliasImplTrait(type_alias, idx) => {
+ crate::opaques::tait_hidden_types(self.db, type_alias)[idx].get()
}
- _ => panic!("Unexpected SolverDefId in type_of_opaque_hir_typeck"),
}
}
@@ -2240,16 +2137,20 @@ impl<'db> Interner for DbInterner<'db> {
rustc_type_ir::AnonConstKind::GCE
}
- fn alias_ty_kind_from_def_id(self, def_id: Self::DefId) -> AliasTyKind<DbInterner<'db>> {
+ fn alias_ty_kind_from_def_id(self, def_id: Self::DefId) -> AliasTyKind<'db> {
match def_id {
SolverDefId::TypeAliasId(type_alias) => match type_alias.loc(self.db).container {
ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => {
- AliasTyKind::Free { def_id }
+ AliasTyKind::Free { def_id: type_alias.into() }
+ }
+ ItemContainerId::ImplId(_) => AliasTyKind::Inherent { def_id: type_alias.into() },
+ ItemContainerId::TraitId(_) => {
+ AliasTyKind::Projection { def_id: type_alias.into() }
}
- ItemContainerId::ImplId(_) => AliasTyKind::Inherent { def_id },
- ItemContainerId::TraitId(_) => AliasTyKind::Projection { def_id },
},
- SolverDefId::InternedOpaqueTyId(_) => AliasTyKind::Opaque { def_id },
+ SolverDefId::InternedOpaqueTyId(def_id) => {
+ AliasTyKind::Opaque { def_id: def_id.into() }
+ }
_ => unreachable!(),
}
}
@@ -2339,7 +2240,7 @@ impl<'db> DbInterner<'db> {
output: Ty<'db>,
c_variadic: bool,
safety: Safety,
- abi: FnAbi,
+ abi: ExternAbi,
) -> FnSig<'db>
where
I: IntoIterator<Item = Ty<'db>>,
@@ -2349,9 +2250,7 @@ impl<'db> DbInterner<'db> {
self,
inputs.into_iter().chain(std::iter::once(output)),
),
- c_variadic,
- safety,
- abi,
+ fn_sig_kind: FnSigKind::new(abi, safety, c_variadic),
}
}
@@ -2360,23 +2259,22 @@ impl<'db> DbInterner<'db> {
where
I: IntoIterator<Item = Ty<'db>>,
{
- FnSig {
- inputs_and_output: Tys::new_from_iter(
- self,
- inputs.into_iter().chain(std::iter::once(output)),
- ),
- c_variadic: false,
- safety: Safety::Safe,
- abi: FnAbi::Rust,
- }
+ self.mk_fn_sig(inputs, output, false, Safety::Safe, ExternAbi::Rust)
}
}
fn predicates_of(db: &dyn HirDatabase, def_id: SolverDefId) -> &GenericPredicates {
- if let SolverDefId::BuiltinDeriveImplId(impl_) = def_id {
- crate::builtin_derive::predicates(db, impl_)
- } else {
- GenericPredicates::query(db, def_id.try_into().unwrap())
+ match def_id {
+ SolverDefId::BuiltinDeriveImplId(impl_) => crate::builtin_derive::predicates(db, impl_),
+ SolverDefId::AnonConstId(anon_const) => {
+ let loc = anon_const.loc(db);
+ if loc.allow_using_generic_params {
+ GenericPredicates::query(db, loc.owner.generic_def(db))
+ } else {
+ GenericPredicates::empty()
+ }
+ }
+ _ => GenericPredicates::query(db, def_id.try_into().unwrap()),
}
}
@@ -2428,10 +2326,22 @@ TrivialTypeTraversalImpls! {
CoroutineIdWrapper,
CoroutineClosureIdWrapper,
AdtIdWrapper,
+ TraitAssocTyId,
+ TraitAssocConstId,
+ TraitAssocTermId,
+ ImplOrTraitAssocTyId,
+ ImplOrTraitAssocConstId,
+ ImplOrTraitAssocTermId,
+ FreeTyAliasId,
+ FreeConstAliasId,
+ FreeTermAliasId,
+ InherentAssocTyId,
+ InherentAssocConstId,
+ InherentAssocTermId,
+ OpaqueTyIdWrapper,
AnyImplId,
GeneralConstIdWrapper,
Safety,
- FnAbi,
Span,
ParamConst,
ParamTy,
diff --git a/crates/hir-ty/src/next_solver/ir_print.rs b/crates/hir-ty/src/next_solver/ir_print.rs
index 5dd372a367..925640e1f6 100644
--- a/crates/hir-ty/src/next_solver/ir_print.rs
+++ b/crates/hir-ty/src/next_solver/ir_print.rs
@@ -3,6 +3,8 @@
use hir_def::signatures::{TraitSignature, TypeAliasSignature};
use rustc_type_ir::{self as ty, ir_print::IrPrint};
+use crate::next_solver::TermId;
+
use super::SolverDefId;
use super::interner::DbInterner;
@@ -32,7 +34,7 @@ impl<'db> IrPrint<ty::AliasTerm<Self>> for DbInterner<'db> {
}
fn print_debug(t: &ty::AliasTerm<Self>, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- crate::with_attached_db(|db| match t.def_id {
+ crate::with_attached_db(|db| match t.def_id() {
SolverDefId::TypeAliasId(id) => fmt.write_str(&format!(
"AliasTerm({:?}[{:?}])",
TypeAliasSignature::of(db, id).name.as_str(),
@@ -141,8 +143,8 @@ impl<'db> IrPrint<ty::ExistentialProjection<Self>> for DbInterner<'db> {
fmt: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
crate::with_attached_db(|db| {
- let id = match t.def_id {
- SolverDefId::TypeAliasId(id) => id,
+ let id = match t.def_id.0 {
+ TermId::TypeAliasId(id) => id,
_ => panic!("Expected trait."),
};
fmt.write_str(&format!(
@@ -167,7 +169,7 @@ impl<'db> IrPrint<ty::ProjectionPredicate<Self>> for DbInterner<'db> {
fmt: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
crate::with_attached_db(|db| {
- let id = match t.projection_term.def_id {
+ let id = match t.projection_term.def_id() {
SolverDefId::TypeAliasId(id) => id,
_ => panic!("Expected trait."),
};
diff --git a/crates/hir-ty/src/next_solver/normalize.rs b/crates/hir-ty/src/next_solver/normalize.rs
index 5d8f3fe519..152b58baeb 100644
--- a/crates/hir-ty/src/next_solver/normalize.rs
+++ b/crates/hir-ty/src/next_solver/normalize.rs
@@ -99,10 +99,10 @@ impl<'db> NormalizationFolder<'_, 'db> {
self.depth += 1;
- let infer_term = infcx.next_term_var_of_kind(alias_term);
+ let infer_term = infcx.next_term_var_of_kind(alias_term, self.at.cause.span());
let obligation = Obligation::new(
interner,
- self.at.cause.clone(),
+ *self.at.cause,
self.at.param_env,
PredicateKind::AliasRelate(alias_term, infer_term, AliasRelationDirection::Equate),
);
@@ -229,7 +229,6 @@ impl<'db> FallibleTypeFolder<DbInterner<'db>> for NormalizationFolder<'_, 'db> {
}
// Deeply normalize a value and return it
-#[expect(dead_code, reason = "rustc has this")]
pub(crate) fn deeply_normalize_for_diagnostics<'db, T: TypeFoldable<DbInterner<'db>>>(
infcx: &InferCtxt<'db>,
param_env: ParamEnv<'db>,
diff --git a/crates/hir-ty/src/next_solver/predicate.rs b/crates/hir-ty/src/next_solver/predicate.rs
index 8658d03a9e..30738dea5c 100644
--- a/crates/hir-ty/src/next_solver/predicate.rs
+++ b/crates/hir-ty/src/next_solver/predicate.rs
@@ -31,6 +31,7 @@ pub type ExistentialPredicate<'db> = ty::ExistentialPredicate<DbInterner<'db>>;
pub type ExistentialTraitRef<'db> = ty::ExistentialTraitRef<DbInterner<'db>>;
pub type ExistentialProjection<'db> = ty::ExistentialProjection<DbInterner<'db>>;
pub type TraitPredicate<'db> = ty::TraitPredicate<DbInterner<'db>>;
+pub type HostEffectPredicate<'db> = ty::HostEffectPredicate<DbInterner<'db>>;
pub type ClauseKind<'db> = ty::ClauseKind<DbInterner<'db>>;
pub type PredicateKind<'db> = ty::PredicateKind<DbInterner<'db>>;
pub type NormalizesTo<'db> = ty::NormalizesTo<DbInterner<'db>>;
@@ -705,34 +706,6 @@ impl<'db> rustc_type_ir::inherent::Predicate<DbInterner<'db>> for Predicate<'db>
_ => None,
}
}
-
- /// Whether this projection can be soundly normalized.
- ///
- /// Wf predicates must not be normalized, as normalization
- /// can remove required bounds which would cause us to
- /// unsoundly accept some programs. See #91068.
- fn allow_normalization(self) -> bool {
- // TODO: this should probably live in rustc_type_ir
- match self.inner().as_ref().skip_binder() {
- PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) => {
- false
- }
- PredicateKind::Clause(ClauseKind::Trait(_))
- | PredicateKind::Clause(ClauseKind::RegionOutlives(_))
- | PredicateKind::Clause(ClauseKind::TypeOutlives(_))
- | PredicateKind::Clause(ClauseKind::Projection(_))
- | PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
- | PredicateKind::Clause(ClauseKind::HostEffect(..))
- | PredicateKind::Clause(ClauseKind::UnstableFeature(_))
- | PredicateKind::DynCompatible(_)
- | PredicateKind::Subtype(_)
- | PredicateKind::Coerce(_)
- | PredicateKind::Clause(ClauseKind::ConstEvaluatable(_))
- | PredicateKind::ConstEquate(_, _)
- | PredicateKind::NormalizesTo(..)
- | PredicateKind::Ambiguous => true,
- }
- }
}
impl<'db> Predicate<'db> {
@@ -912,7 +885,9 @@ impl<'db> rustc_type_ir::inherent::Clause<DbInterner<'db>> for Clause<'db> {
let shifted_pred =
cx.shift_bound_var_indices(trait_bound_vars.len(), bound_pred.skip_binder());
// 2) Self: Bar1<'a, '^0.1> -> T: Bar1<'^0.0, '^0.1>
- let new = EarlyBinder::bind(shifted_pred).instantiate(cx, trait_ref.skip_binder().args);
+ let new = EarlyBinder::bind(shifted_pred)
+ .instantiate(cx, trait_ref.skip_binder().args)
+ .skip_norm_wip();
// 3) ['x] + ['b] -> ['x, 'b]
let bound_vars =
BoundVarKinds::new_from_iter(cx, trait_bound_vars.iter().chain(pred_bound_vars.iter()));
diff --git a/crates/hir-ty/src/next_solver/region.rs b/crates/hir-ty/src/next_solver/region.rs
index 3f0aebac2d..72a25f4df6 100644
--- a/crates/hir-ty/src/next_solver/region.rs
+++ b/crates/hir-ty/src/next_solver/region.rs
@@ -18,6 +18,7 @@ use crate::next_solver::{
use super::{SolverDefId, interner::DbInterner};
pub type RegionKind<'db> = rustc_type_ir::RegionKind<DbInterner<'db>>;
+pub type RegionConstraint<'db> = rustc_type_ir::RegionConstraint<DbInterner<'db>>;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Region<'db> {
@@ -136,7 +137,7 @@ impl<'db> Region<'db> {
}
RegionKind::ReError(..) => {
flags |= TypeFlags::HAS_FREE_REGIONS;
- flags |= TypeFlags::HAS_ERROR;
+ flags |= TypeFlags::HAS_RE_ERROR;
}
}
diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs
index d45ac6c959..b1b3a0e0dc 100644
--- a/crates/hir-ty/src/next_solver/solver.rs
+++ b/crates/hir-ty/src/next_solver/solver.rs
@@ -1,30 +1,32 @@
//! Defining `SolverContext` for next-trait-solver.
use hir_def::{
- AssocItemId, GeneralConstId,
+ AssocItemId,
signatures::{ConstSignature, TypeAliasSignature},
};
use rustc_next_trait_solver::delegate::SolverDelegate;
use rustc_type_ir::{
- AliasTyKind, GenericArgKind, InferCtxtLike, Interner, PredicatePolarity, TypeFlags,
+ AliasTyKind, GenericArgKind, InferCtxtLike, InferTy, Interner, PredicatePolarity, TypeFlags,
TypeVisitableExt,
inherent::{IntoKind, Term as _, Ty as _},
lang_items::SolverTraitLangItem,
- solve::{Certainty, NoSolution},
+ solve::{Certainty, FetchEligibleAssocItemResponse, NoSolution, VisibleForLeakCheck},
};
use tracing::debug;
use crate::{
- ParamEnvAndCrate,
+ ParamEnvAndCrate, Span,
+ db::GeneralConstId,
next_solver::{
- AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs,
- ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, UnevaluatedConst,
- fold::fold_tys, util::sizedness_fast_path,
+ AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, ErrorGuaranteed,
+ GenericArgs, ImplOrTraitAssocTermId, OpaqueTyIdWrapper, ParamEnv, Predicate, PredicateKind,
+ RegionConstraint, SubtypePredicate, TermId, TraitAssocTermId, Ty, TyKind, TypingMode,
+ UnevaluatedConst, fold::fold_tys, util::sizedness_fast_path,
},
};
use super::{
- Const, DbInterner, ErrorGuaranteed, GenericArg, SolverDefId, Span,
+ Const, DbInterner, GenericArg,
infer::{DbInternerInferExt, InferCtxt, canonical::instantiate::CanonicalExt},
};
@@ -63,15 +65,15 @@ impl<'db> SolverDelegate for SolverContext<'db> {
where
V: rustc_type_ir::TypeFoldable<Self::Interner>,
{
- let (infcx, value, vars) = cx.infer_ctxt().build_with_canonical(canonical);
+ let (infcx, value, vars) = cx.infer_ctxt().build_with_canonical(Span::Dummy, canonical);
(SolverContext(infcx), value, vars)
}
- fn fresh_var_for_kind_with_span(&self, arg: GenericArg<'db>, _span: Span) -> GenericArg<'db> {
+ fn fresh_var_for_kind_with_span(&self, arg: GenericArg<'db>, span: Span) -> GenericArg<'db> {
match arg.kind() {
- GenericArgKind::Lifetime(_) => self.next_region_var().into(),
- GenericArgKind::Type(_) => self.next_ty_var().into(),
- GenericArgKind::Const(_) => self.next_const_var().into(),
+ GenericArgKind::Lifetime(_) => self.next_region_var(span).into(),
+ GenericArgKind::Type(_) => self.next_ty_var(span).into(),
+ GenericArgKind::Const(_) => self.next_const_var(span).into(),
}
}
@@ -98,14 +100,9 @@ impl<'db> SolverDelegate for SolverContext<'db> {
None
}
- fn make_deduplicated_outlives_constraints(
+ fn make_deduplicated_region_constraints(
&self,
- ) -> Vec<
- rustc_type_ir::OutlivesPredicate<
- Self::Interner,
- <Self::Interner as rustc_type_ir::Interner>::GenericArg,
- >,
- > {
+ ) -> Vec<(RegionConstraint<'db>, VisibleForLeakCheck)> {
// FIXME: add if we care about regions
vec![]
}
@@ -124,23 +121,22 @@ impl<'db> SolverDelegate for SolverContext<'db> {
fn instantiate_canonical_var(
&self,
kind: CanonicalVarKind<'db>,
- _span: <Self::Interner as Interner>::Span,
+ span: Span,
var_values: &[GenericArg<'db>],
universe_map: impl Fn(rustc_type_ir::UniverseIndex) -> rustc_type_ir::UniverseIndex,
) -> GenericArg<'db> {
- self.0.instantiate_canonical_var(kind, var_values, universe_map)
+ self.0.instantiate_canonical_var(span, kind, var_values, universe_map)
}
fn add_item_bounds_for_hidden_type(
&self,
- def_id: SolverDefId,
+ opaque_id: OpaqueTyIdWrapper,
args: GenericArgs<'db>,
param_env: ParamEnv<'db>,
hidden_ty: Ty<'db>,
goals: &mut Vec<Goal<'db, Predicate<'db>>>,
) {
let interner = self.interner;
- let opaque_id = def_id.expect_opaque_ty();
// Require that the hidden type is well-formed. We have to
// make sure we wf-check the hidden type to fix #114728.
//
@@ -162,14 +158,14 @@ impl<'db> SolverDelegate for SolverContext<'db> {
kind: AliasTyKind::Opaque { def_id: def_id2 },
args: args2,
..
- }) if def_id == def_id2 && args == args2 => hidden_ty,
+ }) if opaque_id == def_id2 && args == args2 => hidden_ty,
_ => ty,
})
};
- let item_bounds = opaque_id.predicates(interner.db);
+ let item_bounds = opaque_id.0.predicates(interner.db);
for predicate in item_bounds.iter_instantiated_copied(interner, args.as_slice()) {
- let predicate = replace_opaques_in(predicate);
+ let predicate = replace_opaques_in(predicate.skip_norm_wip());
// Require that the predicate holds for the concrete type.
debug!(?predicate);
@@ -180,16 +176,16 @@ impl<'db> SolverDelegate for SolverContext<'db> {
fn fetch_eligible_assoc_item(
&self,
_goal_trait_ref: rustc_type_ir::TraitRef<Self::Interner>,
- trait_assoc_def_id: SolverDefId,
+ trait_assoc_def_id: TraitAssocTermId,
impl_id: AnyImplId,
- ) -> Result<Option<SolverDefId>, ErrorGuaranteed> {
+ ) -> FetchEligibleAssocItemResponse<Self::Interner> {
let AnyImplId::ImplId(impl_id) = impl_id else {
// Builtin derive traits don't have type/consts assoc items.
- return Ok(None);
+ return FetchEligibleAssocItemResponse::Err(ErrorGuaranteed);
};
let impl_items = impl_id.impl_items(self.0.interner.db());
- let id = match trait_assoc_def_id {
- SolverDefId::TypeAliasId(trait_assoc_id) => {
+ let id = match trait_assoc_def_id.0 {
+ TermId::TypeAliasId(trait_assoc_id) => {
let trait_assoc_data = TypeAliasSignature::of(self.0.interner.db, trait_assoc_id);
impl_items
.items
@@ -206,9 +202,9 @@ impl<'db> SolverDelegate for SolverContext<'db> {
.or_else(|| {
if trait_assoc_data.ty.is_some() { Some(trait_assoc_id) } else { None }
})
- .map(SolverDefId::TypeAliasId)
+ .map(|def| ImplOrTraitAssocTermId(TermId::TypeAliasId(def)))
}
- SolverDefId::ConstId(trait_assoc_id) => {
+ TermId::ConstId(trait_assoc_id) => {
let trait_assoc_data = ConstSignature::of(self.0.interner.db, trait_assoc_id);
let trait_assoc_name = trait_assoc_data
.name
@@ -231,11 +227,20 @@ impl<'db> SolverDelegate for SolverContext<'db> {
if trait_assoc_data.has_body() { Some(trait_assoc_id) } else { None }
},
)
- .map(SolverDefId::ConstId)
+ .map(|def| ImplOrTraitAssocTermId(TermId::ConstId(def)))
}
- _ => panic!("Unexpected SolverDefId"),
};
- Ok(id)
+ match id {
+ Some(id) => FetchEligibleAssocItemResponse::Found(id),
+ None => match self.typing_mode_raw() {
+ TypingMode::ErasedNotCoherence(_) => {
+ FetchEligibleAssocItemResponse::NotFoundBecauseErased
+ }
+ typing_mode => {
+ FetchEligibleAssocItemResponse::NotFound(typing_mode.assert_not_erased())
+ }
+ },
+ }
}
fn is_transmutable(
@@ -260,11 +265,10 @@ impl<'db> SolverDelegate for SolverContext<'db> {
self.cx().db.const_eval(c, subst, None).ok()?
}
GeneralConstId::StaticId(c) => self.cx().db.const_eval_static(c).ok()?,
- // TODO: Wire up const_eval_anon query in Phase 5.
- // For now, return an error const so normalization resolves the
- // unevaluated const to Error (matching the old behavior where
- // complex expressions produced ConstKind::Error directly).
- GeneralConstId::AnonConstId(_) => return Some(Const::error(self.cx())),
+ GeneralConstId::AnonConstId(c) => {
+ let subst = uv.args;
+ self.cx().db.anon_const_eval(c, subst, None).ok()?
+ }
};
Some(Const::new_from_allocation(
self.interner,
@@ -293,10 +297,10 @@ impl<'db> SolverDelegate for SolverContext<'db> {
}
if trait_pred.polarity() == PredicatePolarity::Positive {
- match self.0.cx().as_trait_lang_item(trait_pred.def_id()) {
+ match self.0.interner.as_trait_lang_item(trait_pred.def_id()) {
Some(SolverTraitLangItem::Sized) | Some(SolverTraitLangItem::MetaSized) => {
let predicate = self.resolve_vars_if_possible(goal.predicate);
- if sizedness_fast_path(self.cx(), predicate, goal.param_env) {
+ if sizedness_fast_path(self.interner, predicate, goal.param_env) {
return Some(Certainty::Yes);
}
}
@@ -322,17 +326,31 @@ impl<'db> SolverDelegate for SolverContext<'db> {
let pred = goal.predicate.kind();
match pred.no_bound_vars()? {
- PredicateKind::Clause(ClauseKind::RegionOutlives(_outlives)) => Some(Certainty::Yes),
- PredicateKind::Clause(ClauseKind::TypeOutlives(_outlives)) => Some(Certainty::Yes),
+ PredicateKind::DynCompatible(def_id)
+ if self.0.interner.trait_is_dyn_compatible(def_id) =>
+ {
+ Some(Certainty::Yes)
+ }
+ PredicateKind::Clause(ClauseKind::RegionOutlives(outlives)) => {
+ self.0.sub_regions(outlives.1, outlives.0);
+ Some(Certainty::Yes)
+ }
+ PredicateKind::Clause(ClauseKind::TypeOutlives(outlives)) => {
+ self.0.register_type_outlives_constraint(outlives.0, outlives.1);
+
+ Some(Certainty::Yes)
+ }
PredicateKind::Subtype(SubtypePredicate { a, b, .. })
| PredicateKind::Coerce(CoercePredicate { a, b }) => {
- if self.shallow_resolve(a).is_ty_var() && self.shallow_resolve(b).is_ty_var() {
- // FIXME: We also need to register a subtype relation between these vars
- // when those are added, and if they aren't in the same sub root then
- // we should mark this goal as `has_changed`.
- Some(Certainty::AMBIGUOUS)
- } else {
- None
+ match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) {
+ (
+ TyKind::Infer(InferTy::TyVar(a_vid)),
+ TyKind::Infer(InferTy::TyVar(b_vid)),
+ ) => {
+ self.sub_unify_ty_vids_raw(a_vid, b_vid);
+ Some(Certainty::AMBIGUOUS)
+ }
+ _ => None,
}
}
PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, _)) => {
@@ -343,6 +361,7 @@ impl<'db> SolverDelegate for SolverContext<'db> {
}
}
PredicateKind::Clause(ClauseKind::WellFormed(arg)) => {
+ let arg = self.shallow_resolve_term(arg);
if arg.is_trivially_wf(self.interner) {
Some(Certainty::Yes)
} else if arg.is_infer() {
diff --git a/crates/hir-ty/src/next_solver/structural_normalize.rs b/crates/hir-ty/src/next_solver/structural_normalize.rs
index 00c3708358..0dbd97a636 100644
--- a/crates/hir-ty/src/next_solver/structural_normalize.rs
+++ b/crates/hir-ty/src/next_solver/structural_normalize.rs
@@ -30,18 +30,18 @@ impl<'db> At<'_, 'db> {
) -> Result<Term<'db>, Vec<NextSolverError<'db>>> {
assert!(!term.is_infer(), "should have resolved vars before calling");
- if term.to_alias_term().is_none() {
+ if term.to_alias_term(self.infcx.interner).is_none() {
return Ok(term);
}
- let new_infer = self.infcx.next_term_var_of_kind(term);
+ let new_infer = self.infcx.next_term_var_of_kind(term, self.cause.span());
// We simply emit an `alias-eq` goal here, since that will take care of
// normalizing the LHS of the projection until it is a rigid projection
// (or a not-yet-defined opaque in scope).
let obligation = Obligation::new(
self.infcx.interner,
- self.cause.clone(),
+ *self.cause,
self.param_env,
PredicateKind::AliasRelate(term, new_infer, AliasRelationDirection::Equate),
);
diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs
index 39abdaf079..c43e04b9d0 100644
--- a/crates/hir-ty/src/next_solver/ty.rs
+++ b/crates/hir-ty/src/next_solver/ty.rs
@@ -9,15 +9,15 @@ use hir_def::{
use hir_def::{TraitId, type_ref::Rawness};
use intern::{Interned, InternedRef, impl_internable};
use macros::GenericTypeVisitable;
-use rustc_abi::{Float, Integer, Size};
+use rustc_abi::{ExternAbi, Float, Integer, Size};
use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult};
use rustc_type_ir::{
- AliasTyKind, BoundVar, BoundVarIndexKind, ClosureKind, CoroutineArgs, CoroutineArgsParts,
- DebruijnIndex, FlagComputation, Flags, FloatTy, FloatVid, GenericTypeVisitable, InferTy, IntTy,
- IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
- TypeVisitableExt, TypeVisitor, UintTy, Upcast, WithCachedTypeInfo,
+ BoundVar, BoundVarIndexKind, ClosureKind, DebruijnIndex, FlagComputation, Flags, FloatTy,
+ FloatVid, GenericTypeVisitable, InferTy, IntTy, IntVid, Interner, TyVid, TypeFoldable,
+ TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy,
+ Upcast, WithCachedTypeInfo,
inherent::{
- AdtDef as _, BoundExistentialPredicates, Const as _, GenericArgs as _, IntoKind, ParamLike,
+ AdtDef as _, BoundExistentialPredicates, GenericArgs as _, IntoKind, ParamLike,
Safety as _, SliceLike, Ty as _,
},
relate::Relate,
@@ -26,13 +26,12 @@ use rustc_type_ir::{
};
use crate::{
- FnAbi,
- db::{HirDatabase, InternedClosure},
+ db::{HirDatabase, InternedOpaqueTyId},
lower::GenericPredicates,
next_solver::{
AdtDef, AliasTy, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const,
- CoroutineClosureIdWrapper, CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Region,
- TraitRef, TypeAliasIdWrapper,
+ CoroutineClosureIdWrapper, CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Predicate,
+ Region, TraitRef, TypeAliasIdWrapper, Unnormalized,
abi::Safety,
impl_foldable_for_interned_slice, impl_stored_interned, interned_slice,
util::{CoroutineArgsExt, IntegerTypeExt},
@@ -47,6 +46,9 @@ use super::{
pub type SimplifiedType = rustc_type_ir::fast_reject::SimplifiedType<SolverDefId>;
pub type TyKind<'db> = rustc_type_ir::TyKind<DbInterner<'db>>;
pub type FnHeader<'db> = rustc_type_ir::FnHeader<DbInterner<'db>>;
+pub type AliasTyKind<'db> = rustc_type_ir::AliasTyKind<DbInterner<'db>>;
+pub type AliasTermKind<'db> = rustc_type_ir::AliasTermKind<DbInterner<'db>>;
+pub type FnSigKind<'db> = rustc_type_ir::FnSigKind<DbInterner<'db>>;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Ty<'db> {
@@ -174,15 +176,44 @@ impl<'db> Ty<'db> {
pub fn new_opaque(
interner: DbInterner<'db>,
- def_id: SolverDefId,
+ def_id: InternedOpaqueTyId,
args: GenericArgs<'db>,
) -> Self {
Ty::new_alias(
interner,
- AliasTy::new_from_args(interner, AliasTyKind::Opaque { def_id }, args),
+ AliasTy::new_from_args(interner, AliasTyKind::Opaque { def_id: def_id.into() }, args),
)
}
+ /// Note: this needs an interner with crate.
+ pub fn new_array(interner: DbInterner<'db>, ty: Ty<'db>, n: u128) -> Ty<'db> {
+ Ty::new(
+ interner,
+ TyKind::Array(
+ ty,
+ crate::consteval::usize_const(interner.db, Some(n), interner.expect_crate()),
+ ),
+ )
+ }
+
+ fn new_generic_adt(interner: DbInterner<'db>, adt_id: AdtId, ty_param: Ty<'db>) -> Ty<'db> {
+ let args = GenericArgs::fill_with_defaults(
+ interner,
+ adt_id.into(),
+ [ty_param.into()],
+ |_, _, _| panic!("all params except the first should have defaults"),
+ );
+ Ty::new_adt(interner, adt_id, args)
+ }
+
+ /// Note: Unlike most other constructors, this require the interner to have a crate, because this needs lang items.
+ pub fn new_box(interner: DbInterner<'db>, ty: Ty<'db>) -> Ty<'db> {
+ let Some(def_id) = interner.lang_items().OwnedBox else {
+ return Ty::new_error(interner, ErrorGuaranteed);
+ };
+ Ty::new_generic_adt(interner, def_id.into(), ty)
+ }
+
/// Returns the `Size` for primitive types (bool, uint, int, char, float).
pub fn primitive_size(self, interner: DbInterner<'db>) -> Size {
match self.kind() {
@@ -252,9 +283,9 @@ impl<'db> Ty<'db> {
tys.last().is_none_or(|ty| ty.has_trivial_sizedness(tcx, sizedness))
}
- TyKind::Adt(def, args) => def
- .sizedness_constraint(tcx, sizedness)
- .is_none_or(|ty| ty.instantiate(tcx, args).has_trivial_sizedness(tcx, sizedness)),
+ TyKind::Adt(def, args) => def.sizedness_constraint(tcx, sizedness).is_none_or(|ty| {
+ ty.instantiate(tcx, args).skip_norm_wip().has_trivial_sizedness(tcx, sizedness)
+ }),
TyKind::Alias(..) | TyKind::Param(_) | TyKind::Placeholder(..) | TyKind::Bound(..) => {
false
@@ -472,10 +503,14 @@ impl<'db> Ty<'db> {
}
}
+ pub fn is_box(self) -> bool {
+ matches!(self.kind(), TyKind::Adt(adt_def, _) if adt_def.is_box())
+ }
+
#[inline]
pub fn as_adt(self) -> Option<(AdtId, GenericArgs<'db>)> {
match self.kind() {
- TyKind::Adt(adt_def, args) => Some((adt_def.def_id().0, args)),
+ TyKind::Adt(adt_def, args) => Some((adt_def.def_id(), args)),
_ => None,
}
}
@@ -501,7 +536,7 @@ impl<'db> Ty<'db> {
/// unsafe.
pub fn safe_to_unsafe_fn_ty(interner: DbInterner<'db>, sig: PolyFnSig<'db>) -> Ty<'db> {
assert!(sig.safety().is_safe());
- Ty::new_fn_ptr(interner, sig.map_bound(|sig| FnSig { safety: Safety::Unsafe, ..sig }))
+ Ty::new_fn_ptr(interner, sig.map_bound(|sig| sig.set_safety(Safety::Unsafe)))
}
/// Returns the type of `*ty`.
@@ -538,7 +573,7 @@ impl<'db> Ty<'db> {
pub fn callable_sig(self, interner: DbInterner<'db>) -> Option<Binder<'db, FnSig<'db>>> {
match self.kind() {
TyKind::FnDef(callable, args) => {
- Some(interner.fn_sig(callable).instantiate(interner, args))
+ Some(interner.fn_sig(callable).instantiate(interner, args).skip_norm_wip())
}
TyKind::FnPtr(sig, hdr) => Some(sig.with(hdr)),
TyKind::Closure(_, closure_args) => {
@@ -546,23 +581,13 @@ impl<'db> Ty<'db> {
}
TyKind::CoroutineClosure(coroutine_id, args) => {
Some(args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
- let unit_ty = Ty::new_unit(interner);
- let return_ty = Ty::new_coroutine(
+ let closure_args = args.as_coroutine_closure();
+ let return_ty = sig.to_coroutine(
interner,
+ closure_args.parent_args(),
+ closure_args.kind_ty(),
interner.coroutine_for_closure(coroutine_id),
- CoroutineArgs::new(
- interner,
- CoroutineArgsParts {
- parent_args: args.as_coroutine_closure().parent_args(),
- kind_ty: unit_ty,
- resume_ty: unit_ty,
- yield_ty: unit_ty,
- return_ty: sig.return_ty,
- // FIXME: Deduce this from the coroutine closure's upvars.
- tupled_upvars_ty: unit_ty,
- },
- )
- .args,
+ closure_args.tupled_upvars_ty(),
);
FnSig {
inputs_and_output: Tys::new_from_iter(
@@ -572,9 +597,7 @@ impl<'db> Ty<'db> {
.iter()
.chain(std::iter::once(return_ty)),
),
- c_variadic: sig.c_variadic,
- safety: sig.safety,
- abi: sig.abi,
+ fn_sig_kind: sig.fn_sig_kind,
}
}))
}
@@ -597,6 +620,10 @@ impl<'db> Ty<'db> {
}
}
+ pub fn is_tuple(self) -> bool {
+ matches!(self.kind(), TyKind::Tuple(_))
+ }
+
pub fn as_tuple(self) -> Option<Tys<'db>> {
match self.kind() {
TyKind::Tuple(tys) => Some(tys),
@@ -704,9 +731,10 @@ impl<'db> Ty<'db> {
match self.kind() {
TyKind::Alias(AliasTy { kind: AliasTyKind::Opaque { def_id }, args, .. }) => Some(
def_id
- .expect_opaque_ty()
+ .0
.predicates(db)
.iter_instantiated_copied(interner, args.as_slice())
+ .map(Unnormalized::skip_norm_wip)
.collect(),
),
TyKind::Param(param) => {
@@ -718,6 +746,7 @@ impl<'db> Ty<'db> {
TypeParamProvenance::ArgumentImplTrait => {
let predicates = GenericPredicates::query_all(db, param.id.parent())
.iter_identity()
+ .map(Unnormalized::skip_norm_wip)
.filter(|wc| match wc.kind().skip_binder() {
ClauseKind::Trait(tr) => tr.self_ty() == self,
ClauseKind::Projection(pred) => pred.self_ty() == self,
@@ -734,7 +763,7 @@ impl<'db> Ty<'db> {
}
}
TyKind::Coroutine(coroutine_id, _args) => {
- let InternedClosure(owner, _) = coroutine_id.0.loc(db);
+ let owner = coroutine_id.0.loc(db).owner;
let krate = owner.krate(db);
if let Some(future_trait) = hir_def::lang_item::lang_items(db, krate).Future {
// This is only used by type walking.
@@ -784,25 +813,11 @@ impl<'db> Ty<'db> {
}
pub fn references_non_lt_error<'db, T: TypeVisitableExt<DbInterner<'db>>>(t: &T) -> bool {
- t.references_error() && t.visit_with(&mut ReferencesNonLifetimeError).is_break()
-}
-
-struct ReferencesNonLifetimeError;
-
-impl<'db> TypeVisitor<DbInterner<'db>> for ReferencesNonLifetimeError {
- type Result = ControlFlow<()>;
-
- fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result {
- if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) }
- }
-
- fn visit_const(&mut self, c: Const<'db>) -> Self::Result {
- if c.is_ct_error() { ControlFlow::Break(()) } else { c.super_visit_with(self) }
- }
+ t.has_non_region_error()
}
pub fn references_only_ty_error<'db, T: TypeVisitableExt<DbInterner<'db>>>(t: &T) -> bool {
- t.references_error() && t.visit_with(&mut ReferencesOnlyTyError).is_break()
+ references_non_lt_error(t) && t.visit_with(&mut ReferencesOnlyTyError).is_break()
}
struct ReferencesOnlyTyError;
@@ -811,7 +826,29 @@ impl<'db> TypeVisitor<DbInterner<'db>> for ReferencesOnlyTyError {
type Result = ControlFlow<()>;
fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result {
- if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) }
+ if !ty.references_non_lt_error() {
+ ControlFlow::Continue(())
+ } else if ty.is_ty_error() {
+ ControlFlow::Break(())
+ } else {
+ ty.super_visit_with(self)
+ }
+ }
+
+ fn visit_const(&mut self, c: Const<'db>) -> Self::Result {
+ if !references_non_lt_error(&c) {
+ ControlFlow::Continue(())
+ } else {
+ c.super_visit_with(self)
+ }
+ }
+
+ fn visit_predicate(&mut self, p: Predicate<'db>) -> Self::Result {
+ if !references_non_lt_error(&p) {
+ ControlFlow::Continue(())
+ } else {
+ p.super_visit_with(self)
+ }
}
}
@@ -847,6 +884,15 @@ impl<'db> TypeVisitable<DbInterner<'db>> for Ty<'db> {
}
}
+impl<'db> TypeVisitable<DbInterner<'db>> for StoredTy {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ self.as_ref().visit_with(visitor)
+ }
+}
+
impl<'db> TypeSuperVisitable<DbInterner<'db>> for Ty<'db> {
fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
&self,
@@ -913,6 +959,18 @@ impl<'db> TypeFoldable<DbInterner<'db>> for Ty<'db> {
}
}
+impl<'db> TypeFoldable<DbInterner<'db>> for StoredTy {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(self.as_ref().try_fold_with(folder)?.store())
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ self.as_ref().fold_with(folder).store()
+ }
+}
+
impl<'db> TypeSuperFoldable<DbInterner<'db>> for Ty<'db> {
fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
self,
@@ -1294,9 +1352,11 @@ impl<'db> rustc_type_ir::inherent::Ty<DbInterner<'db>> for Ty<'db> {
false
}
- fn discriminant_ty(self, interner: DbInterner<'db>) -> <DbInterner<'db> as Interner>::Ty {
+ fn discriminant_ty(self, interner: DbInterner<'db>) -> Ty<'db> {
match self.kind() {
- TyKind::Adt(adt, _) if adt.is_enum() => adt.repr().discr_type().to_ty(interner),
+ TyKind::Adt(adt, _) if adt.is_enum() => {
+ adt.repr(interner.db).discr_type().to_ty(interner)
+ }
TyKind::Coroutine(_, args) => args.as_coroutine().discr_ty(interner),
TyKind::Param(_) | TyKind::Alias(..) | TyKind::Infer(InferTy::TyVar(_)) => {
@@ -1444,7 +1504,7 @@ impl<'db> DbInterner<'db> {
TyKind::Tuple(params) => params,
_ => panic!(),
};
- self.mk_fn_sig(params, s.output(), s.c_variadic, safety, FnAbi::Rust)
+ self.mk_fn_sig(params, s.output(), s.c_variadic(), safety, ExternAbi::Rust)
})
}
}
diff --git a/crates/hir-ty/src/next_solver/util.rs b/crates/hir-ty/src/next_solver/util.rs
index 858233cb2c..fb3bd18bf4 100644
--- a/crates/hir-ty/src/next_solver/util.rs
+++ b/crates/hir-ty/src/next_solver/util.rs
@@ -423,14 +423,14 @@ pub fn sizedness_constraint_for_ty<'db>(
.and_then(|ty| sizedness_constraint_for_ty(interner, sizedness, ty)),
Adt(adt, args) => {
- if crate::representability::representability(interner.db, adt.def_id().0)
+ if crate::representability::representability(interner.db, adt.def_id())
== Representability::Infinite
{
return None;
}
adt.struct_tail_ty(interner).and_then(|tail_ty| {
- let tail_ty = tail_ty.instantiate(interner, args);
+ let tail_ty = tail_ty.instantiate(interner, args).skip_norm_wip();
sizedness_constraint_for_ty(interner, sizedness, tail_ty)
})
}
@@ -717,7 +717,7 @@ pub(crate) fn clauses_as_obligations<'db>(
param_env: ParamEnv<'db>,
) -> impl Iterator<Item = PredicateObligation<'db>> {
clauses.into_iter().map(move |clause| Obligation {
- cause: cause.clone(),
+ cause,
param_env,
predicate: clause.as_predicate(),
recursion_depth: 0,
diff --git a/crates/hir-ty/src/opaques.rs b/crates/hir-ty/src/opaques.rs
index 2e85beea91..4244b1bac4 100644
--- a/crates/hir-ty/src/opaques.rs
+++ b/crates/hir-ty/src/opaques.rs
@@ -10,7 +10,7 @@ use rustc_type_ir::inherent::Ty as _;
use syntax::ast;
use crate::{
- ImplTraitId, InferenceResult,
+ ImplTraitId, InferBodyId, InferenceResult,
db::{HirDatabase, InternedOpaqueTyId},
lower::{ImplTraitIdx, ImplTraits},
next_solver::{
@@ -22,10 +22,10 @@ use crate::{
pub(crate) fn opaque_types_defined_by(
db: &dyn HirDatabase,
- def_id: DefWithBodyId,
+ def_id: InferBodyId,
result: &mut Vec<SolverDefId>,
) {
- if let DefWithBodyId::FunctionId(func) = def_id {
+ if let Some(func) = def_id.as_function() {
// A function may define its own RPITs.
extend_with_opaques(
db,
@@ -66,9 +66,15 @@ pub(crate) fn opaque_types_defined_by(
_ => {}
};
match def_id {
- DefWithBodyId::ConstId(id) => extend_with_atpit_from_container(id.loc(db).container),
- DefWithBodyId::FunctionId(id) => extend_with_atpit_from_container(id.loc(db).container),
- DefWithBodyId::StaticId(_) | DefWithBodyId::VariantId(_) => {}
+ InferBodyId::DefWithBodyId(DefWithBodyId::ConstId(id)) => {
+ extend_with_atpit_from_container(id.loc(db).container)
+ }
+ InferBodyId::DefWithBodyId(DefWithBodyId::FunctionId(id)) => {
+ extend_with_atpit_from_container(id.loc(db).container)
+ }
+ InferBodyId::DefWithBodyId(DefWithBodyId::StaticId(_))
+ | InferBodyId::DefWithBodyId(DefWithBodyId::VariantId(_))
+ | InferBodyId::AnonConstId(_) => {}
}
// FIXME: Collect opaques from `#[define_opaque]`.
@@ -91,8 +97,8 @@ pub(crate) fn opaque_types_defined_by(
// These are firewall queries to prevent drawing dependencies between infers:
#[salsa::tracked(returns(ref))]
-pub(crate) fn rpit_hidden_types<'db>(
- db: &'db dyn HirDatabase,
+pub(crate) fn rpit_hidden_types(
+ db: &dyn HirDatabase,
function: FunctionId,
) -> ArenaMap<ImplTraitIdx, StoredEarlyBinder<StoredTy>> {
let infer = InferenceResult::of(db, DefWithBodyId::from(function));
@@ -105,8 +111,8 @@ pub(crate) fn rpit_hidden_types<'db>(
}
#[salsa::tracked(returns(ref))]
-pub(crate) fn tait_hidden_types<'db>(
- db: &'db dyn HirDatabase,
+pub(crate) fn tait_hidden_types(
+ db: &dyn HirDatabase,
type_alias: TypeAliasId,
) -> ArenaMap<ImplTraitIdx, StoredEarlyBinder<StoredTy>> {
// Call this first, to not perform redundant work if there are no TAITs.
@@ -149,7 +155,7 @@ pub(crate) fn tait_hidden_types<'db>(
_ = ocx.eq(
&cause,
param_env,
- entry.get().get().instantiate_identity(),
+ entry.get().get().instantiate_identity().skip_norm_wip(),
hidden_type,
);
}
diff --git a/crates/hir-ty/src/representability.rs b/crates/hir-ty/src/representability.rs
index bae204c4ef..0b7dc4d309 100644
--- a/crates/hir-ty/src/representability.rs
+++ b/crates/hir-ty/src/representability.rs
@@ -1,7 +1,7 @@
//! Detecting whether a type is infinitely-sized.
use hir_def::{AdtId, VariantId, hir::generics::GenericParams};
-use rustc_type_ir::inherent::{AdtDef, IntoKind};
+use rustc_type_ir::inherent::IntoKind;
use crate::{
db::HirDatabase,
@@ -47,14 +47,14 @@ pub(crate) fn representability_cycle(
fn variant_representability(db: &dyn HirDatabase, id: VariantId) -> Representability {
for ty in db.field_types(id).values() {
- rtry!(representability_ty(db, ty.get().instantiate_identity()));
+ rtry!(representability_ty(db, ty.get().instantiate_identity().skip_norm_wip()));
}
Representability::Representable
}
fn representability_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>) -> Representability {
match ty.kind() {
- TyKind::Adt(adt_id, args) => representability_adt_ty(db, adt_id.def_id().0, args),
+ TyKind::Adt(adt_id, args) => representability_adt_ty(db, adt_id.def_id(), args),
// FIXME(#11924) allow zero-length arrays?
TyKind::Array(ty, _) => representability_ty(db, ty),
TyKind::Tuple(tys) => {
@@ -94,7 +94,11 @@ fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> {
.collect::<Box<[bool]>>();
let mut handle_variant = |variant| {
for field in db.field_types(variant).values() {
- params_in_repr_ty(db, field.get().instantiate_identity(), &mut params_in_repr);
+ params_in_repr_ty(
+ db,
+ field.get().instantiate_identity().skip_norm_wip(),
+ &mut params_in_repr,
+ );
}
};
match def_id {
@@ -112,7 +116,7 @@ fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> {
fn params_in_repr_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>, params_in_repr: &mut [bool]) {
match ty.kind() {
TyKind::Adt(adt, args) => {
- let inner_params_in_repr = self::params_in_repr(db, adt.def_id().0);
+ let inner_params_in_repr = self::params_in_repr(db, adt.def_id());
for (i, arg) in args.iter().enumerate() {
if let GenericArgKind::Type(ty) = arg.kind()
&& inner_params_in_repr[i]
diff --git a/crates/hir-ty/src/solver_errors.rs b/crates/hir-ty/src/solver_errors.rs
new file mode 100644
index 0000000000..e4e76fa67b
--- /dev/null
+++ b/crates/hir-ty/src/solver_errors.rs
@@ -0,0 +1,90 @@
+//! Handling of trait solver errors and converting them to errors `hir` can pass to `ide-diagnostics`.
+//!
+//! Note that we also have [`crate::next_solver::infer::errors`], which takes the raw [`NextSolverError`],
+//! and converts it into [`FulfillmentError`] that contains more details.
+//!
+//! [`NextSolverError`]: crate::next_solver::fulfill::NextSolverError
+
+use macros::{TypeFoldable, TypeVisitable};
+use rustc_type_ir::{PredicatePolarity, inherent::IntoKind};
+
+use crate::{
+ Span,
+ next_solver::{
+ ClauseKind, DbInterner, PredicateKind, StoredTraitRef, TraitPredicate,
+ infer::{
+ errors::{FulfillmentError, FulfillmentErrorCode},
+ select::SelectionError,
+ },
+ },
+};
+
+#[derive(Debug, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)]
+pub struct SolverDiagnostic {
+ pub span: Span,
+ pub kind: SolverDiagnosticKind,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)]
+pub enum SolverDiagnosticKind {
+ TraitUnimplemented {
+ trait_predicate: StoredTraitPredicate,
+ root_trait_predicate: Option<StoredTraitPredicate>,
+ },
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)]
+pub struct StoredTraitPredicate {
+ pub trait_ref: StoredTraitRef,
+ pub polarity: PredicatePolarity,
+}
+
+impl StoredTraitPredicate {
+ #[inline]
+ pub fn get<'db>(&'db self, interner: DbInterner<'db>) -> TraitPredicate<'db> {
+ TraitPredicate { polarity: self.polarity, trait_ref: self.trait_ref.get(interner) }
+ }
+}
+
+impl SolverDiagnostic {
+ pub fn from_fulfillment_error(error: &FulfillmentError<'_>) -> Option<Self> {
+ let span = error.obligation.cause.span();
+ if span.is_dummy() {
+ return None;
+ }
+
+ // FIXME: Handle more error kinds.
+ let kind = match &error.code {
+ FulfillmentErrorCode::Select(SelectionError::Unimplemented) => {
+ match error.obligation.predicate.kind().skip_binder() {
+ PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => {
+ handle_trait_unimplemented(error, trait_pred)?
+ }
+ _ => return None,
+ }
+ }
+ _ => return None,
+ };
+ Some(SolverDiagnostic { span, kind })
+ }
+}
+
+fn handle_trait_unimplemented<'db>(
+ error: &FulfillmentError<'db>,
+ trait_pred: TraitPredicate<'db>,
+) -> Option<SolverDiagnosticKind> {
+ let trait_predicate = StoredTraitPredicate {
+ trait_ref: StoredTraitRef::new(trait_pred.trait_ref),
+ polarity: trait_pred.polarity,
+ };
+
+ let root_trait_predicate = match error.root_obligation.predicate.kind().skip_binder() {
+ PredicateKind::Clause(ClauseKind::Trait(trait_pred)) => Some(StoredTraitPredicate {
+ trait_ref: StoredTraitRef::new(trait_pred.trait_ref),
+ polarity: trait_pred.polarity,
+ }),
+ _ => None,
+ };
+
+ Some(SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate })
+}
diff --git a/crates/hir-ty/src/specialization.rs b/crates/hir-ty/src/specialization.rs
index 8bc6c51fae..467b598447 100644
--- a/crates/hir-ty/src/specialization.rs
+++ b/crates/hir-ty/src/specialization.rs
@@ -1,17 +1,17 @@
//! Impl specialization related things
use hir_def::{
- ExpressionStoreOwnerId, GenericDefId, HasModule, ImplId, nameres::crate_def_map,
- signatures::ImplSignature,
+ ExpressionStoreOwnerId, GenericDefId, HasModule, ImplId, signatures::ImplSignature,
+ unstable_features::UnstableFeatures,
};
-use intern::sym;
use tracing::debug;
use crate::{
+ Span,
db::HirDatabase,
lower::GenericPredicates,
next_solver::{
- DbInterner, TypingMode,
+ DbInterner, TypingMode, Unnormalized,
infer::{DbInternerInferExt, traits::ObligationCause},
obligation_ctxt::ObligationCtxt,
util::clauses_as_obligations,
@@ -81,7 +81,7 @@ fn specializes_query(
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
let specializing_impl_trait_ref =
- db.impl_trait(specializing_impl_def_id).unwrap().instantiate_identity();
+ db.impl_trait(specializing_impl_def_id).unwrap().instantiate_identity().skip_norm_wip();
let cause = &ObligationCause::dummy();
debug!(
"fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
@@ -92,11 +92,12 @@ fn specializes_query(
let mut ocx = ObligationCtxt::new(&infcx);
- let parent_args = infcx.fresh_args_for_item(parent_impl_def_id.into());
+ let parent_args = infcx.fresh_args_for_item(Span::Dummy, parent_impl_def_id.into());
let parent_impl_trait_ref = db
.impl_trait(parent_impl_def_id)
.expect("expected source impl to be a trait impl")
- .instantiate(interner, parent_args);
+ .instantiate(interner, parent_args)
+ .skip_norm_wip();
// do the impls unify? If not, no specialization.
let Ok(()) = ocx.eq(cause, param_env, specializing_impl_trait_ref, parent_impl_trait_ref)
@@ -109,8 +110,9 @@ fn specializes_query(
// only be referenced via projection predicates.
ocx.register_obligations(clauses_as_obligations(
GenericPredicates::query_all(db, parent_impl_def_id.into())
- .iter_instantiated(interner, parent_args.as_slice()),
- cause.clone(),
+ .iter_instantiated(interner, parent_args.as_slice())
+ .map(Unnormalized::skip_norm_wip),
+ *cause,
param_env,
));
@@ -153,10 +155,8 @@ pub(crate) fn specializes(
// `#[allow_internal_unstable(specialization)]`, but `#[allow_internal_unstable]`
// is an internal feature, std is not using it for specialization nor is likely to
// ever use it, and we don't have the span information necessary to replicate that.
- let def_map = crate_def_map(db, module.krate(db));
- if !def_map.is_unstable_feature_enabled(&sym::specialization)
- && !def_map.is_unstable_feature_enabled(&sym::min_specialization)
- {
+ let features = UnstableFeatures::query(db, module.krate(db));
+ if !features.specialization && !features.min_specialization {
return false;
}
diff --git a/crates/hir-ty/src/tests.rs b/crates/hir-ty/src/tests.rs
index 430a570444..d259ce7963 100644
--- a/crates/hir-ty/src/tests.rs
+++ b/crates/hir-ty/src/tests.rs
@@ -16,8 +16,8 @@ mod traits;
use base_db::{Crate, SourceDatabase};
use expect_test::Expect;
use hir_def::{
- AssocItemId, DefWithBodyId, GenericDefId, HasModule, Lookup, ModuleDefId, ModuleId,
- SyntheticSyntax,
+ AdtId, AssocItemId, DefWithBodyId, GenericDefId, HasModule, Lookup, ModuleDefId, ModuleId,
+ SyntheticSyntax, VariantId,
expr_store::{Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap},
hir::{ExprId, Pat, PatId},
item_scope::ItemScope,
@@ -36,9 +36,10 @@ use syntax::{
use test_fixture::WithFixture;
use crate::{
- InferenceResult,
+ InferenceDiagnostic, InferenceResult,
+ db::{AnonConstId, HirDatabase},
display::{DisplayTarget, HirDisplay},
- infer::{Adjustment, TypeMismatch},
+ infer::Adjustment,
next_solver::Ty,
setup_tracing,
test_db::TestDB,
@@ -88,15 +89,12 @@ fn check_impl(
let file_range = FileRange { file_id, range };
if only_types {
types.insert(file_range, expected);
- } else if expected.starts_with("type: ") {
- types.insert(file_range, expected.trim_start_matches("type: ").to_owned());
+ } else if let Some(ty) = expected.strip_prefix("type: ") {
+ types.insert(file_range, ty.to_owned());
} else if expected.starts_with("expected") {
mismatches.insert(file_range, expected);
- } else if expected.starts_with("adjustments:") {
- adjustments.insert(
- file_range,
- expected.trim_start_matches("adjustments:").trim().to_owned(),
- );
+ } else if let Some(adjs) = expected.strip_prefix("adjustments:") {
+ adjustments.insert(file_range, adjs.trim().to_owned());
} else {
panic!("unexpected annotation: {expected} @ {range:?}");
}
@@ -198,7 +196,14 @@ fn check_impl(
}
}
- for (expr_or_pat, mismatch) in inference_result.type_mismatches() {
+ let type_mismatches =
+ inference_result.diagnostics().iter().filter_map(|diag| match diag {
+ InferenceDiagnostic::TypeMismatch { node, expected, found } => {
+ Some((*node, expected.as_ref(), found.as_ref()))
+ }
+ _ => None,
+ });
+ for (expr_or_pat, expected, actual) in type_mismatches {
let Some(node) = (match expr_or_pat {
hir_def::hir::ExprOrPatId::ExprId(expr) => {
expr_node(body_source_map, expr, &db)
@@ -210,8 +215,8 @@ fn check_impl(
let range = node.as_ref().original_file_range_rooted(&db);
let actual = format!(
"expected {}, got {}",
- mismatch.expected.as_ref().display_test(&db, display_target),
- mismatch.actual.as_ref().display_test(&db, display_target)
+ expected.display_test(&db, display_target),
+ actual.display_test(&db, display_target)
);
match mismatches.remove(&range) {
Some(annotation) => assert_eq!(actual, annotation),
@@ -329,7 +334,17 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
krate: Crate| {
let display_target = DisplayTarget::from_crate(&db, krate);
let mut types: Vec<(InFile<SyntaxNode>, Ty<'_>)> = Vec::new();
- let mut mismatches: Vec<(InFile<SyntaxNode>, &TypeMismatch)> = Vec::new();
+ let type_mismatch_for_node = inference_result
+ .diagnostics()
+ .iter()
+ .filter_map(|diag| match diag {
+ InferenceDiagnostic::TypeMismatch { node, expected, found } => {
+ Some((*node, (expected.as_ref(), found.as_ref())))
+ }
+ _ => None,
+ })
+ .collect::<FxHashMap<_, _>>();
+ let mut mismatches: Vec<(InFile<SyntaxNode>, (Ty<'_>, Ty<'_>))> = Vec::new();
if let Some((binding_id, syntax_ptr)) = self_param {
let ty = &inference_result.type_of_binding[binding_id];
@@ -352,8 +367,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
Err(SyntheticSyntax) => continue,
};
types.push((node.clone(), ty.as_ref()));
- if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat) {
- mismatches.push((node, mismatch));
+ if let Some(mismatch) = type_mismatch_for_node.get(&pat.into()) {
+ mismatches.push((node, *mismatch));
}
}
@@ -366,8 +381,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
Err(SyntheticSyntax) => continue,
};
types.push((node.clone(), ty.as_ref()));
- if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) {
- mismatches.push((node, mismatch));
+ if let Some(mismatch) = type_mismatch_for_node.get(&expr.into()) {
+ mismatches.push((node, *mismatch));
}
}
@@ -398,7 +413,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let range = node.value.text_range();
(range.start(), range.end())
});
- for (src_ptr, mismatch) in &mismatches {
+ for (src_ptr, (expected, actual)) in &mismatches {
let range = src_ptr.value.text_range();
let macro_prefix = if src_ptr.file_id != file_id { "!" } else { "" };
format_to!(
@@ -406,8 +421,8 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
"{}{:?}: expected {}, got {}\n",
macro_prefix,
range,
- mismatch.expected.as_ref().display_test(&db, display_target),
- mismatch.actual.as_ref().display_test(&db, display_target),
+ expected.display_test(&db, display_target),
+ actual.display_test(&db, display_target),
);
}
}
@@ -418,6 +433,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let mut defs: Vec<(DefWithBodyId, Crate)> = Vec::new();
let mut generic_defs: Vec<(GenericDefId, Crate)> = Vec::new();
+ let mut variants: Vec<(VariantId, Crate)> = Vec::new();
visit_module(&db, def_map, module, &mut |it| {
let krate = module.krate(&db);
match it {
@@ -438,6 +454,16 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
}
ModuleDefId::AdtId(it) => {
generic_defs.push((it.into(), krate));
+ match it {
+ AdtId::StructId(id) => variants.push((id.into(), krate)),
+ AdtId::UnionId(id) => variants.push((id.into(), krate)),
+ AdtId::EnumId(id) => variants.extend(
+ id.enum_variants(&db)
+ .variants
+ .iter()
+ .map(|&(variant, ..)| (variant.into(), krate)),
+ ),
+ }
}
ModuleDefId::TraitId(it) => {
generic_defs.push((it.into(), krate));
@@ -488,7 +514,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
for (def, krate) in defs {
let (body, source_map) = Body::with_source_map(&db, def);
let infer = InferenceResult::of(&db, def);
- let self_param = body.self_param.map(|id| (id, source_map.self_param_syntax()));
+ let self_param = body.self_param().map(|id| (id, source_map.self_param_syntax()));
infer_def(infer, body, source_map, self_param, krate);
}
@@ -497,11 +523,26 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
for (def, krate) in generic_defs {
let (store, source_map) = ExpressionStore::with_source_map(&db, def.into());
// Skip if there are no const expressions in the signature
- if store.const_expr_origins().is_empty() {
+ if store.expr_roots().next().is_none() {
continue;
}
- let infer = InferenceResult::of(&db, def);
- infer_def(infer, store, source_map, None, krate);
+ for &anon_const in AnonConstId::all_from_signature(&db, def).into_iter().flatten() {
+ let infer = InferenceResult::of(&db, anon_const);
+ infer_def(infer, store, source_map, None, krate);
+ }
+ }
+ variants.dedup();
+ for (def, krate) in variants {
+ let (store, source_map) = ExpressionStore::with_source_map(&db, def.into());
+ // Skip if there are no const expressions in the signature
+ if store.expr_roots().next().is_none() {
+ continue;
+ }
+ let anon_consts = db.field_types_with_diagnostics(def).defined_anon_consts();
+ for &anon_const in anon_consts {
+ let infer = InferenceResult::of(&db, anon_const);
+ infer_def(infer, store, source_map, None, krate);
+ }
}
buf.truncate(buf.trim_end().len());
@@ -558,7 +599,7 @@ pub(crate) fn visit_module(
let body = Body::of(db, it.into());
visit_body(db, body, cb);
}
- ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => {
+ ModuleDefId::AdtId(AdtId::EnumId(it)) => {
it.enum_variants(db).variants.iter().for_each(|&(it, _, _)| {
let body = Body::of(db, it.into());
cb(it.into());
diff --git a/crates/hir-ty/src/tests/closure_captures.rs b/crates/hir-ty/src/tests/closure_captures.rs
index 5324d8c605..3942a7ae27 100644
--- a/crates/hir-ty/src/tests/closure_captures.rs
+++ b/crates/hir-ty/src/tests/closure_captures.rs
@@ -6,7 +6,7 @@ use hir_def::{
};
use hir_expand::{HirFileId, files::InFileWrapper};
use itertools::Itertools;
-use rustc_type_ir::inherent::{AdtDef as _, IntoKind};
+use rustc_type_ir::inherent::IntoKind;
use span::{Edition, TextRange};
use stdx::{format_to, never};
use syntax::{AstNode, AstPtr};
@@ -42,7 +42,7 @@ fn display_place(db: &TestDB, store: &ExpressionStore, place: &Place, local: Bin
match ty.kind() {
TyKind::Tuple(_) => format_to!(result, ".{field_idx}"),
TyKind::Adt(adt_def, _) => {
- let variant = match adt_def.def_id().0 {
+ let variant = match adt_def.def_id() {
AdtId::StructId(id) => VariantId::from(id),
AdtId::UnionId(id) => id.into(),
AdtId::EnumId(id) => {
@@ -102,19 +102,19 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec
// FIXME: Deduplicate this with hir::Local::sources().
let captured_local = capture.captured_local();
- let local_text_range = match body.self_param.zip(source_map.self_param_syntax())
+ let local_text_range = if body.self_params.contains(&captured_local)
+ && let Some(source) = source_map.self_param_syntax()
{
- Some((param, source)) if param == captured_local => {
- format!("{:?}", text_range(db, source))
- }
- _ => source_map
+ format!("{:?}", text_range(db, source))
+ } else {
+ source_map
.patterns_for_binding(captured_local)
.iter()
.map(|&definition| {
text_range(db, source_map.pat_syntax(definition).unwrap())
})
.map(|it| format!("{it:?}"))
- .join(", "),
+ .join(", ")
};
let place = display_place(db, body, &capture.place, captured_local);
let capture_ty = capture
@@ -600,3 +600,20 @@ fn f() {
expect!["77..110;46..47;96..97 ByRef(Immutable) b &'<erased> i32"],
);
}
+
+#[test]
+fn fail_to_normalize_place() {
+ check_closure_captures(
+ r#"
+//- minicore: index, slice
+const FAIL_CONST: usize = loop {};
+struct Foo {
+ arr: [i32; FAIL_CONST],
+}
+fn foo(foo: &Foo) {
+ || { return foo.arr[0] };
+}
+ "#,
+ expect!["102..126;85..88;114..117 ByRef(Immutable) *foo &'<erased> Foo"],
+ );
+}
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index a80ce5002d..db06d55278 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -392,6 +392,34 @@ fn test() {
}
#[test]
+fn gen_yield_coerce() {
+ check_no_mismatches(
+ r#"
+fn test() {
+ let g = gen {
+ yield &1u32;
+ yield &&1u32;
+ };
+}
+ "#,
+ );
+}
+
+#[test]
+fn async_gen_yield_coerce() {
+ check_no_mismatches(
+ r#"
+fn test() {
+ let g = async gen {
+ yield &1u32;
+ yield &&1u32;
+ };
+}
+ "#,
+ );
+}
+
+#[test]
fn assign_coerce() {
check_no_mismatches(
r"
@@ -878,11 +906,11 @@ struct V<T> { t: T }
fn main() {
let a: V<&dyn Tr>;
(a,) = V { t: &S };
- //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,)
+ //^^^^expected V<&'? S>, got ({unknown},)
let mut a: V<&dyn Tr> = V { t: &S };
(a,) = V { t: &S };
- //^^^^expected V<&'? S>, got (V<&'? (dyn Tr + '?)>,)
+ //^^^^expected V<&'? S>, got ({unknown},)
}
"#,
);
@@ -1008,3 +1036,36 @@ fn f() {
"#,
);
}
+
+#[test]
+fn regression_22270() {
+ check_no_mismatches(
+ r#"
+fn a() {}
+fn b() {}
+
+fn foo<T, const N: usize>(x: [T; N]) -> Vec<T> {
+ loop {}
+}
+
+fn bar() {
+ foo([a, b]);
+}
+ "#,
+ );
+}
+
+#[test]
+fn async_fn_ret() {
+ check_no_mismatches(
+ r#"
+//- minicore: coerce_unsized, unsize, future, index, slice, range
+async fn foo(a: &[i32]) -> &[i32] {
+ if true {
+ return &[];
+ }
+ &a[..0]
+}
+ "#,
+ );
+}
diff --git a/crates/hir-ty/src/tests/diagnostics.rs b/crates/hir-ty/src/tests/diagnostics.rs
index f257aa1b6e..94ca88088d 100644
--- a/crates/hir-ty/src/tests/diagnostics.rs
+++ b/crates/hir-ty/src/tests/diagnostics.rs
@@ -1,6 +1,4 @@
-use crate::tests::check_no_mismatches;
-
-use super::check;
+use super::{check, check_no_mismatches};
#[test]
fn function_return_type_mismatch_1() {
diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs
index 960155a8e4..4c26b19120 100644
--- a/crates/hir-ty/src/tests/incremental.rs
+++ b/crates/hir-ty/src/tests/incremental.rs
@@ -36,7 +36,7 @@ fn foo() -> i32 {
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"InferenceResult::for_body_",
"FunctionSignature::of_",
"FunctionSignature::with_source_map_",
@@ -80,7 +80,7 @@ fn foo() -> i32 {
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
+ "real_span_map",
"AttrFlags::query_",
"FunctionSignature::with_source_map_",
"FunctionSignature::of_",
@@ -125,7 +125,7 @@ fn baz() -> i32 {
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"InferenceResult::for_body_",
"FunctionSignature::of_",
"FunctionSignature::with_source_map_",
@@ -196,7 +196,7 @@ fn baz() -> i32 {
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
+ "real_span_map",
"AttrFlags::query_",
"FunctionSignature::with_source_map_",
"FunctionSignature::of_",
@@ -249,7 +249,7 @@ $0",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"TraitImpls::for_crate_",
"lang_items",
"crate_lang_items",
@@ -286,7 +286,7 @@ pub struct NewStruct {
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
+ "real_span_map",
"crate_local_def_map",
"TraitImpls::for_crate_",
"crate_lang_items",
@@ -324,7 +324,7 @@ $0",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"TraitImpls::for_crate_",
"lang_items",
"crate_lang_items",
@@ -362,7 +362,7 @@ pub enum SomeEnum {
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
+ "real_span_map",
"crate_local_def_map",
"TraitImpls::for_crate_",
"crate_lang_items",
@@ -400,7 +400,7 @@ $0",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"TraitImpls::for_crate_",
"lang_items",
"crate_lang_items",
@@ -435,7 +435,7 @@ fn bar() -> f32 {
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
+ "real_span_map",
"crate_local_def_map",
"TraitImpls::for_crate_",
"crate_lang_items",
@@ -477,7 +477,7 @@ $0",
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"TraitImpls::for_crate_",
"lang_items",
"crate_lang_items",
@@ -520,7 +520,7 @@ impl SomeStruct {
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
+ "real_span_map",
"crate_local_def_map",
"TraitImpls::for_crate_",
"crate_lang_items",
@@ -578,7 +578,7 @@ fn main() {
"file_item_tree_query",
"ast_id_map",
"parse",
- "real_span_map_shim",
+ "real_span_map",
"TraitItems::query_with_diagnostics_",
"Body::of_",
"Body::with_source_map_",
@@ -611,15 +611,14 @@ fn main() {
"StructSignature::with_source_map_",
"AttrFlags::query_",
"GenericPredicates::query_with_diagnostics_",
- "value_ty_query",
"InherentImpls::for_crate_",
- "callable_item_signature_query",
+ "callable_item_signature_with_diagnostics",
"TraitImpls::for_crate_and_deps_",
"TraitImpls::for_crate_",
- "impl_trait_with_diagnostics_query",
+ "impl_trait_with_diagnostics",
"ImplSignature::of_",
"ImplSignature::with_source_map_",
- "impl_self_ty_with_diagnostics_query",
+ "impl_self_ty_with_diagnostics",
"AttrFlags::query_",
"GenericPredicates::query_with_diagnostics_",
"body_upvars_mentioned",
@@ -674,7 +673,7 @@ fn main() {
"parse",
"ast_id_map",
"file_item_tree_query",
- "real_span_map_shim",
+ "real_span_map",
"crate_local_def_map",
"TraitItems::query_with_diagnostics_",
"Body::with_source_map_",
@@ -703,12 +702,12 @@ fn main() {
"AttrFlags::query_",
"GenericPredicates::query_with_diagnostics_",
"InherentImpls::for_crate_",
- "callable_item_signature_query",
+ "callable_item_signature_with_diagnostics",
"TraitImpls::for_crate_",
"ImplSignature::with_source_map_",
"ImplSignature::of_",
- "impl_trait_with_diagnostics_query",
- "impl_self_ty_with_diagnostics_query",
+ "impl_trait_with_diagnostics",
+ "impl_self_ty_with_diagnostics",
"AttrFlags::query_",
"GenericPredicates::query_with_diagnostics_",
"body_upvars_mentioned",
diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs
index fc8c1f8164..4291c9ba18 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1193,7 +1193,7 @@ fn test() {
123..167 '{ ...o(); }': ()
133..134 's': &'? S
137..151 'unsafe { f() }': &'? S
- 146..147 'f': fn f() -> &'static S
+ 146..147 'f': extern "C" fn f() -> &'static S
146..149 'f()': &'static S
157..158 's': &'? S
157..164 's.foo()': bool
diff --git a/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs
index 993293bb56..91273cd177 100644
--- a/crates/hir-ty/src/tests/never_type.rs
+++ b/crates/hir-ty/src/tests/never_type.rs
@@ -354,10 +354,10 @@ fn diverging_expression_3_break() {
11..85 '{ ...} }; }': ()
54..55 'x': u32
63..82 '{ loop...k; } }': u32
- 65..80 'loop { break; }': ()
+ 65..80 'loop { break; }': u32
70..80 '{ break; }': ()
72..77 'break': !
- 65..80: expected u32, got ()
+ 72..77: expected u32, got ()
97..343 '{ ...; }; }': ()
140..141 'x': u32
149..175 '{ for ...; }; }': u32
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index d6bc03f57d..e719f43e74 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -133,8 +133,8 @@ fn infer_literal_pattern() {
55..72 'let "f... any()': bool
59..64 '"foo"': &'static str
59..64 '"foo"': &'static str
- 67..70 'any': fn any<&'static str>() -> &'static str
- 67..72 'any()': &'static str
+ 67..70 'any': fn any<&'? str>() -> &'? str
+ 67..72 'any()': &'? str
73..75 '{}': ()
80..99 'if let...y() {}': ()
83..96 'let 1 = any()': bool
@@ -193,31 +193,27 @@ fn infer_literal_pattern() {
fn infer_range_pattern() {
check_infer_with_mismatches(
r#"
-//- minicore: range
-fn test(x..y: &core::ops::Range<u32>) {
+fn test() {
if let 1..76 = 2u32 {}
if let 1..=76 = 2u32 {}
}
"#,
expect![[r#"
- 8..9 'x': Range<u32>
- 8..12 'x..y': Range<u32>
- 11..12 'y': Range<u32>
- 38..96 '{ ...2 {} }': ()
- 44..66 'if let...u32 {}': ()
- 47..63 'let 1....= 2u32': bool
- 51..52 '1': u32
- 51..56 '1..76': u32
+ 10..68 '{ ...2 {} }': ()
+ 16..38 'if let...u32 {}': ()
+ 19..35 'let 1....= 2u32': bool
+ 23..24 '1': u32
+ 23..28 '1..76': u32
+ 26..28 '76': u32
+ 31..35 '2u32': u32
+ 36..38 '{}': ()
+ 43..66 'if let...u32 {}': ()
+ 46..63 'let 1....= 2u32': bool
+ 50..51 '1': u32
+ 50..56 '1..=76': u32
54..56 '76': u32
59..63 '2u32': u32
64..66 '{}': ()
- 71..94 'if let...u32 {}': ()
- 74..91 'let 1....= 2u32': bool
- 78..79 '1': u32
- 78..84 '1..=76': u32
- 82..84 '76': u32
- 87..91 '2u32': u32
- 92..94 '{}': ()
"#]],
);
check_no_mismatches(
@@ -265,7 +261,6 @@ fn infer_pattern_match_ergonomics() {
#[test]
fn infer_pattern_match_ergonomics_ref() {
- cov_mark::check!(match_ergonomics_ref);
check_infer(
r#"
fn test() {
@@ -409,7 +404,7 @@ fn infer_pattern_match_byte_string_literal() {
209..233 'if let...[S] {}': ()
212..230 'let b"... &v[S]': bool
216..222 'b"foo"': &'static [u8]
- 216..222 'b"foo"': &'static [u8]
+ 216..222 'b"foo"': &'static [u8; 3]
225..230 '&v[S]': &'? [u8]
226..227 'v': [u8; 3]
226..230 'v[S]': [u8]
@@ -422,8 +417,6 @@ fn infer_pattern_match_byte_string_literal() {
254..256 '&v': &'? [u8; 3]
255..256 'v': [u8; 3]
257..259 '{}': ()
- 199..200 '3': usize
- 62..63 'N': usize
"#]],
);
}
@@ -825,6 +818,8 @@ fn box_pattern() {
);
check_infer(
r#"
+ #![feature(lang_items)]
+
#[lang = "owned_box"]
pub struct Box<T>(T);
@@ -835,12 +830,13 @@ fn box_pattern() {
}
"#,
expect![[r#"
- 52..58 'params': Box<i32>
- 70..124 '{ ... } }': ()
- 76..122 'match ... }': ()
- 82..88 'params': Box<i32>
- 99..110 'box integer': Box<i32>
- 114..116 '{}': ()
+ 77..83 'params': Box<i32>
+ 95..149 '{ ... } }': ()
+ 101..147 'match ... }': ()
+ 107..113 'params': Box<i32>
+ 124..135 'box integer': Box<i32>
+ 128..135 'integer': i32
+ 139..141 '{}': ()
"#]],
);
}
@@ -1307,3 +1303,59 @@ fn bar() {
"#,
);
}
+
+#[test]
+fn deref_pattern() {
+ check_infer(
+ r#"
+//- minicore: deref_pat
+use core::ops::{Deref, DerefPure};
+
+#[lang = "owned_box"]
+pub struct Box<T>(T);
+impl<T> Deref for Box<T> {
+ type Target = T;
+ fn deref(&self) -> &Self::Target {
+ loop {}
+ }
+}
+impl<T> DerefPure for Box<T> {}
+
+pub struct Foo<T>(T);
+impl<T> Deref for Foo<T> {
+ type Target = [T];
+ fn deref(&self) -> &Self::Target {
+ loop {}
+ }
+}
+impl<T> DerefPure for Foo<T> {}
+
+fn foo(v: &Box<Foo<i32>>) {
+ match v {
+ deref!(deref!(inner)) => {}
+ _ => {}
+ }
+}
+ "#,
+ expect![[r#"
+ 142..146 'self': &'? Box<T>
+ 165..188 '{ ... }': &'? T
+ 175..182 'loop {}': !
+ 180..182 '{}': ()
+ 310..314 'self': &'? Foo<T>
+ 333..356 '{ ... }': &'? [T]
+ 343..350 'loop {}': !
+ 348..350 '{}': ()
+ !0..20 'builti...inner)': Foo<i32>
+ !0..28 'builti...nner))': Box<Foo<i32>>
+ !14..19 'inner': &'? [i32]
+ 399..400 'v': &'? Box<Foo<i32>>
+ 418..493 '{ ... } }': ()
+ 424..491 'match ... }': ()
+ 430..431 'v': &'? Box<Foo<i32>>
+ 467..469 '{}': ()
+ 478..479 '_': &'? Box<Foo<i32>>
+ 483..485 '{}': ()
+ "#]],
+ );
+}
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index e30fa779da..5a90e700ac 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -2,6 +2,8 @@ mod new_solver;
use expect_test::expect;
+use crate::tests::check;
+
use super::{check_infer, check_no_mismatches, check_types};
#[test]
@@ -1956,7 +1958,7 @@ fn main() {
Alias::Braced;
//^^^^^^^^^^^^^ {unknown}
let Alias::Braced = loop {};
- //^^^^^^^^^^^^^ !
+ //^^^^^^^^^^^^^ {unknown}
let Alias::Braced(..) = loop {};
//^^^^^^^^^^^^^^^^^ Enum
@@ -2013,18 +2015,20 @@ where
#[test]
fn tait_async_stack_overflow_17199() {
- check_types(
+ // The error here is because we don't support TAITs.
+ check(
r#"
//- minicore: fmt, future
type Foo = impl core::fmt::Debug;
async fn foo() -> Foo {
()
+ // ^^ expected impl Debug, got ()
}
async fn test() {
let t = foo().await;
- // ^ impl Debug
+ // ^ type: impl Debug
}
"#,
);
@@ -2234,7 +2238,7 @@ type Bar = impl Foo;
async fn f<A, B, C>() -> Bar {}
"#,
expect![[r#"
- 64..66 '{}': ()
+ 64..66 '{}': impl Foo + ?Sized
"#]],
);
}
@@ -2363,7 +2367,6 @@ fn test() {
}
"#,
expect![[r#"
- 46..49 'Foo': Foo<N>
93..97 'self': Foo<N>
108..125 '{ ... }': usize
118..119 'N': usize
@@ -2755,7 +2758,6 @@ where
664..680 'filter...ter_fn': dyn Fn(&'? T) -> bool + 'static
691..698 'loop {}': !
696..698 '{}': ()
- 512..513 'N': usize
"#]],
);
}
diff --git a/crates/hir-ty/src/tests/regression/new_solver.rs b/crates/hir-ty/src/tests/regression/new_solver.rs
index 565360dc25..33a12fcd1e 100644
--- a/crates/hir-ty/src/tests/regression/new_solver.rs
+++ b/crates/hir-ty/src/tests/regression/new_solver.rs
@@ -34,7 +34,6 @@ impl Space for [u8; 1] {
223..227 'iter': IntoIter<u8>
230..231 'a': Vec<u8>
230..243 'a.into_iter()': IntoIter<u8>
- 322..323 '1': usize
"#]],
);
}
@@ -473,8 +472,6 @@ fn foo() {
249..257 'to_bytes': fn to_bytes() -> [u8; _]
249..259 'to_bytes()': [u8; _]
249..268 'to_byt..._vec()': Vec<<[u8; _] as Foo>::Item>
- 205..206 '_': usize
- 156..157 'N': usize
"#]],
);
}
@@ -516,15 +513,15 @@ fn test_at_most() {
"#,
expect![[r#"
48..49 '0': usize
- 182..186 'self': Between<M, _, T>
+ 182..186 'self': Between<M, 0, T>
188..192 '_sep': &'? str
- 200..206 '_other': Between<M, _, T>
- 222..242 '{ ... }': Between<M, _, T>
- 232..236 'self': Between<M, _, T>
+ 200..206 '_other': Between<M, 0, T>
+ 222..242 '{ ... }': Between<M, 0, T>
+ 232..236 'self': Between<M, 0, T>
300..304 'self': Self
- 343..372 '{ ... }': Between<M, _, Self>
- 353..360 'Between': fn Between<M, _, Self>(Self) -> Between<M, _, Self>
- 353..366 'Between(self)': Between<M, _, Self>
+ 343..372 '{ ... }': Between<M, 0, Self>
+ 353..360 'Between': fn Between<M, 0, Self>(Self) -> Between<M, 0, Self>
+ 353..366 'Between(self)': Between<M, 0, Self>
361..365 'self': Self
404..408 'self': Self
433..462 '{ ... }': Between<0, N, Self>
@@ -532,21 +529,22 @@ fn test_at_most() {
443..456 'Between(self)': Between<0, N, Self>
451..455 'self': Self
510..587 '{ ...um); }': ()
- 520..523 'num': Between<1, _, char>
+ 520..523 'num': Between<1, 0, char>
526..529 ''9'': char
- 526..545 ''9'.at...:<1>()': Between<1, _, char>
- 555..559 '_ver': Between<1, _, char>
- 562..565 'num': Between<1, _, char>
- 562..584 'num.se..., num)': Between<1, _, char>
+ 526..545 ''9'.at...:<1>()': Between<1, 0, char>
+ 541..542 '1': usize
+ 555..559 '_ver': Between<1, 0, char>
+ 562..565 'num': Between<1, 0, char>
+ 562..584 'num.se..., num)': Between<1, 0, char>
575..578 '"."': &'static str
- 580..583 'num': Between<1, _, char>
+ 580..583 'num': Between<1, 0, char>
607..644 '{ ...>(); }': ()
617..620 'num': Between<0, 1, char>
623..626 ''9'': char
623..641 ''9'.at...:<1>()': Between<0, 1, char>
+ 637..638 '1': usize
320..335 '{ Consts::MAX }': usize
322..333 'Consts::MAX': usize
- 421..422 '0': i32
144..159 '{ Consts::MAX }': usize
146..157 'Consts::MAX': usize
"#]],
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 3ea21f8265..c0b8d93b47 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -1240,6 +1240,9 @@ fn infer_array() {
274..275 'x': [u8; 0]
287..289 '[]': [u8; 0]
299..300 'y': [u8; 4]
+ 307..308 '2': usize
+ 307..310 '2+2': usize
+ 309..310 '2': usize
314..323 '[1,2,3,4]': [u8; 4]
315..316 '1': u8
317..318 '2': u8
@@ -1811,8 +1814,6 @@ impl Foo for u8 {
}
#[test]
-// FIXME
-#[should_panic]
fn const_eval_in_function_signature() {
check_types(
r#"
@@ -2066,6 +2067,138 @@ fn test() {
}
#[test]
+fn gen_block_types_inferred() {
+ check_infer(
+ r#"
+//- minicore: iterator, deref
+use core::iter::Iterator;
+
+fn test() {
+ let mut generator = gen {
+ yield 1i8;
+ };
+ let result = generator.next();
+}
+ "#,
+ expect![[r#"
+ 37..131 '{ ...t(); }': ()
+ 47..60 'mut generator': impl Iterator<Item = i8>
+ 63..93 'gen { ... }': impl Iterator<Item = i8>
+ 77..86 'yield 1i8': ()
+ 83..86 '1i8': i8
+ 103..109 'result': Option<i8>
+ 112..121 'generator': impl Iterator<Item = i8>
+ 112..128 'genera...next()': Option<i8>
+ "#]],
+ );
+}
+
+#[test]
+fn async_gen_block_types_inferred() {
+ check_infer(
+ r#"
+//- minicore: async_iterator, option, future, deref, pin
+use core::async_iter::AsyncIterator;
+use core::pin::Pin;
+use core::task::Context;
+
+fn test(mut cx: Context<'_>) {
+ let mut generator = async gen {
+ yield 1i8;
+ };
+ let result = Pin::new(&mut generator).poll_next(&mut cx);
+}
+ "#,
+ expect![[r#"
+ 91..97 'mut cx': Context<'?>
+ 112..239 '{ ...cx); }': ()
+ 122..135 'mut generator': impl AsyncIterator<Item = {unknown}>
+ 138..174 'async ... }': impl AsyncIterator<Item = {unknown}>
+ 158..167 'yield 1i8': ()
+ 164..167 '1i8': i8
+ 184..190 'result': Poll<Option<{unknown}>>
+ 193..201 'Pin::new': fn new<&'? mut impl AsyncIterator<Item = {unknown}>>(&'? mut impl AsyncIterator<Item = {unknown}>) -> Pin<&'? mut impl AsyncIterator<Item = {unknown}>>
+ 193..217 'Pin::n...rator)': Pin<&'? mut impl AsyncIterator<Item = {unknown}>>
+ 193..236 'Pin::n...ut cx)': Poll<Option<{unknown}>>
+ 202..216 '&mut generator': &'? mut impl AsyncIterator<Item = {unknown}>
+ 207..216 'generator': impl AsyncIterator<Item = {unknown}>
+ 228..235 '&mut cx': &'? mut Context<'?>
+ 233..235 'cx': Context<'?>
+ "#]],
+ );
+}
+
+#[test]
+fn gen_fn_types_inferred() {
+ check_infer(
+ r#"
+//- minicore: iterator, deref
+use core::iter::Iterator;
+
+gen fn html() {
+ yield ();
+}
+
+fn test() {
+ let mut generator = html();
+ let result = generator.next();
+}
+ "#,
+ expect![[r#"
+ 41..58 '{ ... (); }': ()
+ 47..55 'yield ()': ()
+ 53..55 '()': ()
+ 70..140 '{ ...t(); }': ()
+ 80..93 'mut generator': impl Iterator<Item = ()>
+ 96..100 'html': fn html() -> impl Iterator<Item = ()>
+ 96..102 'html()': impl Iterator<Item = ()>
+ 112..118 'result': Option<()>
+ 121..130 'generator': impl Iterator<Item = ()>
+ 121..137 'genera...next()': Option<()>
+ "#]],
+ );
+}
+
+#[test]
+fn async_gen_fn_types_inferred() {
+ check_infer(
+ r#"
+//- minicore: async_iterator, option, future, deref, pin
+use core::async_iter::AsyncIterator;
+use core::pin::Pin;
+use core::task::Context;
+
+async gen fn html() {
+ yield ();
+}
+
+fn test(mut cx: Context<'_>) {
+ let mut generator = html();
+ let result = Pin::new(&mut generator).poll_next(&mut cx);
+}
+ "#,
+ expect![[r#"
+ 103..120 '{ ... (); }': ()
+ 109..117 'yield ()': ()
+ 115..117 '()': ()
+ 130..136 'mut cx': Context<'?>
+ 151..248 '{ ...cx); }': ()
+ 161..174 'mut generator': impl AsyncIterator<Item = ()>
+ 177..181 'html': fn html() -> impl AsyncIterator<Item = ()>
+ 177..183 'html()': impl AsyncIterator<Item = ()>
+ 193..199 'result': Poll<Option<()>>
+ 202..210 'Pin::new': fn new<&'? mut impl AsyncIterator<Item = ()>>(&'? mut impl AsyncIterator<Item = ()>) -> Pin<&'? mut impl AsyncIterator<Item = ()>>
+ 202..226 'Pin::n...rator)': Pin<&'? mut impl AsyncIterator<Item = ()>>
+ 202..245 'Pin::n...ut cx)': Poll<Option<()>>
+ 211..225 '&mut generator': &'? mut impl AsyncIterator<Item = ()>
+ 216..225 'generator': impl AsyncIterator<Item = ()>
+ 237..244 '&mut cx': &'? mut Context<'?>
+ 242..244 'cx': Context<'?>
+ "#]],
+ );
+}
+
+#[test]
fn tuple_pattern_nested_match_ergonomics() {
check_no_mismatches(
r#"
@@ -2271,6 +2404,7 @@ fn infer_generic_from_later_assignment() {
89..127 'loop {... }': !
94..127 '{ ... }': ()
104..107 'end': Option<bool>
+ 104..107 'end': Option<bool>
104..120 'end = ...(true)': ()
110..114 'Some': fn Some<bool>(bool) -> Option<bool>
110..120 'Some(true)': Option<bool>
@@ -2563,7 +2697,6 @@ fn generic_default_in_struct_literal() {
#[test]
fn generic_default_depending_on_other_type_arg() {
- // FIXME: the {unknown} is a bug
check_infer(
r#"
struct Thing<T = u128, F = fn() -> T> { t: T }
@@ -2580,7 +2713,7 @@ fn generic_default_depending_on_other_type_arg() {
83..130 '{ ...2 }; }': ()
89..91 't1': Thing<u32, fn() -> u32>
97..99 't2': Thing<u128, fn() -> u128>
- 105..127 'Thing:...1u32 }': Thing<u32, fn() -> {unknown}>
+ 105..127 'Thing:...1u32 }': Thing<u32, fn() -> u32>
121..125 '1u32': u32
"#]],
);
@@ -3459,15 +3592,13 @@ struct TS(usize);
fn main() {
let x;
[x,] = &[1,];
- //^^^^expected &'? [i32; 1], got [{unknown}]
let x;
[(x,),] = &[(1,),];
- //^^^^^^^expected &'? [(i32,); 1], got [{unknown}]
let x;
((x,),) = &((1,),);
- //^^^^^^^expected &'? ((i32,),), got (({unknown},),)
+ //^^^^^^^expected &'? ((i32,),), got ({unknown},)
let x;
(x,) = &(1,);
@@ -3475,7 +3606,7 @@ fn main() {
let x;
(S { a: x },) = &(S { a: 42 },);
- //^^^^^^^^^^^^^expected &'? (S,), got (S,)
+ //^^^^^^^^^^^^^expected &'? (S,), got ({unknown},)
let x;
S { a: x } = &S { a: 42 };
@@ -3868,8 +3999,6 @@ fn main() {
208..209 'c': u8
213..214 'a': A
213..221 'a.into()': [u8; 2]
- 33..34 '2': usize
- 111..112 '3': usize
"#]],
);
}
@@ -3901,6 +4030,7 @@ fn main() {
100..147 'async_... })': ()
114..146 'async ... }': impl AsyncFnOnce(i32)
121..124 'arg': i32
+ 121..124 'arg': i32
126..146 '{ ... }': ()
136..139 'arg': i32
153..160 'closure': fn closure<impl FnOnce(i32)>(impl FnOnce(i32))
@@ -4055,14 +4185,14 @@ fn foo() {
130..153 '{ ... }': &'? T
140..147 'loop {}': !
145..147 '{}': ()
- 207..220 'LazyLock::new': fn new<[u32; _]>() -> LazyLock<[u32; _]>
- 207..222 'LazyLock::new()': LazyLock<[u32; _]>
+ 207..220 'LazyLock::new': fn new<[u32; 0]>() -> LazyLock<[u32; 0]>
+ 207..222 'LazyLock::new()': LazyLock<[u32; 0]>
234..285 '{ ...CK); }': ()
- 244..245 '_': &'? [u32; _]
- 248..263 'LazyLock::force': fn force<[u32; _]>(&'? LazyLock<[u32; _]>) -> &'? [u32; _]
- 248..282 'LazyLo..._LOCK)': &'? [u32; _]
- 264..281 '&VALUE...Y_LOCK': &'? LazyLock<[u32; _]>
- 265..281 'VALUES...Y_LOCK': LazyLock<[u32; _]>
+ 244..245 '_': &'? [u32; 0]
+ 248..263 'LazyLock::force': fn force<[u32; 0]>(&'? LazyLock<[u32; 0]>) -> &'? [u32; 0]
+ 248..282 'LazyLo..._LOCK)': &'? [u32; 0]
+ 264..281 '&VALUE...Y_LOCK': &'? LazyLock<[u32; 0]>
+ 265..281 'VALUES...Y_LOCK': LazyLock<[u32; 0]>
197..202 '{ 0 }': usize
199..200 '0': usize
"#]],
@@ -4140,11 +4270,38 @@ union U {
"#,
expect![[r#"
242..243 '0': isize
- 46..47 '2': i32
- 65..68 '0.0': f32
- 90..91 '2': i32
- 200..201 '0': i32
- 212..213 '0': i32
+ 111..125 '{ C as usize }': usize
+ 113..114 'C': f32
+ 113..123 'C as usize': usize
+ "#]],
+ );
+}
+
+#[test]
+fn async_closure_with_params() {
+ check_no_mismatches(
+ r#"
+fn foo() {
+ let capture = false;
+ async move |param: i32| {
+ capture;
+ };
+}
+ "#,
+ );
+}
+
+#[test]
+fn enum_variant_anon_const() {
+ check_infer(
+ r#"
+enum Enum {
+ Variant([(); { 2 }]),
+}
+ "#,
+ expect![[r#"
+ 29..34 '{ 2 }': usize
+ 31..32 '2': usize
"#]],
);
}
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 278666ef35..ea978cde58 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -124,6 +124,25 @@ async fn test() {
}
#[test]
+fn infer_async_gen_closure() {
+ check(
+ r#"
+//- minicore: async_iterator, fn, add, builtin_impls
+//- /main.rs edition:2024
+fn test() {
+ let f = async gen move |x: i32| {
+ yield x + 42;
+ //^^^^^^ expected Poll<Option<{unknown}>>, got i32
+ };
+ let a = f(4);
+ a;
+// ^ type: impl AsyncIterator<Item = {unknown}>
+}
+"#,
+ );
+}
+
+#[test]
fn auto_sized_async_block() {
check_no_mismatches(
r#"
@@ -1271,7 +1290,6 @@ fn bar() {
241..245 'R::B': fn B<(), i32>(i32) -> R<(), i32>
241..248 'R::B(7)': R<(), i32>
246..247 '7': i32
- 46..47 '2': usize
"#]],
);
}
@@ -3783,8 +3801,6 @@ fn main() {
371..373 'v4': usize
376..378 'v3': [u8; 4]
376..389 'v3.do_thing()': usize
- 86..87 '4': usize
- 192..193 '2': usize
"#]],
)
}
@@ -3824,9 +3840,6 @@ fn main() {
240..242 'v2': [u8; 2]
245..246 'v': [u8; 2]
245..257 'v.do_thing()': [u8; 2]
- 130..131 'L': usize
- 102..103 'L': usize
- 130..131 'L': usize
"#]],
)
}
@@ -4911,6 +4924,7 @@ async fn baz<T: AsyncFnOnce(u32) -> i32>(c: T) {
"#,
expect![[r#"
37..38 'a': T
+ 37..38 'a': T
43..83 '{ ...ait; }': ()
53..57 'fut1': <T as AsyncFnMut<(u32,)>>::CallRefFuture<'?>
60..61 'a': T
@@ -4919,6 +4933,7 @@ async fn baz<T: AsyncFnOnce(u32) -> i32>(c: T) {
70..74 'fut1': <T as AsyncFnMut<(u32,)>>::CallRefFuture<'?>
70..80 'fut1.await': i32
124..129 'mut b': T
+ 124..129 'mut b': T
134..174 '{ ...ait; }': ()
144..148 'fut2': <T as AsyncFnMut<(u32,)>>::CallRefFuture<'?>
151..152 'b': T
@@ -4927,6 +4942,7 @@ async fn baz<T: AsyncFnOnce(u32) -> i32>(c: T) {
161..165 'fut2': <T as AsyncFnMut<(u32,)>>::CallRefFuture<'?>
161..171 'fut2.await': i32
216..217 'c': T
+ 216..217 'c': T
222..262 '{ ...ait; }': ()
232..236 'fut3': <T as AsyncFnOnce<(u32,)>>::CallOnceFuture
239..240 'c': T
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs
index 878696c721..ad1c3fb709 100644
--- a/crates/hir-ty/src/traits.rs
+++ b/crates/hir-ty/src/traits.rs
@@ -15,18 +15,16 @@ use hir_def::{
};
use hir_expand::name::Name;
use intern::sym;
-use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt};
use rustc_type_ir::{
TypingMode,
- inherent::{AdtDef, BoundExistentialPredicates, IntoKind, Span as _},
- solve::Certainty,
+ inherent::{BoundExistentialPredicates, IntoKind},
};
use crate::{
+ Span,
db::HirDatabase,
next_solver::{
- Canonical, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, SolverContext, Span,
- StoredClauses, Ty, TyKind,
+ DbInterner, GenericArgs, ParamEnv, StoredClauses, Ty, TyKind,
infer::{
DbInternerInferExt, InferCtxt,
traits::{Obligation, ObligationCause},
@@ -79,91 +77,6 @@ pub fn structurally_normalize_ty<'db>(
ty.replace_infer_with_error(infcx.interner)
}
-#[derive(Clone, Debug, PartialEq)]
-pub enum NextTraitSolveResult {
- Certain,
- Uncertain,
- NoSolution,
-}
-
-impl NextTraitSolveResult {
- pub fn no_solution(&self) -> bool {
- matches!(self, NextTraitSolveResult::NoSolution)
- }
-
- pub fn certain(&self) -> bool {
- matches!(self, NextTraitSolveResult::Certain)
- }
-
- pub fn uncertain(&self) -> bool {
- matches!(self, NextTraitSolveResult::Uncertain)
- }
-}
-
-pub fn next_trait_solve_canonical_in_ctxt<'db>(
- infer_ctxt: &InferCtxt<'db>,
- goal: Canonical<'db, Goal<'db, Predicate<'db>>>,
-) -> NextTraitSolveResult {
- infer_ctxt.probe(|_| {
- let context = <&SolverContext<'db>>::from(infer_ctxt);
-
- tracing::info!(?goal);
-
- let (goal, var_values) = context.instantiate_canonical(&goal);
- tracing::info!(?var_values);
-
- let res = context.evaluate_root_goal(goal, Span::dummy(), None);
-
- let obligation = Obligation {
- cause: ObligationCause::dummy(),
- param_env: goal.param_env,
- recursion_depth: 0,
- predicate: goal.predicate,
- };
- infer_ctxt.inspect_evaluated_obligation(&obligation, &res, || {
- Some(context.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1)
- });
-
- let res = res.map(|r| (r.has_changed, r.certainty));
-
- tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res);
-
- match res {
- Err(_) => NextTraitSolveResult::NoSolution,
- Ok((_, Certainty::Yes)) => NextTraitSolveResult::Certain,
- Ok((_, Certainty::Maybe { .. })) => NextTraitSolveResult::Uncertain,
- }
- })
-}
-
-/// Solve a trait goal using next trait solver.
-pub fn next_trait_solve_in_ctxt<'db, 'a>(
- infer_ctxt: &'a InferCtxt<'db>,
- goal: Goal<'db, Predicate<'db>>,
-) -> Result<(HasChanged, Certainty), rustc_type_ir::solve::NoSolution> {
- tracing::info!(?goal);
-
- let context = <&SolverContext<'db>>::from(infer_ctxt);
-
- let res = context.evaluate_root_goal(goal, Span::dummy(), None);
-
- let obligation = Obligation {
- cause: ObligationCause::dummy(),
- param_env: goal.param_env,
- recursion_depth: 0,
- predicate: goal.predicate,
- };
- infer_ctxt.inspect_evaluated_obligation(&obligation, &res, || {
- Some(context.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1)
- });
-
- let res = res.map(|r| (r.has_changed, r.certainty));
-
- tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res);
-
- res
-}
-
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Update)]
pub enum FnTrait {
// Warning: Order is important. If something implements `x` it should also implement
@@ -209,7 +122,7 @@ pub fn implements_trait_unique<'db>(
trait_: TraitId,
) -> bool {
implements_trait_unique_impl(db, env, trait_, &mut |infcx| {
- infcx.fill_rest_fresh_args(trait_.into(), [ty.into()])
+ infcx.fill_rest_fresh_args(Span::Dummy, trait_.into(), [ty.into()])
})
}
@@ -235,14 +148,13 @@ fn implements_trait_unique_impl<'db>(
let args = create_args(&infcx);
let trait_ref = rustc_type_ir::TraitRef::new_from_args(interner, trait_.into(), args);
- let goal = Goal::new(interner, env.param_env, trait_ref);
- let result = crate::traits::next_trait_solve_in_ctxt(&infcx, goal);
- matches!(result, Ok((_, Certainty::Yes)))
+ let obligation = Obligation::new(interner, ObligationCause::dummy(), env.param_env, trait_ref);
+ infcx.predicate_must_hold_modulo_regions(&obligation)
}
pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id: ImplId) -> bool {
- let self_ty = db.impl_self_ty(impl_id).instantiate_identity();
+ let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip();
let self_ty = self_ty.kind();
let impl_allowed = match self_ty {
TyKind::Tuple(_)
@@ -259,7 +171,7 @@ pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id
| TyKind::Uint(_)
| TyKind::Float(_) => def_map.is_rustc_coherence_is_core(),
- TyKind::Adt(adt_def, _) => adt_def.def_id().0.module(db).krate(db) == def_map.krate(),
+ TyKind::Adt(adt_def, _) => adt_def.def_id().module(db).krate(db) == def_map.krate(),
TyKind::Dynamic(it, _) => it
.principal_def_id()
.is_some_and(|trait_id| trait_id.0.module(db).krate(db) == def_map.krate()),
@@ -282,7 +194,7 @@ pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id
| TyKind::Uint(_)
| TyKind::Float(_) => true,
- TyKind::Adt(adt_def, _) => match adt_def.def_id().0 {
+ TyKind::Adt(adt_def, _) => match adt_def.def_id() {
hir_def::AdtId::StructId(id) => StructSignature::of(db, id)
.flags
.contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
@@ -334,7 +246,7 @@ pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool
let local_crate = impl_.lookup(db).container.krate(db);
let is_local = |tgt_crate| tgt_crate == local_crate;
- let trait_ref = impl_trait.instantiate_identity();
+ let trait_ref = impl_trait.instantiate_identity().skip_norm_wip();
let trait_id = trait_ref.def_id.0;
if is_local(trait_id.module(db).krate(db)) {
// trait to be implemented is local
@@ -347,7 +259,7 @@ pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool
match ty.kind() {
TyKind::Ref(_, referenced, _) => ty = referenced,
TyKind::Adt(adt_def, subs) => {
- let AdtId::StructId(s) = adt_def.def_id().0 else {
+ let AdtId::StructId(s) = adt_def.def_id() else {
break ty;
};
let struct_signature = StructSignature::of(db, s);
@@ -370,7 +282,7 @@ pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool
// FIXME: param coverage
// - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`)
let is_not_orphan = trait_ref.args.types().any(|ty| match unwrap_fundamental(ty).kind() {
- TyKind::Adt(adt_def, _) => is_local(adt_def.def_id().0.module(db).krate(db)),
+ TyKind::Adt(adt_def, _) => is_local(adt_def.def_id().module(db).krate(db)),
TyKind::Error(_) => true,
TyKind::Dynamic(it, _) => {
it.principal_def_id().is_some_and(|trait_id| is_local(trait_id.0.module(db).krate(db)))
diff --git a/crates/hir-ty/src/upvars.rs b/crates/hir-ty/src/upvars.rs
index 48f3c803d8..6dcd8b59a5 100644
--- a/crates/hir-ty/src/upvars.rs
+++ b/crates/hir-ty/src/upvars.rs
@@ -3,7 +3,7 @@
use hir_def::{
DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId,
expr_store::{ExpressionStore, path::Path},
- hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
+ hir::{BindingId, Expr, ExprId, ExprOrPatId},
resolver::{HasResolver, Resolver, ValueNs},
};
use hir_expand::mod_path::PathKind;
@@ -110,10 +110,7 @@ pub fn upvars_mentioned_impl(
owner: ExpressionStoreOwnerId,
) -> Option<Box<FxHashMap<ExprId, Upvars>>> {
let store = ExpressionStore::of(db, owner);
- if store.const_expr_origins().is_empty() {
- // Save constructing a Resolver.
- return None;
- }
+ store.expr_roots().next()?;
let mut resolver = owner.resolver(db);
let mut result = FxHashMap::default();
for root_expr in store.expr_roots() {
@@ -181,22 +178,6 @@ pub fn upvars_mentioned_impl(
path,
);
}
- &Expr::Assignment { target, .. } => {
- body.walk_pats(target, &mut |pat| {
- let Pat::Path(path) = &body[pat] else { return };
- resolve_maybe_upvar(
- db,
- resolver,
- owner,
- body,
- current_closure,
- expr,
- pat.into(),
- upvars,
- path,
- );
- });
- }
&Expr::Closure { body: body_expr, .. } => {
let mut closure_upvars = FxHashSet::default();
handle_expr_inside_closure(
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index ae9b2c4618..764d02ccf1 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -5,10 +5,9 @@ use std::iter::Enumerate;
use base_db::target::{self, TargetData};
use hir_def::{
- EnumId, EnumVariantId, FunctionId, Lookup, TraitId, attrs::AttrFlags, lang_item::LangItems,
+ EnumId, EnumVariantId, FunctionId, Lookup, TraitId, lang_item::LangItems,
signatures::FunctionSignature,
};
-use intern::sym;
use rustc_abi::TargetDataLayout;
use span::Edition;
@@ -105,21 +104,10 @@ pub fn is_fn_unsafe_to_call(
let loc = func.lookup(db);
match loc.container {
- hir_def::ItemContainerId::ExternBlockId(block) => {
- let is_intrinsic_block = block.abi(db) == Some(sym::rust_dash_intrinsic);
- if is_intrinsic_block {
- // legacy intrinsics
- // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
- if AttrFlags::query(db, func.into()).contains(AttrFlags::RUSTC_SAFE_INTRINSIC) {
- Unsafety::Safe
- } else {
- Unsafety::Unsafe
- }
- } else {
- // Function in an `extern` block are always unsafe to call, except when
- // it is marked as `safe`.
- if data.is_safe() { Unsafety::Safe } else { Unsafety::Unsafe }
- }
+ hir_def::ItemContainerId::ExternBlockId(_) => {
+ // Function in an `extern` block are always unsafe to call, except when
+ // it is marked as `safe`.
+ if data.is_safe() { Unsafety::Safe } else { Unsafety::Unsafe }
}
_ => Unsafety::Safe,
}
diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs
index a88457e3c7..7eee78b8c4 100644
--- a/crates/hir-ty/src/variance.rs
+++ b/crates/hir-ty/src/variance.rs
@@ -18,10 +18,7 @@ use hir_def::{
signatures::{StructFlags, StructSignature},
};
use rustc_ast_ir::Mutability;
-use rustc_type_ir::{
- Variance,
- inherent::{AdtDef, IntoKind},
-};
+use rustc_type_ir::{Variance, inherent::IntoKind};
use stdx::never;
use crate::{
@@ -129,7 +126,7 @@ impl<'db> Context<'db> {
let mut add_constraints_from_variant = |variant| {
for (_, field) in db.field_types(variant).iter() {
self.add_constraints_from_ty(
- field.get().instantiate_identity(),
+ field.get().instantiate_identity().skip_norm_wip(),
Variance::Covariant,
);
}
@@ -214,7 +211,7 @@ impl<'db> Context<'db> {
}
}
TyKind::Adt(def, args) => {
- self.add_constraints_from_args(def.def_id().0.into(), args, variance);
+ self.add_constraints_from_args(def.def_id().into(), args, variance);
}
TyKind::Alias(alias) => {
// FIXME: Probably not correct wrt. opaques.
@@ -479,7 +476,6 @@ struct Other<'a> {
#[test]
fn rustc_test_variance_associated_consts() {
- // FIXME: Should be invariant
check(
r#"
trait Trait {
@@ -491,7 +487,7 @@ struct Foo<T: Trait> { //~ ERROR [T: o]
}
"#,
expect![[r#"
- Foo[T: bivariant]
+ Foo[T: invariant]
"#]],
);
}
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 223103b6e5..f9cf05e73a 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -18,9 +18,7 @@ use hir_expand::{
};
use hir_ty::{
db::HirDatabase,
- method_resolution::{
- self, CandidateId, MethodError, MethodResolutionContext, MethodResolutionUnstableFeatures,
- },
+ method_resolution::{self, CandidateId, MethodError, MethodResolutionContext},
next_solver::{DbInterner, TypingMode, infer::DbInternerInferExt},
};
use intern::Symbol;
@@ -498,15 +496,16 @@ fn resolve_impl_trait_item<'db>(
// FIXME: resolve type aliases (which are not yielded by iterate_path_candidates)
let interner = DbInterner::new_with(db, environment.krate);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
- let unstable_features =
- MethodResolutionUnstableFeatures::from_def_map(resolver.top_level_def_map());
+ let features = resolver.top_level_def_map().features();
let ctx = MethodResolutionContext {
infcx: &infcx,
resolver: &resolver,
param_env: environment.param_env,
traits_in_scope: &traits_in_scope,
edition: krate.edition(db),
- unstable_features: &unstable_features,
+ features,
+ call_span: hir_ty::Span::Dummy,
+ receiver_span: hir_ty::Span::Dummy,
};
let resolution = ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty.ty);
let resolution = match resolution {
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 6cfb79d5a1..a044f24587 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -6,7 +6,7 @@
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_def::{
- DefWithBodyId, GenericParamId, SyntheticSyntax,
+ DefWithBodyId, GenericParamId, HasModule, SyntheticSyntax,
expr_store::{
ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast,
hir_generic_arg_to_ast, hir_segment_to_ast_segment,
@@ -15,11 +15,15 @@ use hir_def::{
};
use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name};
use hir_ty::{
- CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathGenericsSource,
- PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
+ CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, ParamEnvAndCrate,
+ PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
db::HirDatabase,
diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
+ display::{DisplayTarget, HirDisplay},
+ next_solver::DbInterner,
+ solver_errors::SolverDiagnosticKind,
};
+use stdx::{impl_from, never};
use syntax::{
AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
ast::{self, HasGenericArgs},
@@ -27,7 +31,7 @@ use syntax::{
};
use triomphe::Arc;
-use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type};
+use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, Variant};
pub use hir_def::VariantId;
pub use hir_ty::{
@@ -35,6 +39,49 @@ pub use hir_ty::{
diagnostics::{CaseType, IncorrectCase},
};
+#[derive(Debug, Clone)]
+pub enum SpanAst {
+ Expr(ast::Expr),
+ Pat(ast::Pat),
+ Type(ast::Type),
+}
+const _: () = {
+ use syntax::ast::*;
+ impl_from!(Expr, Pat, Type for SpanAst);
+};
+
+impl From<Either<ast::Expr, ast::Pat>> for SpanAst {
+ fn from(value: Either<ast::Expr, ast::Pat>) -> Self {
+ match value {
+ Either::Left(it) => it.into(),
+ Either::Right(it) => it.into(),
+ }
+ }
+}
+
+impl ast::AstNode for SpanAst {
+ fn can_cast(kind: syntax::SyntaxKind) -> bool {
+ ast::Expr::can_cast(kind) || ast::Pat::can_cast(kind) || ast::Type::can_cast(kind)
+ }
+
+ fn cast(syntax: syntax::SyntaxNode) -> Option<Self> {
+ ast::Expr::cast(syntax.clone())
+ .map(SpanAst::Expr)
+ .or_else(|| ast::Pat::cast(syntax.clone()).map(SpanAst::Pat))
+ .or_else(|| ast::Type::cast(syntax).map(SpanAst::Type))
+ }
+
+ fn syntax(&self) -> &syntax::SyntaxNode {
+ match self {
+ SpanAst::Expr(it) => it.syntax(),
+ SpanAst::Pat(it) => it.syntax(),
+ SpanAst::Type(it) => it.syntax(),
+ }
+ }
+}
+
+pub type SpanSyntax = InFile<AstPtr<SpanAst>>;
+
macro_rules! diagnostics {
($AnyDiagnostic:ident <$db:lifetime> -> $($diag:ident $(<$lt:lifetime>)?,)*) => {
#[derive(Debug)]
@@ -56,12 +103,18 @@ diagnostics![AnyDiagnostic<'db> ->
AwaitOutsideOfAsync,
BreakOutsideOfLoop,
CastToUnsized<'db>,
+ ExpectedArrayOrSlicePat<'db>,
ExpectedFunction<'db>,
+ FunctionalRecordUpdateOnNonStruct,
+ GenericDefaultRefersToSelf,
InactiveCode,
IncoherentImpl,
IncorrectCase,
+ IncorrectGenericsLen,
+ IncorrectGenericsOrder,
InvalidCast<'db>,
InvalidDeriveTarget,
+ InvalidLhsOfAssignment,
MacroDefError,
MacroError,
MacroExpansionParseError,
@@ -74,11 +127,16 @@ diagnostics![AnyDiagnostic<'db> ->
MovedOutOfRef<'db>,
NeedMut,
NonExhaustiveLet,
+ NonExhaustiveRecordExpr,
NoSuchField,
+ MismatchedArrayPatLen,
+ DuplicateField,
+ PatternArgInExternFn,
PrivateAssocItem,
PrivateField,
RemoveTrailingReturn,
RemoveUnnecessaryElse,
+ UnusedMustUse<'db>,
ReplaceFilterMapNextWithFindMap,
TraitImplIncorrectSafety,
TraitImplMissingAssocItems,
@@ -102,10 +160,11 @@ diagnostics![AnyDiagnostic<'db> ->
GenericArgsProhibited,
ParenthesizedGenericArgsWithoutFnTrait,
BadRtn,
- IncorrectGenericsLen,
- IncorrectGenericsOrder,
MissingLifetime,
ElidedLifetimesInPath,
+ TypeMustBeKnown<'db>,
+ UnionExprMustHaveExactlyOneField,
+ UnimplementedTrait<'db>,
];
#[derive(Debug)]
@@ -212,6 +271,12 @@ pub struct NoSuchField {
}
#[derive(Debug)]
+pub struct DuplicateField {
+ pub field: InFile<AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>>,
+ pub variant: Variant,
+}
+
+#[derive(Debug)]
pub struct PrivateAssocItem {
pub expr_or_pat: InFile<ExprOrPatPtr>,
pub item: AssocItem,
@@ -225,12 +290,31 @@ pub struct MismatchedTupleStructPatArgCount {
}
#[derive(Debug)]
+pub struct MismatchedArrayPatLen {
+ pub pat: InFile<ExprOrPatPtr>,
+ pub expected: u128,
+ pub found: u128,
+ pub has_rest: bool,
+}
+
+#[derive(Debug)]
+pub struct ExpectedArrayOrSlicePat<'db> {
+ pub pat: InFile<ExprOrPatPtr>,
+ pub found: Type<'db>,
+}
+
+#[derive(Debug)]
pub struct ExpectedFunction<'db> {
pub call: InFile<ExprOrPatPtr>,
pub found: Type<'db>,
}
#[derive(Debug)]
+pub struct FunctionalRecordUpdateOnNonStruct {
+ pub base_expr: InFile<ExprOrPatPtr>,
+}
+
+#[derive(Debug)]
pub struct UnresolvedField<'db> {
pub expr: InFile<ExprOrPatPtr>,
pub receiver: Type<'db>,
@@ -312,6 +396,11 @@ pub struct NonExhaustiveLet {
}
#[derive(Debug)]
+pub struct NonExhaustiveRecordExpr {
+ pub expr: InFile<ExprOrPatPtr>,
+}
+
+#[derive(Debug)]
pub struct TypeMismatch<'db> {
pub expr_or_pat: InFile<ExprOrPatPtr>,
pub expected: Type<'db>,
@@ -386,6 +475,12 @@ pub struct RemoveUnnecessaryElse {
}
#[derive(Debug)]
+pub struct UnusedMustUse<'db> {
+ pub expr: InFile<ExprOrPatPtr>,
+ pub message: Option<&'db str>,
+}
+
+#[derive(Debug)]
pub struct CastToUnsized<'db> {
pub expr: InFile<ExprOrPatPtr>,
pub cast_ty: Type<'db>,
@@ -442,6 +537,12 @@ pub struct ElidedLifetimesInPath {
pub hard_error: bool,
}
+#[derive(Debug)]
+pub struct TypeMustBeKnown<'db> {
+ pub at_point: SpanSyntax,
+ pub top_term: Option<Either<Type<'db>, String>>,
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GenericArgKind {
Lifetime,
@@ -465,10 +566,38 @@ pub struct IncorrectGenericsOrder {
pub expected_kind: GenericArgKind,
}
+#[derive(Debug)]
+pub struct GenericDefaultRefersToSelf {
+ /// The `Self` segment.
+ pub segment: InFile<AstPtr<ast::PathSegment>>,
+}
+
+#[derive(Debug)]
+pub struct UnionExprMustHaveExactlyOneField {
+ pub expr: InFile<ExprOrPatPtr>,
+}
+
+#[derive(Debug)]
+pub struct InvalidLhsOfAssignment {
+ pub lhs: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
+}
+
+#[derive(Debug)]
+pub struct PatternArgInExternFn {
+ pub node: InFile<AstPtr<ast::Pat>>,
+}
+
+#[derive(Debug)]
+pub struct UnimplementedTrait<'db> {
+ pub span: SpanSyntax,
+ pub trait_predicate: crate::TraitPredicate<'db>,
+ pub root_trait_predicate: Option<crate::TraitPredicate<'db>>,
+}
+
impl<'db> AnyDiagnostic<'db> {
pub(crate) fn body_validation_diagnostic(
db: &'db dyn HirDatabase,
- diagnostic: BodyValidationDiagnostic,
+ diagnostic: BodyValidationDiagnostic<'db>,
source_map: &hir_def::expr_store::BodySourceMap,
) -> Option<AnyDiagnostic<'db>> {
match diagnostic {
@@ -536,59 +665,47 @@ impl<'db> AnyDiagnostic<'db> {
}
}
BodyValidationDiagnostic::MissingMatchArms { match_expr, uncovered_patterns } => {
- match source_map.expr_syntax(match_expr) {
- Ok(source_ptr) => {
- let root = source_ptr.file_syntax(db);
- if let Either::Left(ast::Expr::MatchExpr(match_expr)) =
- &source_ptr.value.to_node(&root)
- {
- match match_expr.expr() {
- Some(scrut_expr) if match_expr.match_arm_list().is_some() => {
- return Some(
- MissingMatchArms {
- scrutinee_expr: InFile::new(
- source_ptr.file_id,
- AstPtr::new(&scrut_expr),
- ),
- uncovered_patterns,
- }
- .into(),
- );
- }
- _ => {}
- }
+ if let Ok(source_ptr) = source_map.expr_syntax(match_expr)
+ && let root = source_ptr.file_syntax(db)
+ && let Either::Left(ast::Expr::MatchExpr(match_expr)) =
+ source_ptr.value.to_node(&root)
+ && let Some(scrut_expr) = match_expr.expr()
+ && match_expr.match_arm_list().is_some()
+ {
+ return Some(
+ MissingMatchArms {
+ scrutinee_expr: InFile::new(
+ source_ptr.file_id,
+ AstPtr::new(&scrut_expr),
+ ),
+ uncovered_patterns,
}
- }
- Err(SyntheticSyntax) => (),
+ .into(),
+ );
}
}
BodyValidationDiagnostic::NonExhaustiveLet { pat, uncovered_patterns } => {
- match source_map.pat_syntax(pat) {
- Ok(source_ptr) => {
- if let Some(ast_pat) = source_ptr.value.cast::<ast::Pat>() {
- return Some(
- NonExhaustiveLet {
- pat: InFile::new(source_ptr.file_id, ast_pat),
- uncovered_patterns,
- }
- .into(),
- );
+ if let Ok(source_ptr) = source_map.pat_syntax(pat)
+ && let Some(ast_pat) = source_ptr.value.cast::<ast::Pat>()
+ {
+ return Some(
+ NonExhaustiveLet {
+ pat: InFile::new(source_ptr.file_id, ast_pat),
+ uncovered_patterns,
}
- }
- Err(SyntheticSyntax) => {}
+ .into(),
+ );
}
}
BodyValidationDiagnostic::RemoveTrailingReturn { return_expr } => {
- if let Ok(source_ptr) = source_map.expr_syntax(return_expr) {
+ if let Ok(source_ptr) = source_map.expr_syntax(return_expr)
// Filters out desugared return expressions (e.g. desugared try operators).
- if let Some(ptr) = source_ptr.value.cast::<ast::ReturnExpr>() {
- return Some(
- RemoveTrailingReturn {
- return_expr: InFile::new(source_ptr.file_id, ptr),
- }
+ && let Some(ptr) = source_ptr.value.cast::<ast::ReturnExpr>()
+ {
+ return Some(
+ RemoveTrailingReturn { return_expr: InFile::new(source_ptr.file_id, ptr) }
.into(),
- );
- }
+ );
}
}
BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr } => {
@@ -601,6 +718,11 @@ impl<'db> AnyDiagnostic<'db> {
);
}
}
+ BodyValidationDiagnostic::UnusedMustUse { expr, message } => {
+ if let Ok(source_ptr) = source_map.expr_syntax(expr) {
+ return Some(UnusedMustUse { expr: source_ptr, message }.into());
+ }
+ }
}
None
}
@@ -608,9 +730,10 @@ impl<'db> AnyDiagnostic<'db> {
pub(crate) fn inference_diagnostic(
db: &'db dyn HirDatabase,
def: DefWithBodyId,
- d: &InferenceDiagnostic,
+ d: &'db InferenceDiagnostic,
source_map: &hir_def::expr_store::BodySourceMap,
sig_map: &hir_def::expr_store::ExpressionStoreSourceMap,
+ env: ParamEnvAndCrate<'db>,
) -> Option<AnyDiagnostic<'db>> {
let expr_syntax = |expr| {
source_map
@@ -624,10 +747,28 @@ impl<'db> AnyDiagnostic<'db> {
.inspect_err(|_| stdx::never!("inference diagnostic in desugared pattern"))
.ok()
};
+ let type_syntax = |pat| {
+ source_map
+ .type_syntax(pat)
+ .inspect_err(|_| stdx::never!("inference diagnostic in desugared type"))
+ .ok()
+ };
let expr_or_pat_syntax = |id| match id {
ExprOrPatId::ExprId(expr) => expr_syntax(expr),
ExprOrPatId::PatId(pat) => pat_syntax(pat),
};
+ let span_syntax = |span| match span {
+ hir_ty::Span::ExprId(idx) => expr_syntax(idx).map(|it| it.upcast()),
+ hir_ty::Span::PatId(idx) => pat_syntax(idx).map(|it| it.upcast()),
+ hir_ty::Span::TypeRefId(idx) => type_syntax(idx).map(|it| it.upcast()),
+ hir_ty::Span::BindingId(idx) => {
+ pat_syntax(source_map.patterns_for_binding(idx)[0]).map(|it| it.upcast())
+ }
+ hir_ty::Span::Dummy => {
+ never!("should never create a diagnostic for dummy spans");
+ None
+ }
+ };
Some(match d {
&InferenceDiagnostic::NoSuchField { field: expr, private, variant } => {
let expr_or_pat = match expr {
@@ -639,6 +780,23 @@ impl<'db> AnyDiagnostic<'db> {
let private = private.map(|id| Field { id, parent: variant.into() });
NoSuchField { field: expr_or_pat, private, variant }.into()
}
+ &InferenceDiagnostic::MismatchedArrayPatLen { pat, expected, found, has_rest } => {
+ let pat = pat_syntax(pat)?.map(Into::into);
+ MismatchedArrayPatLen { pat, expected, found, has_rest }.into()
+ }
+ InferenceDiagnostic::ExpectedArrayOrSlicePat { pat, found } => {
+ let pat = pat_syntax(*pat)?.map(Into::into);
+ ExpectedArrayOrSlicePat { pat, found: Type::new(db, def, found.as_ref()) }.into()
+ }
+ &InferenceDiagnostic::DuplicateField { field: expr, variant } => {
+ let expr_or_pat = match expr {
+ ExprOrPatId::ExprId(expr) => {
+ source_map.field_syntax(expr).map(AstPtr::wrap_left)
+ }
+ ExprOrPatId::PatId(pat) => source_map.pat_field_syntax(pat),
+ };
+ DuplicateField { field: expr_or_pat, variant: variant.into() }.into()
+ }
&InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
MismatchedArgCount { call_expr: expr_syntax(call_expr)?, expected, found }.into()
}
@@ -711,21 +869,21 @@ impl<'db> AnyDiagnostic<'db> {
let expr = expr_syntax(expr)?;
BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()
}
+ &InferenceDiagnostic::NonExhaustiveRecordExpr { expr } => {
+ NonExhaustiveRecordExpr { expr: expr_syntax(expr)? }.into()
+ }
+ &InferenceDiagnostic::FunctionalRecordUpdateOnNonStruct { base_expr } => {
+ FunctionalRecordUpdateOnNonStruct { base_expr: expr_syntax(base_expr)? }.into()
+ }
InferenceDiagnostic::TypedHole { expr, expected } => {
let expr = expr_syntax(*expr)?;
TypedHole { expr, expected: Type::new(db, def, expected.as_ref()) }.into()
}
&InferenceDiagnostic::MismatchedTupleStructPatArgCount { pat, expected, found } => {
- let expr_or_pat = match pat {
- ExprOrPatId::ExprId(expr) => expr_syntax(expr)?,
- ExprOrPatId::PatId(pat) => {
- let InFile { file_id, value } = pat_syntax(pat)?;
-
- // cast from Either<Pat, SelfParam> -> Either<_, Pat>
- let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?;
- InFile { file_id, value: ptr }
- }
- };
+ let InFile { file_id, value } = pat_syntax(pat)?;
+ // cast from Either<Pat, SelfParam> -> Either<_, Pat>
+ let ptr = AstPtr::try_from_raw(value.syntax_node_ptr())?;
+ let expr_or_pat = InFile { file_id, value: ptr };
MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into()
}
InferenceDiagnostic::CastToUnsized { expr, cast_ty } => {
@@ -801,6 +959,64 @@ impl<'db> AnyDiagnostic<'db> {
let expected_kind = GenericArgKind::from_id(param_id);
IncorrectGenericsOrder { provided_arg, expected_kind }.into()
}
+ &InferenceDiagnostic::InvalidLhsOfAssignment { lhs } => {
+ let lhs = expr_syntax(lhs)?;
+ InvalidLhsOfAssignment { lhs }.into()
+ }
+ &InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => {
+ let at_point = span_syntax(at_point)?;
+ let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() {
+ rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(Type {
+ ty,
+ env: crate::body_param_env_from_has_crate(db, def),
+ }),
+ // FIXME: Printing the const to string is definitely not the correct thing to do here.
+ rustc_type_ir::GenericArgKind::Const(konst) => Either::Right(
+ konst.display(db, DisplayTarget::from_crate(db, def.krate(db))).to_string(),
+ ),
+ rustc_type_ir::GenericArgKind::Lifetime(_) => {
+ unreachable!("we currently don't emit TypeMustBeKnown for lifetimes")
+ }
+ });
+ TypeMustBeKnown { at_point, top_term }.into()
+ }
+ &InferenceDiagnostic::UnionExprMustHaveExactlyOneField { expr } => {
+ let expr = expr_syntax(expr)?;
+ UnionExprMustHaveExactlyOneField { expr }.into()
+ }
+ InferenceDiagnostic::TypeMismatch { node, expected, found } => {
+ let expr_or_pat = expr_or_pat_syntax(*node)?;
+ TypeMismatch {
+ expr_or_pat,
+ expected: Type { env, ty: expected.as_ref() },
+ actual: Type { env, ty: found.as_ref() },
+ }
+ .into()
+ }
+ InferenceDiagnostic::SolverDiagnostic(d) => {
+ let span = span_syntax(d.span)?;
+ Self::solver_diagnostic(db, &d.kind, span, env)?
+ }
+ })
+ }
+
+ fn solver_diagnostic(
+ db: &'db dyn HirDatabase,
+ d: &'db SolverDiagnosticKind,
+ span: SpanSyntax,
+ env: ParamEnvAndCrate<'db>,
+ ) -> Option<AnyDiagnostic<'db>> {
+ let interner = DbInterner::new_no_crate(db);
+ Some(match d {
+ SolverDiagnosticKind::TraitUnimplemented { trait_predicate, root_trait_predicate } => {
+ let trait_predicate =
+ crate::TraitPredicate { inner: trait_predicate.get(interner), env };
+ let root_trait_predicate =
+ root_trait_predicate.as_ref().map(|root_trait_predicate| {
+ crate::TraitPredicate { inner: root_trait_predicate.get(interner), env }
+ });
+ UnimplementedTrait { span, trait_predicate, root_trait_predicate }.into()
+ }
})
}
@@ -894,6 +1110,11 @@ impl<'db> AnyDiagnostic<'db> {
}
.into()
}
+ PathLoweringDiagnostic::GenericDefaultRefersToSelf { segment } => {
+ let segment = hir_segment_to_ast_segment(&path.value, segment)?;
+ let segment = path.with_value(AstPtr::new(&segment));
+ GenericDefaultRefersToSelf { segment }.into()
+ }
})
}
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 53f24713cd..a71851ea8c 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -7,6 +7,7 @@ use hir_def::{
expr_store::{Body, ExpressionStore},
hir::generics::{GenericParams, TypeOrConstParamData, TypeParamProvenance, WherePredicate},
item_tree::FieldsShape,
+ layout::ExternAbi,
signatures::{
ConstSignature, FunctionSignature, ImplSignature, StaticFlags, StaticSignature, TraitFlags,
TraitSignature, TypeAliasSignature,
@@ -22,16 +23,16 @@ use hir_ty::{
hir_display_with_store, write_bounds_like_dyn_trait_with_prefix, write_params_bounds,
write_visibility,
},
- next_solver::ClauseKind,
+ next_solver::{ClauseKind, Unnormalized},
};
use itertools::Itertools;
-use rustc_type_ir::inherent::IntoKind;
+use rustc_type_ir::inherent::IntoKind as _;
use crate::{
Adt, AnyFunctionId, AsAssocItem, AssocItem, AssocItemContainer, Const, ConstParam, Crate, Enum,
EnumVariant, ExternCrateDecl, Field, Function, GenericParam, HasCrate, HasVisibility, Impl,
- LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitRef,
- TupleField, Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union,
+ LifetimeParam, Macro, Module, SelfParam, Static, Struct, StructKind, Trait, TraitPredicate,
+ TraitRef, TupleField, Type, TypeAlias, TypeNs, TypeOrConstParam, TypeParam, Union,
};
fn write_builtin_derive_impl_method<'db>(
@@ -163,13 +164,16 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re
if data.is_async() {
f.write_str("async ")?;
}
+ if data.is_gen() {
+ f.write_str("gen ")?;
+ }
// FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe
// (they are conditionally unsafe to call). We probably should show something else.
if func.is_unsafe_to_call(db, None, f.edition()) {
f.write_str("unsafe ")?;
}
- if let Some(abi) = &data.abi {
- write!(f, "extern \"{}\" ", abi.as_str())?;
+ if data.abi != ExternAbi::Rust {
+ write!(f, "extern \"{}\" ", data.abi.as_str())?;
}
write!(f, "fn {}", data.name.display(f.db, f.edition()))?;
@@ -200,7 +204,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re
first = false;
}
- let pat_id = body.params[param.idx - body.self_param.is_some() as usize];
+ let pat_id = body.params[param.idx - body.self_param().is_some() as usize];
let pat_str = body.pretty_print_pat(db, func_id.into(), pat_id, true, f.edition());
f.write_str(&pat_str)?;
@@ -223,7 +227,7 @@ fn write_function<'db>(f: &mut HirFormatter<'_, 'db>, func_id: FunctionId) -> Re
// `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
// Use ugly pattern match to strip the Future trait.
// Better way?
- let ret_type = if !data.is_async() {
+ let ret_type = if !data.is_async() && !data.is_gen() {
data.ret_type
} else if let Some(ret_type) = data.ret_type {
match &data.store[ret_type] {
@@ -579,6 +583,7 @@ impl<'db> HirDisplay<'db> for TypeParam {
let predicates = GenericPredicates::query_all(f.db, self.id.parent());
let predicates = predicates
.iter_identity()
+ .map(Unnormalized::skip_norm_wip)
.filter(|wc| match wc.kind().skip_binder() {
ClauseKind::Trait(tr) => tr.self_ty() == ty,
ClauseKind::Projection(proj) => proj.self_ty() == ty,
@@ -850,6 +855,12 @@ impl<'db> HirDisplay<'db> for TraitRef<'db> {
}
}
+impl<'db> HirDisplay<'db> for TraitPredicate<'db> {
+ fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
+ self.inner.hir_fmt(f)
+ }
+}
+
impl<'db> HirDisplay<'db> for Trait {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
// FIXME(trait-alias) needs special handling to print the equal sign
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index 0a48be5473..219eb9c3b9 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -254,14 +254,9 @@ impl TryFrom<AssocItem> for GenericDefId {
}
}
-impl From<(ExpressionStoreOwnerId, BindingId)> for Local {
- fn from((parent, binding_id): (ExpressionStoreOwnerId, BindingId)) -> Self {
- Local { parent, binding_id }
- }
-}
impl From<(DefWithBodyId, BindingId)> for Local {
fn from((parent, binding_id): (DefWithBodyId, BindingId)) -> Self {
- Local { parent: parent.into(), binding_id }
+ Local { parent: parent.into(), parent_infer: parent.into(), binding_id }
}
}
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index 45c9811cc0..9bff8bda3a 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -293,8 +293,9 @@ impl HasSource for Param<'_> {
.map(|value| InFile { file_id, value })
}
Callee::Closure(closure, _) => {
- let InternedClosure(owner, expr_id) = closure.loc(db);
- let (_, source_map) = ExpressionStore::with_source_map(db, owner);
+ let InternedClosure { owner, expr: expr_id, .. } = closure.loc(db);
+ let (_, source_map) =
+ ExpressionStore::with_source_map(db, owner.expression_store_owner(db));
let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?;
let root = db.parse_or_expand(file_id);
match value.to_node(&root) {
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index d24e2c0cb5..63b834a8d1 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -75,6 +75,7 @@ use hir_def::{
TypeAliasSignature, UnionSignature, VariantFields,
},
src::HasSource as _,
+ unstable_features::UnstableFeatures,
visibility::visibility_from_ast,
};
use hir_expand::{
@@ -82,16 +83,14 @@ use hir_expand::{
proc_macro::ProcMacroKind,
};
use hir_ty::{
- GenericPredicates, InferenceResult, ParamEnvAndCrate, TyDefId, TyLoweringDiagnostic,
- ValueTyDefId, all_super_traits, autoderef, check_orphan_rules,
+ GenericPredicates, InferBodyId, InferenceResult, ParamEnvAndCrate, TyDefId,
+ TyLoweringDiagnostic, ValueTyDefId, all_super_traits, autoderef, check_orphan_rules,
consteval::try_const_usize,
- db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId},
+ db::{AnonConstId, InternedClosure, InternedClosureId, InternedCoroutineClosureId},
diagnostics::BodyValidationDiagnostic,
direct_super_traits, known_const_to_ast,
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
- method_resolution::{
- self, InherentImpls, MethodResolutionContext, MethodResolutionUnstableFeatures,
- },
+ method_resolution::{self, InherentImpls, MethodResolutionContext},
mir::interpret_mir,
next_solver::{
AliasTy, AnyImplId, ClauseKind, ConstKind, DbInterner, EarlyBinder, EarlyParamRegion,
@@ -106,7 +105,7 @@ use rustc_hash::FxHashSet;
use rustc_type_ir::{
AliasTyKind, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
TypeVisitor, fast_reject,
- inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _},
+ inherent::{GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _},
};
use span::{AstIdNode, Edition, FileId};
use stdx::{format_to, impl_from, never};
@@ -115,7 +114,7 @@ use syntax::{
ast::{self, HasName as _, HasVisibility as _},
format_smolstr,
};
-use triomphe::{Arc, ThinArc};
+use triomphe::Arc;
use crate::db::{DefDatabase, HirDatabase};
@@ -174,7 +173,7 @@ pub use {
// FIXME: Properly encapsulate mir
hir_ty::mir,
hir_ty::{
- CastError, FnAbi, PointerCast, attach_db, attach_db_allow_change,
+ CastError, PointerCast, attach_db, attach_db_allow_change,
consteval::ConstEvalError,
diagnostics::UnsafetyReason,
display::{ClosureStyle, DisplayTarget, HirDisplay, HirDisplayError, HirWrite},
@@ -197,7 +196,7 @@ use {
hir_def::expr_store::path::Path,
hir_expand::{
name::AsName,
- span_map::{ExpansionSpanMap, RealSpanMap, SpanMap, SpanMapRef},
+ span_map::{ExpansionSpanMap, RealSpanMap, SpanMap},
},
};
@@ -343,7 +342,7 @@ impl Crate {
}
pub fn is_unstable_feature_enabled(self, db: &dyn HirDatabase, feature: &Symbol) -> bool {
- crate_def_map(db, self.id).is_unstable_feature_enabled(feature)
+ UnstableFeatures::query(db, self.id).is_enabled(feature)
}
}
@@ -760,7 +759,7 @@ impl Module {
push_ty_diagnostics(
db,
acc,
- db.field_types_with_diagnostics(s.id.into()).1.clone(),
+ db.field_types_with_diagnostics(s.id.into()).diagnostics(),
source_map,
);
}
@@ -772,7 +771,7 @@ impl Module {
push_ty_diagnostics(
db,
acc,
- db.field_types_with_diagnostics(u.id.into()).1.clone(),
+ db.field_types_with_diagnostics(u.id.into()).diagnostics(),
source_map,
);
}
@@ -802,7 +801,7 @@ impl Module {
push_ty_diagnostics(
db,
acc,
- db.field_types_with_diagnostics(v.into()).1.clone(),
+ db.field_types_with_diagnostics(v.into()).diagnostics(),
source_map,
);
expr_store_diagnostics(db, acc, source_map);
@@ -818,7 +817,7 @@ impl Module {
push_ty_diagnostics(
db,
acc,
- db.type_for_type_alias_with_diagnostics(type_alias.id).1,
+ db.type_for_type_alias_with_diagnostics(type_alias.id).diagnostics(),
source_map,
);
acc.extend(def.diagnostics(db, style_lints));
@@ -881,7 +880,6 @@ impl Module {
}
let drop_maybe_dangle = (|| {
- // FIXME: This can be simplified a lot by exposing hir-ty's utils.rs::Generics helper
let trait_ = trait_?;
let drop_trait = interner.lang_items().Drop?;
if drop_trait != trait_.into() {
@@ -948,7 +946,7 @@ impl Module {
.collect();
if !missing.is_empty() {
- let self_ty = db.impl_self_ty(impl_id).instantiate_identity();
+ let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip();
let self_ty = structurally_normalize_ty(
&infcx,
self_ty,
@@ -984,10 +982,8 @@ impl Module {
// relationship here would be significantly more expensive.
if !missing.is_empty() {
let krate = self.krate(db).id;
- let def_map = crate_def_map(db, krate);
- if def_map.is_unstable_feature_enabled(&sym::specialization)
- || def_map.is_unstable_feature_enabled(&sym::min_specialization)
- {
+ let features = UnstableFeatures::query(db, krate);
+ if features.specialization || features.min_specialization {
missing.retain(|(assoc_name, assoc_item)| {
let AssocItem::Function(_) = assoc_item else {
return true;
@@ -1031,11 +1027,19 @@ impl Module {
impl_assoc_items_scratch.clear();
}
- push_ty_diagnostics(db, acc, db.impl_self_ty_with_diagnostics(impl_id).1, source_map);
push_ty_diagnostics(
db,
acc,
- db.impl_trait_with_diagnostics(impl_id).and_then(|it| it.1),
+ db.impl_self_ty_with_diagnostics(impl_id).diagnostics(),
+ source_map,
+ );
+ push_ty_diagnostics(
+ db,
+ acc,
+ db.impl_trait_with_diagnostics(impl_id)
+ .as_ref()
+ .map(|it| it.diagnostics())
+ .unwrap_or_default(),
source_map,
);
@@ -1124,9 +1128,9 @@ fn macro_call_diagnostics<'db>(
let Some(e) = db.parse_macro_expansion_error(macro_call_id) else {
return;
};
- let ValueResult { value: parse_errors, err } = &*e;
+ let ValueResult { value: parse_errors, err } = e;
if let Some(err) = err {
- let loc = db.lookup_intern_macro_call(macro_call_id);
+ let loc = macro_call_id.loc(db);
let file_id = loc.kind.file_id();
let mut range = precise_macro_call_location(&loc.kind, db, loc.krate);
let RenderedExpandError { message, error, kind } = err.render_to_string(db);
@@ -1138,7 +1142,7 @@ fn macro_call_diagnostics<'db>(
}
if !parse_errors.is_empty() {
- let loc = db.lookup_intern_macro_call(macro_call_id);
+ let loc = macro_call_id.loc(db);
let range = precise_macro_call_location(&loc.kind, db, loc.krate);
acc.push(MacroExpansionParseError { range, errors: parse_errors.clone() }.into())
}
@@ -1341,14 +1345,14 @@ impl<'db> InstantiatedField<'db> {
let var_id = self.inner.parent.into();
let field = db.field_types(var_id)[self.inner.id].get();
- let ty = field.instantiate(interner, self.args);
+ let ty = field.instantiate(interner, self.args).skip_norm_wip();
TypeNs::new(db, var_id, ty)
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub struct TupleField {
- pub owner: ExpressionStoreOwnerId,
+ pub owner: InferBodyId,
pub tuple: TupleId,
pub index: u32,
}
@@ -1366,7 +1370,7 @@ impl TupleField {
.get(self.index as usize)
.copied()
.unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed));
- Type { env: body_param_env_from_has_crate(db, self.owner), ty }
+ Type { env: body_param_env_from_has_crate(db, self.owner.expression_store_owner(db)), ty }
}
}
@@ -1436,11 +1440,11 @@ impl Field {
};
let interner = DbInterner::new_no_crate(db);
let args = generic_args_from_tys(interner, def_id.into(), generics.map(|ty| ty.ty));
- let ty = db.field_types(var_id)[self.id].get().instantiate(interner, args);
+ let ty = db.field_types(var_id)[self.id].get().instantiate(interner, args).skip_norm_wip();
Type::new(db, var_id, ty)
}
- pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+ pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> {
db.layout_of_ty(
self.ty(db).ty.store(),
param_env_from_has_crate(
@@ -1529,7 +1533,7 @@ impl Struct {
}
pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedStruct<'db> {
- let args = infer_ctxt.fresh_args_for_item(self.id.into());
+ let args = infer_ctxt.fresh_args_for_item(hir_ty::Span::Dummy, self.id.into());
InstantiatedStruct { inner: self, args }
}
}
@@ -1566,7 +1570,7 @@ impl<'db> InstantiatedStruct<'db> {
let interner = DbInterner::new_no_crate(db);
let ty = db.ty(self.inner.id.into());
- TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args))
+ TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args).skip_norm_wip())
}
}
@@ -1700,7 +1704,7 @@ impl Enum {
self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit))
}
- pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+ pub fn layout<'db>(self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> {
Adt::from(self).layout(db)
}
@@ -1728,7 +1732,7 @@ impl<'db> InstantiatedEnum<'db> {
let interner = DbInterner::new_no_crate(db);
let ty = db.ty(self.inner.id.into());
- TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args))
+ TypeNs::new(db, self.inner.id, ty.instantiate(interner, self.args).skip_norm_wip())
}
}
@@ -1787,7 +1791,7 @@ impl EnumVariant {
db.const_eval_discriminant(self.into())
}
- pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+ pub fn layout<'db>(&self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> {
let parent_enum = self.parent_enum(db);
let parent_layout = parent_enum.layout(db)?;
Ok(match &parent_layout.0.variants {
@@ -1808,8 +1812,10 @@ impl EnumVariant {
}
pub fn instantiate_infer<'db>(self, infer_ctxt: &InferCtxt<'db>) -> InstantiatedVariant<'db> {
- let args =
- infer_ctxt.fresh_args_for_item(self.parent_enum(infer_ctxt.interner.db()).id.into());
+ let args = infer_ctxt.fresh_args_for_item(
+ hir_ty::Span::Dummy,
+ self.parent_enum(infer_ctxt.interner.db()).id.into(),
+ );
InstantiatedVariant { inner: self, args }
}
}
@@ -1868,7 +1874,7 @@ impl Adt {
has_non_default_type_params(db, self.into())
}
- pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
+ pub fn layout<'db>(self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> {
let interner = DbInterner::new_no_crate(db);
let adt_id = AdtId::from(self);
let args = GenericArgs::for_item_with_defaults(interner, adt_id.into(), |_, id, _| {
@@ -1988,6 +1994,47 @@ impl Variant {
Variant::EnumVariant(e) => (*e).name(db),
}
}
+
+ pub fn adt(&self, db: &dyn HirDatabase) -> Adt {
+ match *self {
+ Variant::Struct(it) => it.into(),
+ Variant::Union(it) => it.into(),
+ Variant::EnumVariant(it) => it.parent_enum(db).into(),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct AnonConst {
+ id: AnonConstId,
+}
+
+impl AnonConst {
+ pub fn owner(self, db: &dyn HirDatabase) -> ExpressionStoreOwner {
+ self.id.loc(db).owner.into()
+ }
+
+ pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> {
+ let loc = self.id.loc(db);
+ let env = body_param_env_from_has_crate(db, loc.owner);
+ Type { env, ty: loc.ty.get().instantiate_identity().skip_norm_wip() }
+ }
+
+ pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst<'_>, ConstEvalError> {
+ let interner = DbInterner::new_no_crate(db);
+ let ty = self.id.loc(db).ty.get().instantiate_identity().skip_norm_wip();
+ db.anon_const_eval(self.id, GenericArgs::empty(interner), None).map(|it| EvaluatedConst {
+ allocation: it,
+ def: self.id.into(),
+ ty,
+ })
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum InferBody {
+ Body(DefWithBody),
+ AnonConst(AnonConst),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -2084,6 +2131,12 @@ impl DefWithBody {
})
}
+ #[deprecated = "you should really not use this, this is exported for analysis-stats only"]
+ pub fn run_mir_body(self, db: &dyn HirDatabase) -> Result<(), MirLowerError> {
+ let Some(id) = self.id() else { return Ok(()) };
+ db.mir_body(id.into()).map(drop)
+ }
+
/// A textual representation of the HIR of this def's body for debugging purposes.
pub fn debug_hir(self, db: &dyn HirDatabase) -> String {
let Some(id) = self.id() else {
@@ -2098,7 +2151,7 @@ impl DefWithBody {
let Some(id) = self.id() else {
return String::new();
};
- let body = db.mir_body(id);
+ let body = db.mir_body(id.into());
match body {
Ok(body) => body.pretty_print(db, self.module(db).krate(db).to_display_target(db)),
Err(e) => format!("error:\n{e:?}"),
@@ -2115,6 +2168,7 @@ impl DefWithBody {
return;
};
let krate = self.module(db).id.krate(db);
+ let env = body_param_env_from_has_crate(db, id);
let (body, source_map) = Body::with_source_map(db, id);
let sig_source_map = match self {
@@ -2138,34 +2192,14 @@ impl DefWithBody {
let infer = InferenceResult::of(db, id);
for d in infer.diagnostics() {
- acc.extend(AnyDiagnostic::inference_diagnostic(db, id, d, source_map, sig_source_map));
- }
-
- 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),
- ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right),
- };
- let expr_or_pat = match expr_or_pat {
- Ok(Either::Left(expr)) => expr,
- Ok(Either::Right(InFile { file_id, value: pat })) => {
- // cast from Either<Pat, SelfParam> -> Either<_, Pat>
- let Some(ptr) = AstPtr::try_from_raw(pat.syntax_node_ptr()) else {
- continue;
- };
- InFile { file_id, value: ptr }
- }
- Err(SyntheticSyntax) => continue,
- };
-
- acc.push(
- TypeMismatch {
- expr_or_pat,
- expected: Type::new(db, id, mismatch.expected.as_ref()),
- actual: Type::new(db, id, mismatch.actual.as_ref()),
- }
- .into(),
- );
+ acc.extend(AnyDiagnostic::inference_diagnostic(
+ db,
+ id,
+ d,
+ source_map,
+ sig_source_map,
+ env,
+ ));
}
let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, id);
@@ -2203,9 +2237,9 @@ impl DefWithBody {
}
}
- if let Ok(borrowck_results) = db.borrowck(id) {
+ if let Ok(borrowck_results) = db.borrowck(id.into()) {
for borrowck_result in borrowck_results.iter() {
- let mir_body = &borrowck_result.mir_body;
+ let mir_body = borrowck_result.mir_body(db);
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) {
@@ -2260,7 +2294,8 @@ impl DefWithBody {
{
need_mut = &mir::MutabilityReason::Not;
}
- let local = Local { parent: id.into(), binding_id };
+ let local =
+ Local { parent: id.into(), parent_infer: mir_body.owner, binding_id };
let is_mut = body[binding_id].mode == BindingAnnotation::Mutable;
match (need_mut, is_mut) {
@@ -2382,6 +2417,9 @@ fn expr_store_diagnostics<'db>(
ExpressionStoreDiagnostics::UndeclaredLabel { node, name } => {
UndeclaredLabel { node: *node, name: name.clone() }.into()
}
+ ExpressionStoreDiagnostics::PatternArgInExternFn { node } => {
+ PatternArgInExternFn { node: *node }.into()
+ }
});
}
@@ -2448,7 +2486,8 @@ impl Function {
let resolver = id.resolver(db);
let interner = DbInterner::new_no_crate(db);
// FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s.
- let callable_sig = db.callable_item_signature(id.into()).instantiate_identity();
+ let callable_sig =
+ db.callable_item_signature(id.into()).instantiate_identity().skip_norm_wip();
let ty = Ty::new_fn_ptr(interner, callable_sig);
Type::new_with_resolver_inner(db, &resolver, ty)
}
@@ -2525,10 +2564,13 @@ impl Function {
// `impl_generics_len - impl_trait_ref.args.len()`.
let trait_method_fn_ptr = Ty::new_fn_ptr(
interner,
- db.callable_item_signature(trait_method.into()).instantiate_identity(),
+ db.callable_item_signature(trait_method.into())
+ .instantiate_identity()
+ .skip_norm_wip(),
);
- let impl_trait_ref =
- hir_ty::builtin_derive::impl_trait(interner, impl_).instantiate_identity();
+ let impl_trait_ref = hir_ty::builtin_derive::impl_trait(interner, impl_)
+ .instantiate_identity()
+ .skip_norm_wip();
let trait_method_args =
GenericArgs::identity_for_item(interner, trait_method.into());
let trait_method_own_args = GenericArgs::new_from_iter(
@@ -2543,8 +2585,9 @@ impl Function {
interner,
impl_trait_ref.args.iter().chain(shifted_trait_method_own_args),
);
- let impl_method_fn_ptr =
- EarlyBinder::bind(trait_method_fn_ptr).instantiate(interner, impl_method_args);
+ let impl_method_fn_ptr = EarlyBinder::bind(trait_method_fn_ptr)
+ .instantiate(interner, impl_method_args)
+ .skip_norm_wip();
Type { env, ty: impl_method_fn_ptr }
}
}
@@ -2575,7 +2618,7 @@ impl Function {
let ret_type = self.ret_type(db);
let interner = DbInterner::new_no_crate(db);
let args = self.adapt_generic_args(interner, generics);
- ret_type.derived(EarlyBinder::bind(ret_type.ty).instantiate(interner, args))
+ ret_type.derived(EarlyBinder::bind(ret_type.ty).instantiate(interner, args).skip_norm_wip())
}
fn adapt_generic_args<'db>(
@@ -2690,7 +2733,7 @@ impl Function {
idx: param.idx,
ty: Type {
env: param.ty.env,
- ty: EarlyBinder::bind(param.ty.ty).instantiate(interner, args),
+ ty: EarlyBinder::bind(param.ty.ty).instantiate(interner, args).skip_norm_wip(),
},
})
.collect()
@@ -2957,25 +3000,38 @@ impl<'db> Param<'db> {
Callee::Def(CallableDefId::FunctionId(it)) => {
let parent = DefWithBodyId::FunctionId(it);
let body = Body::of(db, parent);
- if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
- Some(Local { parent: parent.into(), binding_id: self_param })
+ if let Some(self_param) = body.self_param().filter(|_| self.idx == 0) {
+ Some(Local {
+ parent: parent.into(),
+ parent_infer: parent.into(),
+ binding_id: self_param,
+ })
} else if let Pat::Bind { id, .. } =
- &body[body.params[self.idx - body.self_param.is_some() as usize]]
+ &body[body.params[self.idx - body.self_param().is_some() as usize]]
{
- Some(Local { parent: parent.into(), binding_id: *id })
+ Some(Local {
+ parent: parent.into(),
+ parent_infer: parent.into(),
+ binding_id: *id,
+ })
} else {
None
}
}
Callee::Closure(closure, _) => {
let c = closure.loc(db);
- let body_owner = c.0;
- let store = ExpressionStore::of(db, c.0);
+ let body_infer_owner = c.owner;
+ let body_owner = c.owner.expression_store_owner(db);
+ let store = ExpressionStore::of(db, body_owner);
- if let Expr::Closure { args, .. } = &store[c.1]
+ if let Expr::Closure { args, .. } = &store[c.expr]
&& let Pat::Bind { id, .. } = &store[args[self.idx]]
{
- return Some(Local { parent: body_owner, binding_id: *id });
+ return Some(Local {
+ parent: body_owner,
+ parent_infer: body_infer_owner,
+ binding_id: *id,
+ });
}
None
}
@@ -3042,7 +3098,7 @@ impl SelfParam {
let interner = DbInterner::new_no_crate(db);
let args = self.func.adapt_generic_args(interner, generics);
let Type { env, ty } = self.ty(db);
- Type { env, ty: EarlyBinder::bind(ty).instantiate(interner, args) }
+ Type { env, ty: EarlyBinder::bind(ty).instantiate(interner, args).skip_norm_wip() }
}
}
@@ -3140,7 +3196,7 @@ impl Const {
/// Evaluate the constant.
pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst<'_>, ConstEvalError> {
let interner = DbInterner::new_no_crate(db);
- let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity();
+ let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity().skip_norm_wip();
db.const_eval(self.id, GenericArgs::empty(interner), None).map(|it| EvaluatedConst {
allocation: it,
def: self.id.into(),
@@ -3156,7 +3212,7 @@ impl HasVisibility for Const {
}
pub struct EvaluatedConst<'db> {
- def: DefWithBodyId,
+ def: InferBodyId,
allocation: hir_ty::next_solver::Allocation<'db>,
ty: Ty<'db>,
}
@@ -3220,7 +3276,7 @@ impl Static {
/// Evaluate the static initializer.
pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst<'_>, ConstEvalError> {
- let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity();
+ let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity().skip_norm_wip();
db.const_eval_static(self.id).map(|it| EvaluatedConst {
allocation: it,
def: self.id.into(),
@@ -4018,7 +4074,7 @@ impl AssocItem {
push_ty_diagnostics(
db,
acc,
- db.type_for_type_alias_with_diagnostics(type_alias.id).1,
+ db.type_for_type_alias_with_diagnostics(type_alias.id).diagnostics(),
&TypeAliasSignature::with_source_map(db, type_alias.id).1,
);
for diag in hir_ty::diagnostics::incorrect_case(db, type_alias.id.into()) {
@@ -4182,26 +4238,24 @@ impl GenericDef {
};
expr_store_diagnostics(db, acc, source_map);
- push_ty_diagnostics(db, acc, db.generic_defaults_with_diagnostics(def).1, source_map);
push_ty_diagnostics(
db,
acc,
- GenericPredicates::query_with_diagnostics(db, def).1.clone(),
+ db.generic_defaults_with_diagnostics(def).diagnostics(),
+ source_map,
+ );
+ push_ty_diagnostics(
+ db,
+ acc,
+ GenericPredicates::query_with_diagnostics(db, def).diagnostics(),
+ source_map,
+ );
+ push_ty_diagnostics(
+ db,
+ acc,
+ db.const_param_types_with_diagnostics(def).diagnostics(),
source_map,
);
- for (param_id, param) in generics.iter_type_or_consts() {
- if let TypeOrConstParamData::ConstParamData(_) = param {
- push_ty_diagnostics(
- db,
- acc,
- db.const_param_ty_with_diagnostics(ConstParamId::from_unchecked(
- TypeOrConstParamId { parent: def, local_id: param_id },
- ))
- .1,
- source_map,
- );
- }
- }
}
/// Returns a string describing the kind of this type.
@@ -4296,6 +4350,7 @@ impl<'db> GenericSubstitution<'db> {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Local {
pub(crate) parent: ExpressionStoreOwnerId,
+ pub(crate) parent_infer: InferBodyId,
pub(crate) binding_id: BindingId,
}
@@ -4397,7 +4452,7 @@ impl Local {
pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
let def = self.parent;
- let infer = InferenceResult::of(db, def);
+ let infer = InferenceResult::of(db, self.parent_infer);
let ty = infer.binding_ty(self.binding_id);
Type::new(db, def, ty)
}
@@ -4411,8 +4466,8 @@ impl Local {
}
ExpressionStoreOwnerId::Body(def_with_body_id) => {
b = Body::with_source_map(db, def_with_body_id);
- if let Some((param, source)) = b.0.self_param.zip(b.1.self_param_syntax())
- && param == self.binding_id
+ if b.0.self_params.contains(&self.binding_id)
+ && let Some(source) = b.1.self_param_syntax()
{
let root = source.file_syntax(db);
return vec![LocalSource {
@@ -4452,8 +4507,8 @@ impl Local {
}
ExpressionStoreOwnerId::Body(def_with_body_id) => {
b = Body::with_source_map(db, def_with_body_id);
- if let Some((param, source)) = b.0.self_param.zip(b.1.self_param_syntax())
- && param == self.binding_id
+ if b.0.self_params.contains(&self.binding_id)
+ && let Some(source) = b.1.self_param_syntax()
{
let root = source.file_syntax(db);
return LocalSource {
@@ -4624,13 +4679,12 @@ impl GenericParam {
GenericParam::ConstParam(_) => return None,
GenericParam::LifetimeParam(it) => it.id.parent,
};
- let generics = hir_ty::generics::generics(db, parent);
let index = match self {
- GenericParam::TypeParam(it) => generics.type_or_const_param_idx(it.id.into())?,
+ GenericParam::TypeParam(it) => hir_ty::type_or_const_param_idx(db, it.id.into()),
GenericParam::ConstParam(_) => return None,
- GenericParam::LifetimeParam(it) => generics.lifetime_idx(it.id)?,
+ GenericParam::LifetimeParam(it) => hir_ty::lifetime_param_idx(db, it.id),
};
- db.variances_of(parent).get(index).map(Into::into)
+ db.variances_of(parent).get(index as usize).map(Into::into)
}
}
@@ -4702,8 +4756,8 @@ impl TypeParam {
pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
let resolver = self.id.parent().resolver(db);
let interner = DbInterner::new_no_crate(db);
- let index = hir_ty::param_idx(db, self.id.into()).unwrap();
- let ty = Ty::new_param(interner, self.id, index as u32);
+ let index = hir_ty::type_or_const_param_idx(db, self.id.into());
+ let ty = Ty::new_param(interner, self.id, index);
Type::new_with_resolver_inner(db, &resolver, ty)
}
@@ -4789,25 +4843,30 @@ impl ConstParam {
}
pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
- Type::new(db, self.id.parent(), db.const_param_ty_ns(self.id))
+ Type::new(db, self.id.parent(), db.const_param_ty(self.id))
+ }
+
+ pub fn default(self, db: &dyn HirDatabase, display_target: DisplayTarget) -> Option<String> {
+ let arg = generic_arg_from_param(db, self.id.into())?;
+ Some(arg.display(db, display_target).to_string())
}
- pub fn default(
+ pub fn default_source_code(
self,
db: &dyn HirDatabase,
- display_target: DisplayTarget,
+ target_module: Module,
) -> Option<ast::ConstArg> {
let arg = generic_arg_from_param(db, self.id.into())?;
- known_const_to_ast(arg.konst()?, db, display_target)
+ known_const_to_ast(arg.konst()?, db, target_module.id)
}
}
fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<GenericArg<'_>> {
- let local_idx = hir_ty::param_idx(db, id)?;
+ let local_idx = hir_ty::type_or_const_param_idx(db, id);
let defaults = db.generic_defaults(id.parent);
- let ty = defaults.get(local_idx)?;
+ let ty = defaults.get(local_idx as usize)?;
// FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s.
- Some(ty.instantiate_identity())
+ Some(ty.instantiate_identity().skip_norm_wip())
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -4982,7 +5041,7 @@ impl Impl {
pub fn trait_ref(self, db: &dyn HirDatabase) -> Option<TraitRef<'_>> {
match self.id {
AnyImplId::ImplId(id) => {
- let trait_ref = db.impl_trait(id)?.instantiate_identity();
+ let trait_ref = db.impl_trait(id)?.instantiate_identity().skip_norm_wip();
let resolver = id.resolver(db);
Some(TraitRef::new_with_resolver(db, &resolver, trait_ref))
}
@@ -4994,8 +5053,9 @@ impl Impl {
param_env: hir_ty::builtin_derive::param_env(interner, id),
krate,
};
- let trait_ref =
- hir_ty::builtin_derive::impl_trait(interner, id).instantiate_identity();
+ let trait_ref = hir_ty::builtin_derive::impl_trait(interner, id)
+ .instantiate_identity()
+ .skip_norm_wip();
Some(TraitRef { env, trait_ref })
}
}
@@ -5006,7 +5066,7 @@ impl Impl {
AnyImplId::ImplId(id) => {
let resolver = id.resolver(db);
// FIXME: This shouldn't be `instantiate_identity()`, we shouldn't leak `TyKind::Param`s.
- let ty = db.impl_self_ty(id).instantiate_identity();
+ let ty = db.impl_self_ty(id).instantiate_identity().skip_norm_wip();
Type::new_with_resolver_inner(db, &resolver, ty)
}
AnyImplId::BuiltinDeriveImplId(id) => {
@@ -5019,6 +5079,7 @@ impl Impl {
};
let ty = hir_ty::builtin_derive::impl_trait(interner, id)
.instantiate_identity()
+ .skip_norm_wip()
.self_ty();
Type { env, ty }
}
@@ -5159,14 +5220,15 @@ impl<'db> Closure<'db> {
AnyClosureId::ClosureId(it) => it.loc(db),
AnyClosureId::CoroutineClosureId(it) => it.loc(db),
};
- let InternedClosure(owner, closure) = closure;
- let infer = InferenceResult::of(db, owner);
+ let InternedClosure { owner: infer_owner, expr: closure, .. } = closure;
+ let infer = InferenceResult::of(db, infer_owner);
+ let owner = infer_owner.expression_store_owner(db);
let param_env = body_param_env_from_has_crate(db, owner);
infer.closures_data[&closure]
.min_captures
.values()
.flatten()
- .map(|capture| ClosureCapture { owner, closure, capture, param_env })
+ .map(|capture| ClosureCapture { owner, infer_owner, closure, capture, param_env })
.collect()
}
@@ -5254,6 +5316,7 @@ impl FnTrait {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ClosureCapture<'db> {
owner: ExpressionStoreOwnerId,
+ infer_owner: InferBodyId,
closure: ExprId,
capture: &'db hir_ty::closure_analysis::CapturedPlace,
param_env: ParamEnvAndCrate<'db>,
@@ -5261,7 +5324,11 @@ pub struct ClosureCapture<'db> {
impl<'db> ClosureCapture<'db> {
pub fn local(&self) -> Local {
- Local { parent: self.owner, binding_id: self.capture.captured_local() }
+ Local {
+ parent: self.owner,
+ parent_infer: self.infer_owner,
+ binding_id: self.capture.captured_local(),
+ }
}
/// Returns whether this place has any field (aka. non-deref) projections.
@@ -5304,7 +5371,7 @@ impl<'db> ClosureCapture<'db> {
match ty.kind() {
TyKind::Tuple(_) => format_to!(result, "_{field_idx}"),
TyKind::Adt(adt_def, _) => {
- let variant = match adt_def.def_id().0 {
+ let variant = match adt_def.def_id() {
AdtId::StructId(id) => VariantId::from(id),
AdtId::UnionId(id) => id.into(),
AdtId::EnumId(id) => {
@@ -5338,7 +5405,7 @@ impl<'db> ClosureCapture<'db> {
match ty.kind() {
TyKind::Tuple(_) => format_to!(result, ".{field_idx}"),
TyKind::Adt(adt_def, _) => {
- let variant = match adt_def.def_id().0 {
+ let variant = match adt_def.def_id() {
AdtId::StructId(id) => VariantId::from(id),
AdtId::UnionId(id) => id.into(),
AdtId::EnumId(id) => {
@@ -5488,13 +5555,13 @@ impl<'db> Type<'db> {
}
};
let args = GenericArgs::error_for_item(interner, def.into());
- Type::new(db, def, ty.instantiate(interner, args))
+ Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip())
}
// FIXME: We shouldn't leak `TyKind::Param`s.
fn from_def_params(db: &'db dyn HirDatabase, def: impl Into<TyDefId> + HasResolver) -> Self {
let ty = db.ty(def.into());
- Type::new(db, def, ty.instantiate_identity())
+ Type::new(db, def, ty.instantiate_identity().skip_norm_wip())
}
fn from_value_def(
@@ -5518,7 +5585,7 @@ impl<'db> Type<'db> {
}
};
let args = GenericArgs::error_for_item(interner, def.into());
- Type::new(db, def, ty.instantiate(interner, args))
+ Type::new(db, def, ty.instantiate(interner, args).skip_norm_wip())
}
pub fn new_slice(ty: Self) -> Self {
@@ -5584,7 +5651,7 @@ impl<'db> Type<'db> {
// For non-phantom_data adts we check variants/fields as well as generic parameters
TyKind::Adt(adt_def, args)
- if !is_phantom_data(self.interner.db(), adt_def.def_id().0) =>
+ if !is_phantom_data(self.interner.db(), adt_def.def_id()) =>
{
let _variant_id_to_fields = |id: VariantId| {
let variant_data = &id.fields(self.interner.db());
@@ -5596,7 +5663,10 @@ impl<'db> Type<'db> {
.fields()
.iter()
.map(|(idx, _)| {
- field_types[idx].get().instantiate(self.interner, args)
+ field_types[idx]
+ .get()
+ .instantiate(self.interner, args)
+ .skip_norm_wip()
})
.filter(|it| !it.references_non_lt_error())
.collect()
@@ -5604,7 +5674,7 @@ impl<'db> Type<'db> {
};
let variant_id_to_fields = |_: VariantId| vec![];
- let variants: Vec<Vec<Ty<'db>>> = match adt_def.def_id().0 {
+ let variants: Vec<Vec<Ty<'db>>> = match adt_def.def_id() {
AdtId::StructId(id) => {
vec![variant_id_to_fields(id.into())]
}
@@ -5710,21 +5780,15 @@ impl<'db> Type<'db> {
/// This function is used in `.await` syntax completion.
pub fn into_future_output(&self, db: &'db dyn HirDatabase) -> Option<Type<'db>> {
let lang_items = hir_def::lang_item::lang_items(db, self.env.krate);
- let trait_ = lang_items
- .IntoFutureIntoFuture
- .and_then(|into_future_fn| {
- let assoc_item = as_assoc_item(db, AssocItem::Function, into_future_fn)?;
- let into_future_trait = assoc_item.container_or_implemented_trait(db)?;
- Some(into_future_trait.id)
- })
- .or(lang_items.Future)?;
+ let (trait_, output_assoc_type) = lang_items
+ .IntoFuture
+ .zip(lang_items.IntoFutureOutput)
+ .or(lang_items.Future.zip(lang_items.FutureOutput))?;
if !traits::implements_trait_unique(self.ty, db, self.env, trait_) {
return None;
}
- let output_assoc_type =
- trait_.trait_items(db).associated_type_by_name(&Name::new_symbol_root(sym::Output))?;
self.normalize_trait_assoc_type(db, &[], output_assoc_type.into())
}
@@ -5738,10 +5802,7 @@ impl<'db> Type<'db> {
/// This does **not** resolve `IntoIterator`, only `Iterator`.
pub fn iterator_item(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> {
let lang_items = hir_def::lang_item::lang_items(db, self.env.krate);
- let iterator_trait = lang_items.Iterator?;
- let iterator_item = iterator_trait
- .trait_items(db)
- .associated_type_by_name(&Name::new_symbol_root(sym::Item))?;
+ let iterator_item = lang_items.IteratorItem?;
self.normalize_trait_assoc_type(db, &[], iterator_item.into())
}
@@ -5756,19 +5817,13 @@ impl<'db> Type<'db> {
/// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type
pub fn into_iterator_iter(self, db: &'db dyn HirDatabase) -> Option<Type<'db>> {
let lang_items = hir_def::lang_item::lang_items(db, self.env.krate);
- let trait_ = lang_items.IntoIterIntoIter.and_then(|into_iter_fn| {
- let assoc_item = as_assoc_item(db, AssocItem::Function, into_iter_fn)?;
- let into_iter_trait = assoc_item.container_or_implemented_trait(db)?;
- Some(into_iter_trait.id)
- })?;
+ let trait_ = lang_items.IntoIterator?;
if !traits::implements_trait_unique(self.ty, db, self.env, trait_) {
return None;
}
- let into_iter_assoc_type = trait_
- .trait_items(db)
- .associated_type_by_name(&Name::new_symbol_root(sym::IntoIter))?;
+ let into_iter_assoc_type = lang_items.IntoIterIntoIterType?;
self.normalize_trait_assoc_type(db, &[], into_iter_assoc_type.into())
}
@@ -5882,7 +5937,7 @@ impl<'db> Type<'db> {
pub fn is_packed(&self, db: &'db dyn HirDatabase) -> bool {
let adt_id = match self.ty.kind() {
- TyKind::Adt(adt_def, ..) => adt_def.def_id().0,
+ TyKind::Adt(adt_def, ..) => adt_def.def_id(),
_ => return false,
};
@@ -5921,7 +5976,7 @@ impl<'db> Type<'db> {
let interner = DbInterner::new_no_crate(db);
let (variant_id, substs) = match self.ty.kind() {
TyKind::Adt(adt_def, substs) => {
- let id = match adt_def.def_id().0 {
+ let id = match adt_def.def_id() {
AdtId::StructId(id) => id.into(),
AdtId::UnionId(id) => id.into(),
AdtId::EnumId(_) => return Vec::new(),
@@ -5935,7 +5990,7 @@ impl<'db> Type<'db> {
.iter()
.map(|(local_id, ty)| {
let def = Field { parent: variant_id.into(), id: local_id };
- let ty = ty.get().instantiate(interner, substs);
+ let ty = ty.get().instantiate(interner, substs).skip_norm_wip();
(def, self.derived(ty))
})
.collect()
@@ -6178,8 +6233,7 @@ impl<'db> Type<'db> {
TypingMode::non_body_analysis()
};
let infcx = interner.infer_ctxt().build(typing_mode);
- let unstable_features =
- MethodResolutionUnstableFeatures::from_def_map(resolver.top_level_def_map());
+ let features = resolver.top_level_def_map().features();
let environment = param_env_from_resolver(db, resolver);
let ctx = MethodResolutionContext {
infcx: &infcx,
@@ -6187,7 +6241,9 @@ impl<'db> Type<'db> {
param_env: environment.param_env,
traits_in_scope,
edition: resolver.krate().data(db).edition,
- unstable_features: &unstable_features,
+ features,
+ call_span: hir_ty::Span::Dummy,
+ receiver_span: hir_ty::Span::Dummy,
};
f(&ctx)
}
@@ -6214,7 +6270,7 @@ impl<'db> Type<'db> {
self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| {
// There should be no inference vars in types passed here
let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty);
- let (self_ty, _) = ctx.infcx.instantiate_canonical(&canonical);
+ let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical);
match name {
Some(name) => {
@@ -6322,7 +6378,7 @@ impl<'db> Type<'db> {
self.with_method_resolution(db, scope.resolver(), traits_in_scope, |ctx| {
// There should be no inference vars in types passed here
let canonical = hir_ty::replace_errors_with_variables(ctx.infcx.interner, &self.ty);
- let (self_ty, _) = ctx.infcx.instantiate_canonical(&canonical);
+ let (self_ty, _) = ctx.infcx.instantiate_canonical(hir_ty::Span::Dummy, &canonical);
match name {
Some(name) => {
@@ -6437,7 +6493,7 @@ impl<'db> Type<'db> {
else {
return None;
};
- match def_id.expect_type_alias().loc(db).container {
+ match def_id.0.loc(db).container {
ItemContainerId::TraitId(id) => Some(Trait { id }),
_ => None,
}
@@ -6521,7 +6577,7 @@ impl<'db> Type<'db> {
.collect()
}
- pub fn layout(&self, db: &'db dyn HirDatabase) -> Result<Layout, LayoutError> {
+ pub fn layout(&self, db: &'db dyn HirDatabase) -> Result<Layout<'db>, LayoutError> {
db.layout_of_ty(self.ty.store(), self.env.store())
.map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap()))
}
@@ -6558,21 +6614,13 @@ impl<'db> TypeNs<'db> {
);
let trait_ref =
hir_ty::next_solver::TraitRef::new_from_args(infcx.interner, trait_.id.into(), args);
-
- let pred_kind = rustc_type_ir::Binder::dummy(rustc_type_ir::PredicateKind::Clause(
- rustc_type_ir::ClauseKind::Trait(rustc_type_ir::TraitPredicate {
- trait_ref,
- polarity: rustc_type_ir::PredicatePolarity::Positive,
- }),
- ));
- let predicate = hir_ty::next_solver::Predicate::new(infcx.interner, pred_kind);
- let goal = hir_ty::next_solver::Goal::new(
+ let obligation = hir_ty::next_solver::infer::traits::Obligation::new(
infcx.interner,
- hir_ty::next_solver::ParamEnv::empty(),
- predicate,
+ hir_ty::next_solver::infer::traits::ObligationCause::dummy(),
+ self.env.param_env,
+ trait_ref,
);
- let res = hir_ty::traits::next_trait_solve_in_ctxt(&infcx, goal);
- res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes))
+ infcx.predicate_must_hold_modulo_regions(&obligation)
}
pub fn is_bool(&self) -> bool {
@@ -6701,9 +6749,9 @@ impl<'db> Callable<'db> {
}
#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct Layout(Arc<TyLayout>, Arc<TargetDataLayout>);
+pub struct Layout<'db>(Arc<TyLayout>, &'db TargetDataLayout);
-impl Layout {
+impl<'db> Layout<'db> {
pub fn size(&self) -> u64 {
self.0.size.bytes()
}
@@ -6713,7 +6761,7 @@ impl Layout {
}
pub fn niches(&self) -> Option<u128> {
- Some(self.0.largest_niche?.available(&*self.1))
+ Some(self.0.largest_niche?.available(self.1))
}
pub fn field_offset(&self, field: Field) -> Option<u64> {
@@ -6802,7 +6850,7 @@ impl Layout {
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::Direct => tag.size(self.1).bytes_usize(),
TagEncoding::Niche { .. } => 0,
}
} else {
@@ -6934,6 +6982,33 @@ pub trait HasVisibility {
}
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum PredicatePolarity {
+ /// `T: Trait`
+ Positive,
+ /// `T: !Trait`
+ Negative,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct TraitPredicate<'db> {
+ inner: hir_ty::next_solver::TraitPredicate<'db>,
+ env: ParamEnvAndCrate<'db>,
+}
+
+impl<'db> TraitPredicate<'db> {
+ pub fn polarity(&self) -> PredicatePolarity {
+ match self.inner.polarity {
+ rustc_type_ir::PredicatePolarity::Positive => PredicatePolarity::Positive,
+ rustc_type_ir::PredicatePolarity::Negative => PredicatePolarity::Negative,
+ }
+ }
+
+ pub fn trait_ref(&self) -> TraitRef<'db> {
+ TraitRef { env: self.env, trait_ref: self.inner.trait_ref }
+ }
+}
+
/// Trait for obtaining the defining crate of an item.
pub trait HasCrate {
fn krate(&self, db: &dyn HirDatabase) -> Crate;
@@ -7041,6 +7116,12 @@ impl HasCrate for Module {
}
}
+impl HasCrate for AnonConst {
+ fn krate(&self, db: &dyn HirDatabase) -> Crate {
+ hir_def::HasModule::krate(&self.id.loc(db).owner, db).into()
+ }
+}
+
pub trait HasContainer {
fn container(&self, db: &dyn HirDatabase) -> ItemContainer;
}
@@ -7217,17 +7298,14 @@ pub enum DocLinkDef {
fn push_ty_diagnostics<'db>(
db: &'db dyn HirDatabase,
acc: &mut Vec<AnyDiagnostic<'db>>,
- diagnostics: Option<ThinArc<(), TyLoweringDiagnostic>>,
+ diagnostics: &[TyLoweringDiagnostic],
source_map: &ExpressionStoreSourceMap,
) {
- if let Some(diagnostics) = diagnostics {
- acc.extend(
- diagnostics
- .slice
- .iter()
- .filter_map(|diagnostic| AnyDiagnostic::ty_diagnostic(diagnostic, source_map, db)),
- );
- }
+ acc.extend(
+ diagnostics
+ .iter()
+ .filter_map(|diagnostic| AnyDiagnostic::ty_diagnostic(diagnostic, source_map, db)),
+ );
}
pub trait MethodCandidateCallback {
@@ -7342,10 +7420,8 @@ fn has_non_default_type_params(db: &dyn HirDatabase, generic_def: GenericDefId)
.filter(|(_, param)| matches!(param, TypeOrConstParamData::TypeParamData(_)))
.map(|(local_id, _)| TypeOrConstParamId { parent: generic_def, local_id })
.any(|param| {
- let Some(param) = hir_ty::param_idx(db, param) else {
- return false;
- };
- defaults.get(param).is_none()
+ let param = hir_ty::type_or_const_param_idx(db, param);
+ defaults.get(param as usize).is_none()
})
}
@@ -7379,5 +7455,16 @@ fn empty_param_env<'db>(krate: base_db::Crate) -> ParamEnvAndCrate<'db> {
ParamEnvAndCrate { param_env: ParamEnv::empty(), krate }
}
+// FIXME: We probably don't want to expose this.
+pub trait MacroCallIdExt {
+ fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc;
+}
+impl MacroCallIdExt for span::MacroCallId {
+ #[inline]
+ fn loc(self, db: &dyn HirDatabase) -> hir_expand::MacroCallLoc {
+ hir_expand::MacroCallId::from(self).loc(db)
+ }
+}
+
pub use hir_ty::next_solver;
pub use hir_ty::setup_tracing;
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index b7cc780ae4..a1bbe47188 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -13,11 +13,11 @@ use std::{
use base_db::{FxIndexSet, all_crates, toolchain_channel};
use either::Either;
use hir_def::{
- BuiltinDeriveImplId, DefWithBodyId, ExpressionStoreOwnerId, HasModule, MacroId, StructId,
- TraitId, VariantId,
+ BuiltinDeriveImplId, DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, HasModule, MacroId,
+ StructId, TraitId, VariantId,
attrs::parse_extra_crate_attrs,
expr_store::{Body, ExprOrPatSource, ExpressionStore, HygieneId, path::Path},
- hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
+ hir::{BindingId, Expr, ExprId, ExprOrPatId},
nameres::{ModuleOrigin, crate_def_map},
resolver::{self, HasResolver, Resolver, TypeNs, ValueNs},
type_ref::Mutability,
@@ -32,24 +32,24 @@ use hir_expand::{
name::AsName,
};
use hir_ty::{
- InferenceResult,
+ InferBodyId, InferenceResult,
+ db::AnonConstId,
diagnostics::unsafe_operations,
infer_query_with_inspect,
next_solver::{
- AnyImplId, DbInterner, Span,
+ AnyImplId, DbInterner,
format_proof_tree::{ProofTreeData, dump_proof_tree_structured},
},
};
use intern::{Interned, Symbol, sym};
use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet};
-use rustc_type_ir::inherent::Span as _;
use smallvec::{SmallVec, smallvec};
use span::{FileId, SyntaxContext};
use stdx::{TupleExt, always};
use syntax::{
- AstNode, AstToken, Direction, SmolStr, SmolStrBuilder, SyntaxElement, SyntaxKind, SyntaxNode,
- SyntaxNodePtr, SyntaxToken, T, TextRange, TextSize,
+ AstNode, AstPtr, AstToken, Direction, SmolStr, SmolStrBuilder, SyntaxElement, SyntaxKind,
+ SyntaxNode, SyntaxNodePtr, SyntaxToken, T, TextRange, TextSize,
algo::skip_trivia_token,
ast::{self, HasAttrs as _, HasGenericParams},
};
@@ -165,11 +165,17 @@ pub struct Semantics<'db, DB: ?Sized> {
imp: SemanticsImpl<'db>,
}
+type DefWithoutBodyWithAnonConsts = Either<GenericDefId, VariantId>;
+type ExprToAnonConst = FxHashMap<ExprId, AnonConstId>;
+type DefAnonConstsMap = FxHashMap<DefWithoutBodyWithAnonConsts, ExprToAnonConst>;
+
pub struct SemanticsImpl<'db> {
pub db: &'db dyn HirDatabase,
- s2d_cache: RefCell<SourceToDefCache>,
+ s2d_cache: RefCell<SourceToDefCache<'db>>,
/// MacroCall to its expansion's MacroCallId cache
macro_call_cache: RefCell<FxHashMap<InFile<ast::MacroCall>, MacroCallId>>,
+ /// All anon consts defined by a *signature* (not a body).
+ signature_anon_consts_cache: RefCell<DefAnonConstsMap>,
}
impl<DB: ?Sized> fmt::Debug for Semantics<'_, DB> {
@@ -455,7 +461,12 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> {
impl<'db> SemanticsImpl<'db> {
fn new(db: &'db dyn HirDatabase) -> Self {
- SemanticsImpl { db, s2d_cache: Default::default(), macro_call_cache: Default::default() }
+ SemanticsImpl {
+ db,
+ s2d_cache: Default::default(),
+ macro_call_cache: Default::default(),
+ signature_anon_consts_cache: Default::default(),
+ }
}
pub fn parse(&self, file_id: EditionedFileId) -> ast::SourceFile {
@@ -519,7 +530,7 @@ impl<'db> SemanticsImpl<'db> {
}
}
HirFileId::MacroFile(macro_file) => {
- let node = self.db.lookup_intern_macro_call(macro_file).to_node(self.db);
+ let node = macro_file.loc(self.db).to_node(self.db);
let root = find_root(&node.value);
self.cache(root, node.file_id);
Some(node)
@@ -544,8 +555,16 @@ impl<'db> SemanticsImpl<'db> {
node
}
+ pub fn to_node_syntax(&self, ptr: InFile<SyntaxNodePtr>) -> SyntaxNode {
+ ptr.value.to_node(&self.parse_or_expand(ptr.file_id))
+ }
+
+ pub fn to_node<N: AstNode>(&self, ptr: InFile<AstPtr<N>>) -> N {
+ ptr.value.to_node(&self.parse_or_expand(ptr.file_id))
+ }
+
pub fn expand(&self, file_id: MacroCallId) -> ExpandResult<SyntaxNode> {
- let res = self.db.parse_macro_expansion(file_id).map(|it| it.0.syntax_node());
+ let res = self.db.parse_macro_expansion(file_id).as_ref().map(|it| it.0.syntax_node());
self.cache(res.value.clone(), file_id.into());
res
}
@@ -563,7 +582,7 @@ impl<'db> SemanticsImpl<'db> {
macro_call: &ast::MacroCall,
) -> Option<ExpandResult<SyntaxNode>> {
let file_id = self.to_def(macro_call)?;
- let macro_call = self.db.lookup_intern_macro_call(file_id);
+ let macro_call = file_id.loc(self.db);
let skip = matches!(
macro_call.def.kind,
@@ -645,7 +664,7 @@ impl<'db> SemanticsImpl<'db> {
let ExpandResult { value, err } = self.db.parse_macro_expansion(file_id);
let root_node = value.0.syntax_node();
self.cache(root_node.clone(), file_id.into());
- Some(ExpandResult { value: root_node, err })
+ Some(ExpandResult { value: root_node, err: err.clone() })
})
.collect();
Some(res)
@@ -781,21 +800,16 @@ impl<'db> SemanticsImpl<'db> {
/// Checks if renaming `renamed` to `new_name` may introduce conflicts with other locals,
/// and returns the conflicting locals.
pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &Name) -> Vec<Local> {
- // FIXME: signatures
- let Some(def) = to_be_renamed.parent.as_def_with_body() else {
- return Vec::new();
- };
- let body = Body::of(self.db, def);
+ let (store, root_expr) = to_be_renamed.parent_infer.store_and_root_expr(self.db);
let resolver = to_be_renamed.parent.resolver(self.db);
- let starting_expr =
- body.binding_owner(to_be_renamed.binding_id).unwrap_or(body.root_expr());
+ let starting_expr = store.binding_owner(to_be_renamed.binding_id).unwrap_or(root_expr);
let mut visitor = RenameConflictsVisitor {
- body,
+ body: store,
conflicts: FxHashSet::default(),
db: self.db,
new_name: new_name.symbol().clone(),
old_name: to_be_renamed.name(self.db).symbol().clone(),
- owner: def,
+ owner: to_be_renamed.parent,
to_be_renamed: to_be_renamed.binding_id,
resolver,
};
@@ -803,7 +817,11 @@ impl<'db> SemanticsImpl<'db> {
visitor
.conflicts
.into_iter()
- .map(|binding_id| Local { parent: to_be_renamed.parent, binding_id })
+ .map(|binding_id| Local {
+ parent: to_be_renamed.parent,
+ parent_infer: to_be_renamed.parent_infer,
+ binding_id,
+ })
.collect()
}
@@ -1304,7 +1322,7 @@ impl<'db> SemanticsImpl<'db> {
})
.map(|(call_id, item)| {
let item_range = item.syntax().text_range();
- let loc = db.lookup_intern_macro_call(call_id);
+ let loc = call_id.loc(db);
let text_range = match loc.kind {
hir_expand::MacroCallKind::Attr {
censored_attr_ids: attr_ids,
@@ -1635,6 +1653,44 @@ impl<'db> SemanticsImpl<'db> {
.kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len())
}
+ /// Returns the `return` expressions in this function's body,
+ /// excluding those inside closures or async blocks.
+ pub fn fn_return_points(&self, func: Function) -> Vec<InFile<ast::ReturnExpr>> {
+ let func_id = match func.id {
+ AnyFunctionId::FunctionId(id) => id,
+ _ => return vec![],
+ };
+ let (body, source_map) = Body::with_source_map(self.db, func_id.into());
+
+ fn collect_returns(
+ sema: &SemanticsImpl<'_>,
+ body: &Body,
+ source_map: &hir_def::expr_store::ExpressionStoreSourceMap,
+ expr_id: ExprId,
+ acc: &mut Vec<InFile<ast::ReturnExpr>>,
+ ) {
+ match &body[expr_id] {
+ Expr::Closure { .. } | Expr::Const(_) => return,
+ Expr::Return { .. } => {
+ if let Ok(source) = source_map.expr_syntax(expr_id)
+ && let Some(ret_expr) = source.value.cast::<ast::ReturnExpr>()
+ {
+ let root = sema.parse_or_expand(source.file_id);
+ acc.push(InFile::new(source.file_id, ret_expr.to_node(&root)));
+ }
+ }
+ _ => {}
+ }
+ body.walk_child_exprs(expr_id, |child| {
+ collect_returns(sema, body, source_map, child, acc);
+ });
+ }
+
+ let mut returns = vec![];
+ collect_returns(self, body, source_map, body.root_expr(), &mut returns);
+ returns
+ }
+
pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> {
let text = lifetime.text();
let lifetime_param = lifetime.syntax().ancestors().find_map(|syn| {
@@ -1915,15 +1971,16 @@ impl<'db> SemanticsImpl<'db> {
pub fn get_unsafe_ops(&self, def: ExpressionStoreOwner) -> FxHashSet<ExprOrPatSource> {
let Ok(def) = ExpressionStoreOwnerId::try_from(def) else { return Default::default() };
let (body, source_map) = ExpressionStore::with_source_map(self.db, def);
- let infer = InferenceResult::of(self.db, def);
let mut res = FxHashSet::default();
- for root in body.expr_roots() {
- unsafe_operations(self.db, infer, def, body, root, &mut |node, _| {
- if let Ok(node) = source_map.expr_or_pat_syntax(node) {
- res.insert(node);
- }
- });
- }
+ self.with_all_infers_for_store(def, &mut |infer| {
+ for root in body.expr_roots() {
+ unsafe_operations(self.db, infer, def, body, root, &mut |node, _| {
+ if let Ok(node) = source_map.expr_or_pat_syntax(node) {
+ res.insert(node);
+ }
+ });
+ }
+ });
res
}
@@ -2071,11 +2128,14 @@ impl<'db> SemanticsImpl<'db> {
}
pub fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> {
- self.analyze_no_infer(node).map(|SourceAnalyzer { file_id, resolver, .. }| SemanticsScope {
- db: self.db,
- file_id,
- resolver,
- })
+ self.analyze_no_infer(node).map(
+ |SourceAnalyzer { file_id, resolver, infer_body, .. }| SemanticsScope {
+ db: self.db,
+ file_id,
+ resolver,
+ infer_body,
+ },
+ )
}
pub fn scope_at_offset(
@@ -2084,10 +2144,11 @@ impl<'db> SemanticsImpl<'db> {
offset: TextSize,
) -> Option<SemanticsScope<'db>> {
self.analyze_with_offset_no_infer(node, offset).map(
- |SourceAnalyzer { file_id, resolver, .. }| SemanticsScope {
+ |SourceAnalyzer { file_id, resolver, infer_body, .. }| SemanticsScope {
db: self.db,
file_id,
resolver,
+ infer_body,
},
)
}
@@ -2114,6 +2175,85 @@ impl<'db> SemanticsImpl<'db> {
container.as_expression_store_owner().map(|id| id.into())
}
+ fn populate_anon_const_cache_for<'a>(
+ &self,
+ cache: &'a mut DefAnonConstsMap,
+ def: DefWithoutBodyWithAnonConsts,
+ ) -> &'a ExprToAnonConst {
+ cache.entry(def).or_insert_with(|| match def {
+ Either::Left(def) => {
+ let all_anon_consts =
+ AnonConstId::all_from_signature(self.db, def).into_iter().flatten().copied();
+ all_anon_consts
+ .map(|anon_const| (anon_const.loc(self.db).expr, anon_const))
+ .collect()
+ }
+ Either::Right(def) => {
+ let all_anon_consts =
+ self.db.field_types_with_diagnostics(def).defined_anon_consts().iter().copied();
+ all_anon_consts
+ .map(|anon_const| (anon_const.loc(self.db).expr, anon_const))
+ .collect()
+ }
+ })
+ }
+
+ fn find_anon_const_for_root_expr_in_signature(
+ &self,
+ def: DefWithoutBodyWithAnonConsts,
+ root_expr: ExprId,
+ ) -> Option<AnonConstId> {
+ let mut cache = self.signature_anon_consts_cache.borrow_mut();
+ let anon_consts_map = self.populate_anon_const_cache_for(&mut cache, def);
+ anon_consts_map.get(&root_expr).copied()
+ }
+
+ pub(crate) fn infer_body_for_expr_or_pat(
+ &self,
+ def: ExpressionStoreOwnerId,
+ store: &ExpressionStore,
+ node: ExprOrPatId,
+ ) -> Option<InferBodyId> {
+ let handle_def_without_body = |def| {
+ let root_expr = match node {
+ ExprOrPatId::ExprId(expr) => store.find_root_for_expr(expr),
+ ExprOrPatId::PatId(pat) => store.find_root_for_pat(pat),
+ };
+ let anon_const = self.find_anon_const_for_root_expr_in_signature(def, root_expr)?;
+ Some(anon_const.into())
+ };
+ match def {
+ ExpressionStoreOwnerId::Signature(def) => handle_def_without_body(Either::Left(def)),
+ ExpressionStoreOwnerId::Body(def) => Some(def.into()),
+ ExpressionStoreOwnerId::VariantFields(def) => {
+ handle_def_without_body(Either::Right(def))
+ }
+ }
+ }
+
+ fn with_all_infers_for_store(
+ &self,
+ owner: ExpressionStoreOwnerId,
+ callback: &mut dyn FnMut(&'db InferenceResult),
+ ) {
+ let mut handle_def_without_body = |def| {
+ let mut cache = self.signature_anon_consts_cache.borrow_mut();
+ let map = self.populate_anon_const_cache_for(&mut cache, def);
+ for &anon_const in map.values() {
+ callback(InferenceResult::of(self.db, anon_const));
+ }
+ };
+ match owner {
+ ExpressionStoreOwnerId::Signature(def) => handle_def_without_body(Either::Left(def)),
+ ExpressionStoreOwnerId::Body(def) => {
+ callback(InferenceResult::of(self.db, def));
+ }
+ ExpressionStoreOwnerId::VariantFields(def) => {
+ handle_def_without_body(Either::Right(def))
+ }
+ }
+ }
+
/// Returns none if the file of the node is not part of a crate.
fn analyze(&self, node: &SyntaxNode) -> Option<SourceAnalyzer<'db>> {
let node = self.find_file(node);
@@ -2155,34 +2295,36 @@ impl<'db> SemanticsImpl<'db> {
});
}
ChildContainer::VariantId(def) => {
- return Some(SourceAnalyzer::new_variant_body(self.db, def, node, offset, infer));
+ return Some(SourceAnalyzer::new_variant_body(
+ self.db, self, def, node, offset, infer,
+ ));
}
ChildContainer::TraitId(it) => {
return Some(if infer {
- SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset)
+ SourceAnalyzer::new_generic_def(self.db, self, it.into(), node, offset)
} else {
- SourceAnalyzer::new_generic_def_no_infer(self.db, it.into(), node, offset)
+ SourceAnalyzer::new_generic_def_no_infer(self.db, self, it.into(), node, offset)
});
}
ChildContainer::ImplId(it) => {
return Some(if infer {
- SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset)
+ SourceAnalyzer::new_generic_def(self.db, self, it.into(), node, offset)
} else {
- SourceAnalyzer::new_generic_def_no_infer(self.db, it.into(), node, offset)
+ SourceAnalyzer::new_generic_def_no_infer(self.db, self, it.into(), node, offset)
});
}
ChildContainer::EnumId(it) => {
return Some(if infer {
- SourceAnalyzer::new_generic_def(self.db, it.into(), node, offset)
+ SourceAnalyzer::new_generic_def(self.db, self, it.into(), node, offset)
} else {
- SourceAnalyzer::new_generic_def_no_infer(self.db, it.into(), node, offset)
+ SourceAnalyzer::new_generic_def_no_infer(self.db, self, it.into(), node, offset)
});
}
ChildContainer::GenericDefId(it) => {
return Some(if infer {
- SourceAnalyzer::new_generic_def(self.db, it, node, offset)
+ SourceAnalyzer::new_generic_def(self.db, self, it, node, offset)
} else {
- SourceAnalyzer::new_generic_def_no_infer(self.db, it, node, offset)
+ SourceAnalyzer::new_generic_def_no_infer(self.db, self, it, node, offset)
});
}
ChildContainer::ModuleId(it) => it.resolver(self.db),
@@ -2315,6 +2457,7 @@ impl<'db> SemanticsImpl<'db> {
text_range: TextRange,
) -> Option<FxIndexSet<Local>> {
let sa = self.analyze(element.either(|e| e.syntax(), |s| s.syntax()))?;
+ let infer_body = sa.infer_body?;
let store = sa.store()?;
let mut resolver = sa.resolver.clone();
let def = resolver.expression_store_owner()?;
@@ -2371,13 +2514,7 @@ impl<'db> SemanticsImpl<'db> {
None
}
}
- ExprOrPatId::PatId(pat_id) => {
- if let Pat::Path(path) = &store[pat_id] {
- Some(path)
- } else {
- None
- }
- }
+ ExprOrPatId::PatId(_) => None,
};
if let Some(path) = path
@@ -2387,7 +2524,11 @@ impl<'db> SemanticsImpl<'db> {
let hygiene = store.expr_or_pat_path_hygiene(id);
resolver.resolve_path_in_value_ns_fully(self.db, path, hygiene).inspect(|value| {
if let ValueNs::LocalBinding(id) = value {
- locals.insert((def, *id).into());
+ locals.insert(Local {
+ parent: def,
+ parent_infer: infer_body,
+ binding_id: *id,
+ });
}
});
}
@@ -2427,7 +2568,8 @@ impl<'db> SemanticsImpl<'db> {
if result.is_err()
&& let Some(tree) = proof_tree
{
- let data = dump_proof_tree_structured(tree, Span::dummy(), infer_ctxt);
+ let data =
+ dump_proof_tree_structured(tree, hir_ty::Span::Dummy, infer_ctxt);
RESULT.with(|ctx| ctx.borrow_mut().push(data));
}
}),
@@ -2448,7 +2590,7 @@ fn macro_call_to_macro_id(
macro_call_id: MacroCallId,
) -> Option<MacroId> {
let db: &dyn ExpandDatabase = ctx.db;
- let loc = db.lookup_intern_macro_call(macro_call_id);
+ let loc = macro_call_id.loc(db);
match loc.def.ast_id() {
Either::Left(it) => {
@@ -2514,7 +2656,6 @@ to_def_impls![
(crate::ConstParam, ast::ConstParam, const_param_to_def),
(crate::GenericParam, ast::GenericParam, generic_param_to_def),
(crate::Macro, ast::Macro, macro_to_def),
- (crate::Local, ast::IdentPat, bind_pat_to_def),
(crate::Local, ast::SelfParam, self_param_to_def),
(crate::Label, ast::Label, label_to_def),
(crate::Adt, ast::Adt, adt_to_def),
@@ -2524,6 +2665,14 @@ to_def_impls![
(MacroCallId, ast::MacroCall, macro_call_to_macro_call),
];
+impl ToDef for ast::IdentPat {
+ type Def = crate::Local;
+
+ fn to_def(sema: &SemanticsImpl<'_>, src: InFile<&Self>) -> Option<Self::Def> {
+ sema.with_ctx(|ctx| ctx.bind_pat_to_def(src, sema))
+ }
+}
+
fn find_root(node: &SyntaxNode) -> SyntaxNode {
node.ancestors().last().unwrap()
}
@@ -2550,6 +2699,7 @@ fn find_root(node: &SyntaxNode) -> SyntaxNode {
#[derive(Debug)]
pub struct SemanticsScope<'db> {
pub db: &'db dyn HirDatabase,
+ infer_body: Option<InferBodyId>,
file_id: HirFileId,
resolver: Resolver<'db>,
}
@@ -2601,9 +2751,11 @@ impl<'db> SemanticsScope<'db> {
resolver::ScopeDef::AdtSelfType(it) => ScopeDef::AdtSelfType(it.into()),
resolver::ScopeDef::GenericParam(id) => ScopeDef::GenericParam(id.into()),
resolver::ScopeDef::Local(binding_id) => {
- match self.resolver.expression_store_owner() {
- Some(parent) => ScopeDef::Local(Local { parent, binding_id }),
- None => continue,
+ match (self.resolver.expression_store_owner(), self.infer_body) {
+ (Some(parent), Some(parent_infer)) => {
+ ScopeDef::Local(Local { parent, parent_infer, binding_id })
+ }
+ _ => continue,
}
}
resolver::ScopeDef::Label(label_id) => {
@@ -2657,6 +2809,7 @@ impl<'db> SemanticsScope<'db> {
resolve_hir_path(
self.db,
&self.resolver,
+ self.infer_body,
&Path::BarePath(Interned::new(ModPath::from_segments(kind, segments))),
HygieneId::ROOT,
None,
@@ -2715,9 +2868,9 @@ impl ops::Deref for VisibleTraits {
struct RenameConflictsVisitor<'a> {
db: &'a dyn HirDatabase,
- owner: DefWithBodyId,
+ owner: ExpressionStoreOwnerId,
resolver: Resolver<'a>,
- body: &'a Body,
+ body: &'a ExpressionStore,
to_be_renamed: BindingId,
new_name: Symbol,
old_name: Symbol,
@@ -2761,15 +2914,6 @@ impl RenameConflictsVisitor<'_> {
self.resolve_path(expr.into(), path);
self.resolver.reset_to_guard(guard);
}
- &Expr::Assignment { target, .. } => {
- let guard = self.resolver.update_to_inner_scope(self.db, self.owner, expr);
- self.body.walk_pats(target, &mut |pat| {
- if let Pat::Path(path) = &self.body[pat] {
- self.resolve_path(pat.into(), path);
- }
- });
- self.resolver.reset_to_guard(guard);
- }
_ => {}
}
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index d932198b43..583b3e4bda 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -114,19 +114,22 @@ use syntax::{
};
use tt::TextRange;
-use crate::{InFile, InlineAsmOperand, db::HirDatabase, semantics::child_by_source::ChildBySource};
+use crate::{
+ InFile, InlineAsmOperand, SemanticsImpl, db::HirDatabase,
+ semantics::child_by_source::ChildBySource,
+};
#[derive(Default)]
-pub(super) struct SourceToDefCache {
+pub(super) struct SourceToDefCache<'db> {
pub(super) dynmap_cache: FxHashMap<(ChildContainer, HirFileId), DynMap>,
- expansion_info_cache: FxHashMap<MacroCallId, ExpansionInfo>,
+ expansion_info_cache: FxHashMap<MacroCallId, ExpansionInfo<'db>>,
pub(super) file_to_def_cache: FxHashMap<FileId, SmallVec<[ModuleId; 1]>>,
pub(super) included_file_cache: FxHashMap<EditionedFileId, Option<MacroCallId>>,
/// Rootnode to HirFileId cache
pub(super) root_to_file_cache: FxHashMap<SyntaxNode, HirFileId>,
}
-impl SourceToDefCache {
+impl<'db> SourceToDefCache<'db> {
pub(super) fn cache(
root_to_file_cache: &mut FxHashMap<SyntaxNode, HirFileId>,
root_node: SyntaxNode,
@@ -156,9 +159,9 @@ impl SourceToDefCache {
pub(super) fn get_or_insert_expansion(
&mut self,
- db: &dyn HirDatabase,
+ db: &'db dyn HirDatabase,
macro_file: MacroCallId,
- ) -> &ExpansionInfo {
+ ) -> &ExpansionInfo<'db> {
self.expansion_info_cache.entry(macro_file).or_insert_with(|| {
let exp_info = macro_file.expansion_info(db);
@@ -172,7 +175,7 @@ impl SourceToDefCache {
pub(super) struct SourceToDefCtx<'db, 'cache> {
pub(super) db: &'db dyn HirDatabase,
- pub(super) cache: &'cache mut SourceToDefCache,
+ pub(super) cache: &'cache mut SourceToDefCache<'db>,
}
impl SourceToDefCtx<'_, '_> {
@@ -345,14 +348,16 @@ impl SourceToDefCtx<'_, '_> {
pub(super) fn bind_pat_to_def(
&mut self,
src: InFile<&ast::IdentPat>,
- ) -> Option<(ExpressionStoreOwnerId, BindingId)> {
+ semantics: &SemanticsImpl<'_>,
+ ) -> Option<crate::Local> {
let container = self.find_container(src.syntax_ref())?.as_expression_store_owner()?;
let (store, source_map) = ExpressionStore::with_source_map(self.db, container);
let src = src.cloned().map(ast::Pat::from);
let pat_id = source_map.node_pat(src.as_ref())?;
// the pattern could resolve to a constant, verify that this is not the case
if let crate::Pat::Bind { id, .. } = store[pat_id.as_pat()?] {
- Some((container, id))
+ let parent_infer = semantics.infer_body_for_expr_or_pat(container, store, pat_id)?;
+ Some(crate::Local { parent: container, parent_infer, binding_id: id })
} else {
None
}
@@ -366,7 +371,7 @@ impl SourceToDefCtx<'_, '_> {
.as_expression_store_owner()?
.as_def_with_body()?;
let body = Body::of(self.db, container);
- Some((container, body.self_param?))
+ Some((container, body.self_param()?))
}
pub(super) fn label_to_def(
&mut self,
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 6c43f80ce8..06182620c8 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -5,12 +5,15 @@
//!
//! So, this modules should not be used during hir construction, it exists
//! purely for "IDE needs".
-use std::iter::{self, once};
+use std::{
+ cell::OnceCell,
+ iter::{self, once},
+};
use either::Either;
use hir_def::{
AdtId, AssocItemId, CallableDefId, ConstId, DefWithBodyId, ExpressionStoreOwnerId, FieldId,
- FunctionId, GenericDefId, LocalFieldId, ModuleDefId, StructId, TraitId, VariantId,
+ FunctionId, GenericDefId, LocalFieldId, ModuleDefId, StructId, VariantId,
expr_store::{
Body, BodySourceMap, ExpressionStore, ExpressionStoreSourceMap, HygieneId,
lower::ExprCollector,
@@ -21,7 +24,7 @@ use hir_def::{
lang_item::LangItems,
nameres::MacroSubNs,
resolver::{Resolver, TypeNs, ValueNs, resolver_for_scope},
- type_ref::{Mutability, TypeRef, TypeRefId},
+ type_ref::{Mutability, TypeRefId},
};
use hir_expand::{
HirFileId, InFile,
@@ -29,7 +32,8 @@ use hir_expand::{
name::{AsName, Name},
};
use hir_ty::{
- Adjustment, InferenceResult, LifetimeElisionKind, ParamEnvAndCrate, TyLoweringContext,
+ Adjustment, InferBodyId, InferenceResult, LifetimeElisionKind, ParamEnvAndCrate,
+ TyLoweringContext, TyLoweringInferVarsCtx,
diagnostics::{
InsideUnsafeBlock, record_literal_missing_fields, record_pattern_missing_fields,
unsafe_operations,
@@ -37,8 +41,8 @@ use hir_ty::{
lang_items::lang_items_for_bin_op,
method_resolution::{self, CandidateId},
next_solver::{
- AliasTy, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, Ty, TyKind, TypingMode,
- infer::DbInternerInferExt,
+ AliasTy, DbInterner, DefaultAny, ErrorGuaranteed, GenericArgs, ParamEnv, Region, Ty,
+ TyKind, TypingMode, infer::DbInternerInferExt,
},
traits::structurally_normalize_ty,
};
@@ -47,7 +51,7 @@ use itertools::Itertools;
use rustc_hash::FxHashSet;
use rustc_type_ir::{
AliasTyKind,
- inherent::{AdtDef, IntoKind, Ty as _},
+ inherent::{IntoKind, Ty as _},
};
use smallvec::SmallVec;
use stdx::never;
@@ -59,7 +63,7 @@ use syntax::{
use crate::{
Adt, AnyFunctionId, AssocItem, BindingMode, BuiltinAttr, BuiltinType, Callable, Const,
DeriveHelper, EnumVariant, Field, Function, GenericSubstitution, Local, Macro, ModuleDef,
- Static, Struct, ToolModule, Trait, TupleField, Type, TypeAlias,
+ SemanticsImpl, Static, Struct, ToolModule, Trait, TupleField, Type, TypeAlias,
db::HirDatabase,
semantics::{PathResolution, PathResolutionPerNs},
};
@@ -71,6 +75,7 @@ pub(crate) struct SourceAnalyzer<'db> {
pub(crate) file_id: HirFileId,
pub(crate) resolver: Resolver<'db>,
pub(crate) body_or_sig: Option<BodyOrSig<'db>>,
+ pub(crate) infer_body: Option<InferBodyId>,
}
#[derive(Debug)]
@@ -137,34 +142,39 @@ impl<'db> SourceAnalyzer<'db> {
scope_for_offset(db, scopes, source_map, node.file_id, offset)
}
};
+ let (scope, _expr) = scope.unzip();
let resolver = resolver_for_scope(db, def, scope);
SourceAnalyzer {
resolver,
body_or_sig: Some(BodyOrSig::Body { def, body, source_map, infer }),
file_id,
+ infer_body: Some(def.into()),
}
}
pub(crate) fn new_generic_def(
db: &'db dyn HirDatabase,
+ sema: &SemanticsImpl<'db>,
def: GenericDefId,
node: InFile<&SyntaxNode>,
offset: Option<TextSize>,
) -> SourceAnalyzer<'db> {
- Self::new_generic_def_(db, def, node, offset, true)
+ Self::new_generic_def_(db, sema, def, node, offset, true)
}
pub(crate) fn new_generic_def_no_infer(
db: &'db dyn HirDatabase,
+ sema: &SemanticsImpl<'db>,
def: GenericDefId,
node: InFile<&SyntaxNode>,
offset: Option<TextSize>,
) -> SourceAnalyzer<'db> {
- Self::new_generic_def_(db, def, node, offset, false)
+ Self::new_generic_def_(db, sema, def, node, offset, false)
}
pub(crate) fn new_generic_def_(
db: &'db dyn HirDatabase,
+ sema: &SemanticsImpl<'db>,
def: GenericDefId,
node @ InFile { file_id, .. }: InFile<&SyntaxNode>,
offset: Option<TextSize>,
@@ -184,17 +194,31 @@ impl<'db> SourceAnalyzer<'db> {
scope_for_offset(db, scopes, source_map, node.file_id, offset)
}
};
+ let (scope, expr) = scope.unzip();
let resolver = resolver_for_scope(db, def, scope);
- let infer = if infer { Some(InferenceResult::of(db, def)) } else { None };
+ let infer_body = expr.and_then(|expr| {
+ sema.infer_body_for_expr_or_pat(
+ ExpressionStoreOwnerId::Signature(def),
+ store,
+ expr.into(),
+ )
+ });
+ let infer = if infer && let Some(infer_body) = infer_body {
+ Some(InferenceResult::of(db, infer_body))
+ } else {
+ None
+ };
SourceAnalyzer {
resolver,
body_or_sig: Some(BodyOrSig::Sig { def, store, source_map, generics, infer }),
file_id,
+ infer_body,
}
}
pub(crate) fn new_variant_body(
db: &'db dyn HirDatabase,
+ sema: &SemanticsImpl<'db>,
def: VariantId,
node @ InFile { file_id, .. }: InFile<&SyntaxNode>,
offset: Option<TextSize>,
@@ -214,8 +238,20 @@ impl<'db> SourceAnalyzer<'db> {
scope_for_offset(db, scopes, source_map, node.file_id, offset)
}
};
+ let (scope, expr) = scope.unzip();
let resolver = resolver_for_scope(db, def, scope);
- let infer = if infer { Some(InferenceResult::of(db, def)) } else { None };
+ let infer_body = expr.and_then(|expr| {
+ sema.infer_body_for_expr_or_pat(
+ ExpressionStoreOwnerId::VariantFields(def),
+ &fields.store,
+ expr.into(),
+ )
+ });
+ let infer = if infer && let Some(infer_body) = infer_body {
+ Some(InferenceResult::of(db, infer_body))
+ } else {
+ None
+ };
SourceAnalyzer {
resolver,
body_or_sig: Some(BodyOrSig::VariantFields {
@@ -225,6 +261,7 @@ impl<'db> SourceAnalyzer<'db> {
infer,
}),
file_id,
+ infer_body,
}
}
@@ -232,7 +269,7 @@ impl<'db> SourceAnalyzer<'db> {
resolver: Resolver<'db>,
node: InFile<&SyntaxNode>,
) -> SourceAnalyzer<'db> {
- SourceAnalyzer { resolver, body_or_sig: None, file_id: node.file_id }
+ SourceAnalyzer { resolver, body_or_sig: None, file_id: node.file_id, infer_body: None }
}
fn owner(&self) -> Option<ExpressionStoreOwnerId> {
@@ -339,39 +376,45 @@ impl<'db> SourceAnalyzer<'db> {
let type_ref = self.type_id(ty)?;
- let mut ty = TyLoweringContext::new(
+ let generic_def = self.resolver.generic_def()?;
+ let generics = OnceCell::new();
+ let mut vars_cts = VarsCtx { types: interner.default_types(), infer: self.infer() };
+ let ty = TyLoweringContext::new(
db,
&self.resolver,
self.store()?,
- self.resolver.generic_def()?,
+ generic_def.into(),
+ generic_def,
+ &generics,
// FIXME: Is this correct here? Anyway that should impact mostly diagnostics, which we don't emit here
// (this can impact the lifetimes generated, e.g. in `const` they won't be `'static`, but this seems like a
// small problem).
LifetimeElisionKind::Infer,
)
+ .with_infer_vars_behavior(Some(&mut vars_cts))
.lower_ty(type_ref);
- // Try and substitute unknown types using InferenceResult
- if let Some(infer) = self.infer()
- && let Some(store) = self.store()
- {
- let mut inferred_types = vec![];
- TypeRef::walk(type_ref, store, &mut |type_ref_id, type_ref| {
- if matches!(type_ref, TypeRef::Placeholder) {
- inferred_types.push(infer.type_of_type_placeholder(type_ref_id));
+ struct VarsCtx<'a, 'db> {
+ types: &'db DefaultAny<'db>,
+ infer: Option<&'a InferenceResult>,
+ }
+
+ impl<'db> TyLoweringInferVarsCtx<'db> for VarsCtx<'_, 'db> {
+ fn next_ty_var(&mut self, span: hir_ty::Span) -> Ty<'db> {
+ if let hir_ty::Span::TypeRefId(type_ref) = span
+ && let Some(ty) =
+ self.infer.and_then(|infer| infer.type_of_type_placeholder(type_ref))
+ {
+ ty
+ } else {
+ self.types.types.error
}
- });
- let mut inferred_types = inferred_types.into_iter();
-
- let substituted_ty = hir_ty::next_solver::fold::fold_tys(interner, ty, |ty| {
- if ty.is_ty_error() { inferred_types.next().flatten().unwrap_or(ty) } else { ty }
- });
-
- // Only used the result if the placeholder and unknown type counts matched
- let success =
- inferred_types.next().is_none() && !substituted_ty.references_non_lt_error();
- if success {
- ty = substituted_ty;
+ }
+ fn next_const_var(&mut self, _span: hir_ty::Span) -> hir_ty::next_solver::Const<'db> {
+ self.types.consts.error
+ }
+ fn next_region_var(&mut self, _span: hir_ty::Span) -> Region<'db> {
+ self.types.regions.error
}
}
@@ -409,7 +452,7 @@ impl<'db> SourceAnalyzer<'db> {
ExprOrPatId::PatId(idx) => infer
.pat_adjustment(idx)
.and_then(|adjusts| adjusts.last())
- .map(|adjust| adjust.as_ref()),
+ .map(|adjust| adjust.source.as_ref()),
};
let ty = infer.expr_or_pat_ty(expr_or_pat_id);
@@ -436,7 +479,7 @@ impl<'db> SourceAnalyzer<'db> {
) -> Option<Type<'db>> {
let binding = match self.body_or_sig.as_ref()? {
BodyOrSig::Sig { .. } | BodyOrSig::VariantFields { .. } => return None,
- BodyOrSig::Body { body, .. } => body.self_param?,
+ BodyOrSig::Body { body, .. } => body.self_param()?,
};
let ty = self.infer()?.binding_ty(binding);
Some(Type::new_with_resolver(db, &self.resolver, ty))
@@ -449,12 +492,12 @@ impl<'db> SourceAnalyzer<'db> {
) -> Option<BindingMode> {
let id = self.pat_id(&pat.clone().into())?;
let infer = self.infer()?;
- infer.binding_mode(id.as_pat()?).map(|bm| match bm {
- hir_ty::BindingMode::Move => BindingMode::Move,
- hir_ty::BindingMode::Ref(hir_ty::next_solver::Mutability::Mut) => {
+ Some(match infer.binding_mode(id.as_pat()?)? {
+ hir_ty::BindingMode(hir_ty::ByRef::No, _) => BindingMode::Move,
+ hir_ty::BindingMode(hir_ty::ByRef::Yes(hir_ty::next_solver::Mutability::Mut), _) => {
BindingMode::Ref(Mutability::Mut)
}
- hir_ty::BindingMode::Ref(hir_ty::next_solver::Mutability::Not) => {
+ hir_ty::BindingMode(hir_ty::ByRef::Yes(hir_ty::next_solver::Mutability::Not), _) => {
BindingMode::Ref(Mutability::Shared)
}
})
@@ -470,7 +513,7 @@ impl<'db> SourceAnalyzer<'db> {
infer
.pat_adjustment(pat_id.as_pat()?)?
.iter()
- .map(|ty| Type::new_with_resolver(db, &self.resolver, ty.as_ref()))
+ .map(|adjust| Type::new_with_resolver(db, &self.resolver, adjust.source.as_ref()))
.collect(),
)
}
@@ -483,7 +526,7 @@ impl<'db> SourceAnalyzer<'db> {
let expr_id = self.expr_id(call.clone().into())?.as_expr()?;
let (func, args) = self.infer()?.method_resolution(expr_id)?;
let interner = DbInterner::new_no_crate(db);
- let ty = db.value_ty(func.into())?.instantiate(interner, args);
+ let ty = db.value_ty(func.into())?.instantiate(interner, args).skip_norm_wip();
let ty = Type::new_with_resolver(db, &self.resolver, ty);
let mut res = ty.as_callable(db)?;
res.is_bound_method = true;
@@ -538,7 +581,7 @@ impl<'db> SourceAnalyzer<'db> {
&self,
field: &ast::FieldExpr,
) -> Option<Either<Field, TupleField>> {
- let def = self.owner()?;
+ let def = self.infer_body?;
let expr_id = self.expr_id(field.clone().into())?.as_expr()?;
self.infer()?.field_resolution(expr_id).map(|it| {
it.map_either(Into::into, |f| TupleField { owner: def, tuple: f.tuple, index: f.index })
@@ -565,7 +608,7 @@ impl<'db> SourceAnalyzer<'db> {
field: &ast::FieldExpr,
) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution<'db>>)>
{
- let def = self.owner()?;
+ let def = self.infer_body?;
let expr_id = self.expr_id(field.clone().into())?.as_expr()?;
let inference_result = self.infer()?;
match inference_result.field_resolution(expr_id) {
@@ -626,8 +669,7 @@ impl<'db> SourceAnalyzer<'db> {
has_start: bool,
has_end: bool,
) -> Option<StructId> {
- let has_new_range =
- self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range);
+ let has_new_range = self.resolver.top_level_def_map().features().new_range;
let lang_items = self.lang_items(db);
match (op_kind, has_start, has_end) {
(RangeOp::Exclusive, false, false) => lang_items.RangeFull,
@@ -707,35 +749,25 @@ impl<'db> SourceAnalyzer<'db> {
db: &'db dyn HirDatabase,
prefix_expr: &ast::PrefixExpr,
) -> Option<Function> {
+ let lang_items = self.lang_items(db);
let (_op_trait, op_fn) = match prefix_expr.op_kind()? {
ast::UnaryOp::Deref => {
// This can be either `Deref::deref` or `DerefMut::deref_mut`.
// Since deref kind is inferenced and stored in `InferenceResult.method_resolution`,
// use that result to find out which one it is.
- let (deref_trait, deref) = self.lang_trait_fn(
- db,
- self.lang_items(db).Deref,
- &Name::new_symbol_root(sym::deref),
- )?;
+ let (deref_trait, deref) = (lang_items.Deref?, lang_items.Deref_deref?);
self.infer()
.and_then(|infer| {
let expr = self.expr_id(prefix_expr.clone().into())?.as_expr()?;
let (func, _) = infer.method_resolution(expr)?;
- let (deref_mut_trait, deref_mut) = self.lang_trait_fn(
- db,
- self.lang_items(db).DerefMut,
- &Name::new_symbol_root(sym::deref_mut),
- )?;
+ let (deref_mut_trait, deref_mut) =
+ (lang_items.DerefMut?, lang_items.DerefMut_deref_mut?);
if func == deref_mut { Some((deref_mut_trait, deref_mut)) } else { None }
})
.unwrap_or((deref_trait, deref))
}
- ast::UnaryOp::Not => {
- self.lang_trait_fn(db, self.lang_items(db).Not, &Name::new_symbol_root(sym::not))?
- }
- ast::UnaryOp::Neg => {
- self.lang_trait_fn(db, self.lang_items(db).Neg, &Name::new_symbol_root(sym::neg))?
- }
+ ast::UnaryOp::Not => (lang_items.Not?, lang_items.Not_not?),
+ ast::UnaryOp::Neg => (lang_items.Neg?, lang_items.Neg_neg?),
};
let ty = self.ty_of_expr(prefix_expr.expr()?)?;
@@ -754,19 +786,16 @@ impl<'db> SourceAnalyzer<'db> {
) -> Option<Function> {
let base_ty = self.ty_of_expr(index_expr.base()?)?;
let index_ty = self.ty_of_expr(index_expr.index()?)?;
+ let lang_items = self.lang_items(db);
- let (_index_trait, index_fn) =
- self.lang_trait_fn(db, self.lang_items(db).Index, &Name::new_symbol_root(sym::index))?;
+ let (_index_trait, index_fn) = (lang_items.Index?, lang_items.Index_index?);
let op_fn = self
.infer()
.and_then(|infer| {
let expr = self.expr_id(index_expr.clone().into())?.as_expr()?;
let (func, _) = infer.method_resolution(expr)?;
- let (_index_mut_trait, index_mut_fn) = self.lang_trait_fn(
- db,
- self.lang_items(db).IndexMut,
- &Name::new_symbol_root(sym::index_mut),
- )?;
+ let (_index_mut_trait, index_mut_fn) =
+ (lang_items.IndexMut_index_mut?, lang_items.IndexMut_index_mut?);
if func == index_mut_fn { Some(index_mut_fn) } else { None }
})
.unwrap_or(index_fn);
@@ -785,10 +814,8 @@ impl<'db> SourceAnalyzer<'db> {
let lhs = self.ty_of_expr(binop_expr.lhs()?)?;
let rhs = self.ty_of_expr(binop_expr.rhs()?)?;
- let (_op_trait, op_fn) =
- lang_items_for_bin_op(self.lang_items(db), op).and_then(|(name, lang_item)| {
- self.lang_trait_fn(db, lang_item, &Name::new_symbol_root(name))
- })?;
+ let (op_fn, _op_trait) = lang_items_for_bin_op(self.lang_items(db), op)
+ .and_then(|(method, trait_)| method.zip(trait_))?;
// HACK: subst for `index()` coincides with that for `Index` because `index()` itself
// doesn't have any generic parameters, so we skip building another subst for `index()`.
let substs = GenericArgs::new_from_slice(&[lhs.into(), rhs.into()]);
@@ -836,9 +863,11 @@ impl<'db> SourceAnalyzer<'db> {
&path,
name_hygiene(db, InFile::new(self.file_id, ast_name.syntax())),
) {
- Some(ValueNs::LocalBinding(binding_id)) => {
- Some(Local { binding_id, parent: self.resolver.expression_store_owner()? })
- }
+ Some(ValueNs::LocalBinding(binding_id)) => Some(Local {
+ binding_id,
+ parent: self.owner()?,
+ parent_infer: self.infer_body?,
+ }),
_ => None,
}
};
@@ -846,8 +875,10 @@ impl<'db> SourceAnalyzer<'db> {
let variant = self.infer()?.variant_resolution_for_expr_or_pat(expr_id)?;
let variant_data = variant.fields(db);
let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? };
- let field_ty =
- (*db.field_types(variant).get(field.local_id)?).get().instantiate(interner, subst);
+ let field_ty = (*db.field_types(variant).get(field.local_id)?)
+ .get()
+ .instantiate(interner, subst)
+ .skip_norm_wip();
Some((
field.into(),
local,
@@ -869,8 +900,10 @@ impl<'db> SourceAnalyzer<'db> {
let variant_data = variant.fields(db);
let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? };
let (adt, subst) = self.infer()?.pat_ty(pat_id.as_pat()?).as_adt()?;
- let field_ty =
- (*db.field_types(variant).get(field.local_id)?).get().instantiate(interner, subst);
+ let field_ty = (*db.field_types(variant).get(field.local_id)?)
+ .get()
+ .instantiate(interner, subst)
+ .skip_norm_wip();
Some((
field.into(),
Type::new_with_resolver(db, &self.resolver, field_ty),
@@ -898,7 +931,14 @@ impl<'db> SourceAnalyzer<'db> {
};
let store_owner = self.resolver.expression_store_owner();
- let res = resolve_hir_value_path(db, &self.resolver, store_owner, path, HygieneId::ROOT)?;
+ let res = resolve_hir_value_path(
+ db,
+ &self.resolver,
+ store_owner,
+ self.infer_body,
+ path,
+ HygieneId::ROOT,
+ )?;
match res {
PathResolution::Def(def) => Some(def),
_ => None,
@@ -932,24 +972,25 @@ impl<'db> SourceAnalyzer<'db> {
if let Either::Right(container) = &mut container {
*container = structurally_normalize_ty(&infcx, *container, trait_env.param_env);
}
- let handle_variants = |variant: VariantId,
- subst: GenericArgs<'db>,
- container: &mut _| {
- let fields = variant.fields(db);
- let field = fields.field(&field_name.as_name())?;
- let field_types = db.field_types(variant);
- *container = Either::Right(field_types[field].get().instantiate(interner, subst));
- let generic_def = match variant {
- VariantId::EnumVariantId(it) => it.loc(db).parent.into(),
- VariantId::StructId(it) => it.into(),
- VariantId::UnionId(it) => it.into(),
+ let handle_variants =
+ |variant: VariantId, subst: GenericArgs<'db>, container: &mut _| {
+ let fields = variant.fields(db);
+ let field = fields.field(&field_name.as_name())?;
+ let field_types = db.field_types(variant);
+ *container = Either::Right(
+ field_types[field].get().instantiate(interner, subst).skip_norm_wip(),
+ );
+ let generic_def = match variant {
+ VariantId::EnumVariantId(it) => it.loc(db).parent.into(),
+ VariantId::StructId(it) => it.into(),
+ VariantId::UnionId(it) => it.into(),
+ };
+ Some((
+ Either::Right(Field { parent: variant.into(), id: field }),
+ generic_def,
+ subst,
+ ))
};
- Some((
- Either::Right(Field { parent: variant.into(), id: field }),
- generic_def,
- subst,
- ))
- };
let temp_ty = Ty::new_error(interner, ErrorGuaranteed);
let (field_def, generic_def, subst) =
match std::mem::replace(&mut container, Either::Right(temp_ty)) {
@@ -957,7 +998,7 @@ impl<'db> SourceAnalyzer<'db> {
handle_variants(VariantId::from(variant_id), subst, &mut container)?
}
Either::Right(container_ty) => match container_ty.kind() {
- TyKind::Adt(adt_def, subst) => match adt_def.def_id().0 {
+ TyKind::Adt(adt_def, subst) => match adt_def.def_id() {
AdtId::StructId(id) => {
handle_variants(id.into(), subst, &mut container)?
}
@@ -1120,7 +1161,7 @@ impl<'db> SourceAnalyzer<'db> {
}
// FIXME: collectiong here shouldnt be necessary?
- let mut collector = ExprCollector::body(db, self.resolver.module(), self.file_id);
+ let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id);
let hir_path =
collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?;
let parent_hir_path = path
@@ -1202,21 +1243,15 @@ impl<'db> SourceAnalyzer<'db> {
}
if let Some(attr) = meta_path.parent_attr() {
- let adt = if let Some(field) =
- attr.syntax().parent().and_then(ast::RecordField::cast)
- {
- field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
- } else if let Some(field) =
- attr.syntax().parent().and_then(ast::TupleField::cast)
- {
- field.syntax().ancestors().take(4).find_map(ast::Adt::cast)
- } else if let Some(variant) =
- attr.syntax().parent().and_then(ast::Variant::cast)
- {
- variant.syntax().ancestors().nth(2).and_then(ast::Adt::cast)
- } else {
- None
- };
+ let adt =
+ attr.syntax().ancestors().find_map(ast::Item::cast).and_then(
+ |it| match it {
+ ast::Item::Struct(it) => Some(ast::Adt::Struct(it)),
+ ast::Item::Enum(it) => Some(ast::Adt::Enum(it)),
+ ast::Item::Union(it) => Some(ast::Adt::Union(it)),
+ _ => None,
+ },
+ );
if let Some(adt) = adt {
let ast_id = db.ast_id_map(self.file_id).ast_id(&adt);
if let Some(helpers) = self
@@ -1266,6 +1301,7 @@ impl<'db> SourceAnalyzer<'db> {
let res = resolve_hir_path_(
db,
&self.resolver,
+ self.infer_body,
&hir_path,
prefer_value_ns,
name_hygiene(db, InFile::new(self.file_id, path.syntax())),
@@ -1287,7 +1323,7 @@ impl<'db> SourceAnalyzer<'db> {
let env = self.trait_environment(db);
let (subst, expected_resolution) = match ty.kind() {
TyKind::Adt(adt_def, subst) => {
- let adt_id = adt_def.def_id().0;
+ let adt_id = adt_def.def_id();
(
GenericSubstitution::new(adt_id.into(), subst, env),
PathResolution::Def(ModuleDef::Adt(adt_id.into())),
@@ -1298,7 +1334,7 @@ impl<'db> SourceAnalyzer<'db> {
args,
..
}) => {
- let assoc_id = def_id.expect_type_alias();
+ let assoc_id = def_id.0;
(
GenericSubstitution::new(assoc_id.into(), args, env),
PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())),
@@ -1332,13 +1368,14 @@ impl<'db> SourceAnalyzer<'db> {
db: &dyn HirDatabase,
path: &ast::Path,
) -> Option<PathResolutionPerNs> {
- let mut collector = ExprCollector::body(db, self.resolver.module(), self.file_id);
+ let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id);
let hir_path =
collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?;
let (store, _) = collector.store.finish();
Some(resolve_hir_path_(
db,
&self.resolver,
+ self.infer_body,
&hir_path,
false,
name_hygiene(db, InFile::new(self.file_id, path.syntax())),
@@ -1428,7 +1465,7 @@ impl<'db> SourceAnalyzer<'db> {
.into_iter()
.map(|local_id| {
let field = FieldId { parent: variant, local_id };
- let ty = field_types[local_id].get().instantiate(interner, substs);
+ let ty = field_types[local_id].get().instantiate(interner, substs).skip_norm_wip();
(field.into(), Type::new_with_resolver_inner(db, &self.resolver, ty))
})
.collect()
@@ -1456,9 +1493,7 @@ impl<'db> SourceAnalyzer<'db> {
};
match expanded_expr {
ExprOrPatId::ExprId(expanded_expr) => walk_expr(expanded_expr),
- ExprOrPatId::PatId(expanded_pat) => {
- body.walk_exprs_in_pat(expanded_pat, &mut walk_expr)
- }
+ ExprOrPatId::PatId(expanded_pat) => body.walk_exprs_in_pat(expanded_pat, walk_expr),
}
return is_unsafe;
}
@@ -1480,6 +1515,7 @@ impl<'db> SourceAnalyzer<'db> {
db,
&self.resolver,
self.resolver.expression_store_owner(),
+ self.infer_body,
&Path::from_known_path_with_no_generic(ModPath::from_segments(
PathKind::Plain,
Some(name.clone()),
@@ -1519,6 +1555,7 @@ impl<'db> SourceAnalyzer<'db> {
db,
&self.resolver,
self.resolver.expression_store_owner(),
+ self.infer_body,
&Path::from_known_path_with_no_generic(ModPath::from_segments(
PathKind::Plain,
Some(name.clone()),
@@ -1586,28 +1623,19 @@ impl<'db> SourceAnalyzer<'db> {
hir_def::lang_item::lang_items(db, self.resolver.krate())
}
- fn lang_trait_fn(
- &self,
- db: &'db dyn HirDatabase,
- lang_trait: Option<TraitId>,
- method_name: &Name,
- ) -> Option<(TraitId, FunctionId)> {
- let trait_id = lang_trait?;
- let fn_id = trait_id.trait_items(db).method_by_name(method_name)?;
- Some((trait_id, fn_id))
- }
-
fn ty_of_expr(&self, expr: ast::Expr) -> Option<Ty<'db>> {
self.infer()?.type_of_expr_or_pat(self.expr_id(expr)?)
}
}
+// Note: the `ExprId` here does not need to be accurate, what's important is that it points at the same
+// inference root.
fn scope_for(
db: &dyn HirDatabase,
scopes: &ExprScopes,
source_map: &ExpressionStoreSourceMap,
node: InFile<&SyntaxNode>,
-) -> Option<ScopeId> {
+) -> Option<(ScopeId, ExprId)> {
node.ancestors_with_macros(db)
.take_while(|it| {
let kind = it.kind();
@@ -1618,7 +1646,7 @@ fn scope_for(
})
.filter_map(|it| it.map(ast::Expr::cast).transpose())
.filter_map(|it| source_map.node_expr(it.as_ref())?.as_expr())
- .find_map(|it| scopes.scope_for(it))
+ .find_map(|expr| scopes.scope_for(expr).map(|scope| (scope, expr)))
}
fn scope_for_offset(
@@ -1627,14 +1655,14 @@ fn scope_for_offset(
source_map: &ExpressionStoreSourceMap,
from_file: HirFileId,
offset: TextSize,
-) -> Option<ScopeId> {
+) -> Option<(ScopeId, ExprId)> {
scopes
.scope_by_expr()
.iter()
.filter_map(|(id, scope)| {
let InFile { file_id, value } = source_map.expr_syntax(id).ok()?;
if from_file == file_id {
- return Some((value.text_range(), scope));
+ return Some((value.text_range(), scope, id));
}
// FIXME handle attribute expansion
@@ -1643,13 +1671,15 @@ fn scope_for_offset(
})
.find(|it| it.file_id == from_file)
.filter(|it| it.kind() == SyntaxKind::MACRO_CALL)?;
- Some((source.text_range(), scope))
+ Some((source.text_range(), scope, id))
+ })
+ .filter(|(expr_range, _scope, _expr)| {
+ expr_range.start() <= offset && offset <= expr_range.end()
})
- .filter(|(expr_range, _scope)| expr_range.start() <= offset && offset <= expr_range.end())
// find containing scope
- .min_by_key(|(expr_range, _scope)| expr_range.len())
- .map(|(expr_range, scope)| {
- adjust(db, scopes, source_map, expr_range, from_file, offset).unwrap_or(*scope)
+ .min_by_key(|(expr_range, _scope, _expr)| expr_range.len())
+ .map(|(expr_range, scope, expr)| {
+ adjust(db, scopes, source_map, expr_range, from_file, offset).unwrap_or((*scope, expr))
})
}
@@ -1662,7 +1692,7 @@ fn adjust(
expr_range: TextRange,
from_file: HirFileId,
offset: TextSize,
-) -> Option<ScopeId> {
+) -> Option<(ScopeId, ExprId)> {
let child_scopes = scopes
.scope_by_expr()
.iter()
@@ -1674,14 +1704,14 @@ fn adjust(
}
let root = source.file_syntax(db);
let node = source.value.to_node(&root);
- Some((node.syntax().text_range(), scope))
+ Some((node.syntax().text_range(), scope, id))
})
- .filter(|&(range, _)| {
+ .filter(|&(range, _, _)| {
range.start() <= offset && expr_range.contains_range(range) && range != expr_range
});
child_scopes
- .max_by(|&(r1, _), &(r2, _)| {
+ .max_by(|&(r1, _, _), &(r2, _, _)| {
if r1.contains_range(r2) {
std::cmp::Ordering::Greater
} else if r2.contains_range(r1) {
@@ -1690,18 +1720,19 @@ fn adjust(
r1.start().cmp(&r2.start())
}
})
- .map(|(_ptr, scope)| *scope)
+ .map(|(_ptr, scope, expr)| (*scope, expr))
}
#[inline]
pub(crate) fn resolve_hir_path(
db: &dyn HirDatabase,
resolver: &Resolver<'_>,
+ infer_body: Option<InferBodyId>,
path: &Path,
hygiene: HygieneId,
store: Option<&ExpressionStore>,
) -> Option<PathResolution> {
- resolve_hir_path_(db, resolver, path, false, hygiene, store, false).any()
+ resolve_hir_path_(db, resolver, infer_body, path, false, hygiene, store, false).any()
}
#[inline]
@@ -1719,6 +1750,7 @@ pub(crate) fn resolve_hir_path_as_attr_macro(
fn resolve_hir_path_(
db: &dyn HirDatabase,
resolver: &Resolver<'_>,
+ infer_body: Option<InferBodyId>,
path: &Path,
prefer_value_ns: bool,
hygiene: HygieneId,
@@ -1728,9 +1760,17 @@ fn resolve_hir_path_(
let types = || {
let (ty, unresolved) = match path.type_anchor() {
Some(type_ref) => resolver.generic_def().and_then(|def| {
- let (_, res) =
- TyLoweringContext::new(db, resolver, store?, def, LifetimeElisionKind::Infer)
- .lower_ty_ext(type_ref);
+ let generics = OnceCell::new();
+ let (_, res) = TyLoweringContext::new(
+ db,
+ resolver,
+ store?,
+ def.into(),
+ def,
+ &generics,
+ LifetimeElisionKind::Infer,
+ )
+ .lower_ty_ext(type_ref);
res.map(|ty_ns| (ty_ns, path.segments().first()))
}),
None => {
@@ -1788,7 +1828,7 @@ fn resolve_hir_path_(
};
let body_owner = resolver.expression_store_owner();
- let values = || resolve_hir_value_path(db, resolver, body_owner, path, hygiene);
+ let values = || resolve_hir_value_path(db, resolver, body_owner, infer_body, path, hygiene);
let items = || {
resolver
@@ -1834,13 +1874,14 @@ fn resolve_hir_value_path(
db: &dyn HirDatabase,
resolver: &Resolver<'_>,
store_owner: Option<ExpressionStoreOwnerId>,
+ infer_body: Option<InferBodyId>,
path: &Path,
hygiene: HygieneId,
) -> Option<PathResolution> {
resolver.resolve_path_in_value_ns_fully(db, path, hygiene).and_then(|val| {
let res = match val {
ValueNs::LocalBinding(binding_id) => {
- let var = Local { parent: store_owner?, binding_id };
+ let var = Local { parent: store_owner?, parent_infer: infer_body?, binding_id };
PathResolution::Local(var)
}
ValueNs::FunctionId(it) => PathResolution::Def(Function::from(it).into()),
@@ -1877,9 +1918,17 @@ fn resolve_hir_path_qualifier(
(|| {
let (ty, unresolved) = match path.type_anchor() {
Some(type_ref) => resolver.generic_def().and_then(|def| {
- let (_, res) =
- TyLoweringContext::new(db, resolver, store, def, LifetimeElisionKind::Infer)
- .lower_ty_ext(type_ref);
+ let generics = OnceCell::new();
+ let (_, res) = TyLoweringContext::new(
+ db,
+ resolver,
+ store,
+ def.into(),
+ def,
+ &generics,
+ LifetimeElisionKind::Infer,
+ )
+ .lower_ty_ext(type_ref);
res.map(|ty_ns| (ty_ns, path.segments().first()))
}),
None => {
diff --git a/crates/hir/src/term_search.rs b/crates/hir/src/term_search.rs
index f2dc1ce798..af2371d493 100644
--- a/crates/hir/src/term_search.rs
+++ b/crates/hir/src/term_search.rs
@@ -214,11 +214,11 @@ impl<'db> LookupTable<'db> {
/// Context for the `term_search` function
#[derive(Debug)]
-pub struct TermSearchCtx<'db, DB: HirDatabase> {
+pub struct TermSearchCtx<'a, 'db, DB: HirDatabase> {
/// Semantics for the program
- pub sema: &'db Semantics<'db, DB>,
+ pub sema: &'a Semantics<'db, DB>,
/// Semantic scope, captures context for the term search
- pub scope: &'db SemanticsScope<'db>,
+ pub scope: &'a SemanticsScope<'db>,
/// Target / expected output type
pub goal: Type<'db>,
/// Configuration for term search
@@ -263,7 +263,7 @@ impl Default for TermSearchConfig {
/// Note that there are usually more ways we can get to the `goal` type but some are discarded to
/// reduce the memory consumption. It is also unlikely anyone is willing ti browse through
/// thousands of possible responses so we currently take first 10 from every tactic.
-pub fn term_search<'db, DB: HirDatabase>(ctx: &'db TermSearchCtx<'db, DB>) -> Vec<Expr<'db>> {
+pub fn term_search<'db, DB: HirDatabase>(ctx: &TermSearchCtx<'_, 'db, DB>) -> Vec<Expr<'db>> {
let module = ctx.scope.module();
let mut defs = FxHashSet::default();
defs.insert(ScopeDef::ModuleDef(ModuleDef::Module(module)));
diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs
index c7ef4e5d5d..8700326e17 100644
--- a/crates/hir/src/term_search/tactics.rs
+++ b/crates/hir/src/term_search/tactics.rs
@@ -41,7 +41,7 @@ use super::{LookupTable, NewTypesKey, TermSearchCtx};
/// _Note that there is no use of calling this tactic in every iteration as the output does not
/// depend on the current state of `lookup`_
pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>(
- ctx: &'a TermSearchCtx<'db, DB>,
+ ctx: &'a TermSearchCtx<'_, 'db, DB>,
defs: &'a FxHashSet<ScopeDef>,
lookup: &'lt mut LookupTable<'db>,
) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
@@ -53,14 +53,15 @@ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>(
ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(Expr::ConstParam(*it)),
ScopeDef::Local(it) => {
if ctx.config.enable_borrowcheck {
- let borrowck = db.borrowck(it.parent.as_def_with_body()?).ok()?;
+ let borrowck = db.borrowck(it.parent_infer).ok()?;
let invalid = borrowck.iter().any(|b| {
+ let mir_body = b.mir_body(ctx.sema.db);
b.partially_moved.iter().any(|moved| {
- Some(&moved.local) == b.mir_body.binding_locals.get(it.binding_id)
+ Some(&moved.local) == mir_body.binding_locals.get(it.binding_id)
}) || b.borrow_regions.iter().any(|region| {
// Shared borrows are fine
- Some(&region.local) == b.mir_body.binding_locals.get(it.binding_id)
+ Some(&region.local) == mir_body.binding_locals.get(it.binding_id)
&& region.kind != BorrowKind::Shared
})
});
@@ -105,7 +106,7 @@ pub(super) fn trivial<'a, 'lt, 'db, DB: HirDatabase>(
/// _Note that there is no use of calling this tactic in every iteration as the output does not
/// depend on the current state of `lookup`_
pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>(
- ctx: &'a TermSearchCtx<'db, DB>,
+ ctx: &'a TermSearchCtx<'_, 'db, DB>,
defs: &'a FxHashSet<ScopeDef>,
lookup: &'lt mut LookupTable<'db>,
) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
@@ -153,7 +154,7 @@ pub(super) fn assoc_const<'a, 'lt, 'db, DB: HirDatabase>(
/// * `lookup` - Lookup table for types
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>(
- ctx: &'a TermSearchCtx<'db, DB>,
+ ctx: &'a TermSearchCtx<'_, 'db, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'lt mut LookupTable<'db>,
should_continue: &'a dyn std::ops::Fn() -> bool,
@@ -302,7 +303,7 @@ pub(super) fn data_constructor<'a, 'lt, 'db, DB: HirDatabase>(
/// * `lookup` - Lookup table for types
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>(
- ctx: &'a TermSearchCtx<'db, DB>,
+ ctx: &'a TermSearchCtx<'_, 'db, DB>,
defs: &'a FxHashSet<ScopeDef>,
lookup: &'lt mut LookupTable<'db>,
should_continue: &'a dyn std::ops::Fn() -> bool,
@@ -437,7 +438,7 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>(
/// * `lookup` - Lookup table for types
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>(
- ctx: &'a TermSearchCtx<'db, DB>,
+ ctx: &'a TermSearchCtx<'_, 'db, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'lt mut LookupTable<'db>,
should_continue: &'a dyn std::ops::Fn() -> bool,
@@ -556,7 +557,7 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>(
/// * `lookup` - Lookup table for types
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>(
- ctx: &'a TermSearchCtx<'db, DB>,
+ ctx: &'a TermSearchCtx<'_, 'db, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'lt mut LookupTable<'db>,
should_continue: &'a dyn std::ops::Fn() -> bool,
@@ -598,7 +599,7 @@ pub(super) fn struct_projection<'a, 'lt, 'db, DB: HirDatabase>(
/// * `defs` - Set of items in scope at term search target location
/// * `lookup` - Lookup table for types
pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>(
- ctx: &'a TermSearchCtx<'db, DB>,
+ ctx: &'a TermSearchCtx<'_, 'db, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'lt mut LookupTable<'db>,
) -> impl Iterator<Item = Expr<'db>> + use<'a, 'db, 'lt, DB> {
@@ -632,7 +633,7 @@ pub(super) fn famous_types<'a, 'lt, 'db, DB: HirDatabase>(
/// * `lookup` - Lookup table for types
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>(
- ctx: &'a TermSearchCtx<'db, DB>,
+ ctx: &'a TermSearchCtx<'_, 'db, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'lt mut LookupTable<'db>,
should_continue: &'a dyn std::ops::Fn() -> bool,
@@ -738,7 +739,7 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>(
/// * `lookup` - Lookup table for types
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn make_tuple<'a, 'lt, 'db, DB: HirDatabase>(
- ctx: &'a TermSearchCtx<'db, DB>,
+ ctx: &'a TermSearchCtx<'_, 'db, DB>,
_defs: &'a FxHashSet<ScopeDef>,
lookup: &'lt mut LookupTable<'db>,
should_continue: &'a dyn std::ops::Fn() -> bool,
diff --git a/crates/ide-assists/src/assist_context.rs b/crates/ide-assists/src/assist_context.rs
index 207a7548f4..d8e097f0e2 100644
--- a/crates/ide-assists/src/assist_context.rs
+++ b/crates/ide-assists/src/assist_context.rs
@@ -13,7 +13,7 @@ use crate::{
Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel, assist_config::AssistConfig,
};
-pub(crate) use ide_db::source_change::{SourceChangeBuilder, TreeMutator};
+pub(crate) use ide_db::source_change::SourceChangeBuilder;
/// `AssistContext` allows to apply an assist or check if it could be applied.
///
@@ -45,9 +45,9 @@ pub(crate) use ide_db::source_change::{SourceChangeBuilder, TreeMutator};
/// Note, however, that we don't actually use such two-phase logic at the
/// moment, because the LSP API is pretty awkward in this place, and it's much
/// easier to just compute the edit eagerly :-)
-pub(crate) struct AssistContext<'a> {
+pub(crate) struct AssistContext<'a, 'db> {
pub(crate) config: &'a AssistConfig,
- pub(crate) sema: Semantics<'a, RootDatabase>,
+ pub(crate) sema: Semantics<'db, RootDatabase>,
frange: FileRange,
trimmed_range: TextRange,
source_file: SourceFile,
@@ -57,12 +57,12 @@ pub(crate) struct AssistContext<'a> {
covering_element: SyntaxElement,
}
-impl<'a> AssistContext<'a> {
+impl<'a, 'db> AssistContext<'a, 'db> {
pub(crate) fn new(
- sema: Semantics<'a, RootDatabase>,
+ sema: Semantics<'db, RootDatabase>,
config: &'a AssistConfig,
frange: FileRange,
- ) -> AssistContext<'a> {
+ ) -> AssistContext<'a, 'db> {
let source_file = sema.parse(frange.file_id);
let start = frange.range.start();
@@ -95,7 +95,7 @@ impl<'a> AssistContext<'a> {
}
}
- pub(crate) fn db(&self) -> &'a RootDatabase {
+ pub(crate) fn db(&self) -> &'db RootDatabase {
self.sema.db
}
@@ -165,7 +165,7 @@ pub(crate) struct Assists {
}
impl Assists {
- pub(crate) fn new(ctx: &AssistContext<'_>, resolve: AssistResolveStrategy) -> Assists {
+ pub(crate) fn new(ctx: &AssistContext<'_, '_>, resolve: AssistResolveStrategy) -> Assists {
Assists {
resolve,
file: ctx.frange.file_id.file_id(ctx.db()),
diff --git a/crates/ide-assists/src/handlers/add_braces.rs b/crates/ide-assists/src/handlers/add_braces.rs
index c5ec88ffb8..4bd987a371 100644
--- a/crates/ide-assists/src/handlers/add_braces.rs
+++ b/crates/ide-assists/src/handlers/add_braces.rs
@@ -44,7 +44,7 @@ use crate::{AssistContext, AssistId, Assists};
// };
// }
// ```
-pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let (expr_type, expr) = get_replacement_node(ctx)?;
acc.add(
@@ -74,7 +74,7 @@ enum ParentType {
Assignment,
}
-fn get_replacement_node(ctx: &AssistContext<'_>) -> Option<(ParentType, ast::Expr)> {
+fn get_replacement_node(ctx: &AssistContext<'_, '_>) -> Option<(ParentType, ast::Expr)> {
let node = ctx.find_node_at_offset::<Either<ast::MatchArm, ast::ClosureExpr>>();
let (parent_type, body) = if let Some(eq_token) = ctx.find_token_syntax_at_offset(T![=]) {
let parent = eq_token.parent()?;
diff --git a/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs b/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs
index 1809b8f305..40d48bee3c 100644
--- a/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs
+++ b/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs
@@ -29,7 +29,7 @@ use crate::{AssistContext, Assists};
// ```
pub(crate) fn add_explicit_method_call_deref(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
if ctx.has_empty_selection() {
return None;
diff --git a/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs b/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs
index 75c5f84b85..db9f9dc074 100644
--- a/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs
+++ b/crates/ide-assists/src/handlers/add_explicit_enum_discriminant.rs
@@ -30,7 +30,7 @@ use crate::{AssistContext, Assists, utils::add_group_separators};
// ```
pub(crate) fn add_explicit_enum_discriminant(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let enum_node = ctx.find_node_at_offset::<ast::Enum>()?;
let enum_def = ctx.sema.to_def(&enum_node)?;
diff --git a/crates/ide-assists/src/handlers/add_explicit_type.rs b/crates/ide-assists/src/handlers/add_explicit_type.rs
index 0dd01b67e8..e543005e67 100644
--- a/crates/ide-assists/src/handlers/add_explicit_type.rs
+++ b/crates/ide-assists/src/handlers/add_explicit_type.rs
@@ -20,7 +20,7 @@ use crate::{AssistContext, AssistId, Assists};
// let x: i32 = 92;
// }
// ```
-pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let syntax_node = ctx.find_node_at_offset::<Either<LetStmt, Param>>()?;
let (ascribed_ty, expr, pat) = if let Either::Left(let_stmt) = syntax_node {
let cursor_in_range = {
@@ -208,8 +208,6 @@ fn main() {
}
"#,
);
- // note: this may break later if we add more consteval. it just needs to be something that our
- // consteval engine doesn't understand
check_assist_not_applicable(
add_explicit_type,
r#"
diff --git a/crates/ide-assists/src/handlers/add_label_to_loop.rs b/crates/ide-assists/src/handlers/add_label_to_loop.rs
index 41e9b6cc84..b2194ab3dc 100644
--- a/crates/ide-assists/src/handlers/add_label_to_loop.rs
+++ b/crates/ide-assists/src/handlers/add_label_to_loop.rs
@@ -30,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists};
// }
// }
// ```
-pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let loop_expr = ctx.find_node_at_offset::<ast::AnyHasLoopBody>()?;
let loop_kw = loop_token(&loop_expr)?;
if loop_expr.label().is_some() || !loop_kw.text_range().contains_inclusive(ctx.offset()) {
@@ -86,7 +86,7 @@ fn loop_token(loop_expr: &ast::AnyHasLoopBody) -> Option<syntax::SyntaxToken> {
fn insert_label_after_token(
editor: &SyntaxEditor,
token: &SyntaxToken,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
builder: &mut SourceChangeBuilder,
) {
let make = editor.make();
diff --git a/crates/ide-assists/src/handlers/add_lifetime_to_type.rs b/crates/ide-assists/src/handlers/add_lifetime_to_type.rs
index 265ee3d2d4..dc847dcdbe 100644
--- a/crates/ide-assists/src/handlers/add_lifetime_to_type.rs
+++ b/crates/ide-assists/src/handlers/add_lifetime_to_type.rs
@@ -22,7 +22,7 @@ use crate::{AssistContext, AssistId, Assists};
// y: u32,
// }
// ```
-pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let ref_type_focused = ctx.find_node_at_offset::<ast::RefType>()?;
if ref_type_focused.lifetime().is_some_and(|lifetime| lifetime.text() != "'_") {
return None;
diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index d1f1f9f123..efbe3817e1 100644
--- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -45,7 +45,10 @@ use crate::{
// }
// }
// ```
-pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn add_missing_impl_members(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
add_missing_impl_members_inner(
acc,
ctx,
@@ -89,7 +92,7 @@ pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext<'_
// ```
pub(crate) fn add_missing_default_members(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
add_missing_impl_members_inner(
acc,
@@ -103,7 +106,7 @@ pub(crate) fn add_missing_default_members(
fn add_missing_impl_members_inner(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
mode: DefaultMethods,
ignore_items: IgnoreAssocItems,
assist_id: &'static str,
@@ -224,7 +227,7 @@ fn add_missing_impl_members_inner(
fn try_gen_trait_body(
make: &SyntaxFactory,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
func: &ast::Fn,
trait_ref: hir::TraitRef<'_>,
impl_def: &ast::Impl,
@@ -2620,4 +2623,22 @@ impl Allocator for System {
"#,
);
}
+
+ #[test]
+ fn does_not_include_defaulted_assoc_types() {
+ check_assist_not_applicable(
+ add_missing_impl_members,
+ r#"
+trait Trait {
+ type NotRequired = ();
+}
+
+struct Struct;
+
+impl Trait for Struct {
+ $0
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index 3c33ddec31..667a1d7813 100644
--- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -4,7 +4,7 @@ use either::Either;
use hir::{Adt, AsAssocItem, Crate, FindPathConfig, HasAttrs, ModuleDef, Semantics};
use ide_db::RootDatabase;
use ide_db::syntax_helpers::suggest_name;
-use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast};
+use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory};
use itertools::Itertools;
use syntax::ast::edit::IndentLevel;
use syntax::ast::syntax_factory::SyntaxFactory;
@@ -38,7 +38,7 @@ use crate::{AssistContext, AssistId, Assists, utils};
// }
// }
// ```
-pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let match_expr = ctx.find_node_at_offset_with_descend::<ast::MatchExpr>()?;
let match_arm_list = match_expr.match_arm_list()?;
let arm_list_range = ctx.sema.original_range_opt(match_arm_list.syntax())?;
@@ -303,7 +303,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
}
fn cursor_at_trivial_match_arm_list(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
match_expr: &MatchExpr,
match_arm_list: &MatchArmList,
) -> Option<()> {
@@ -357,7 +357,7 @@ struct ArmsEdit {
}
impl ArmsEdit {
- fn remove_wildcard_arms(&mut self, ctx: &AssistContext<'_>, editor: &SyntaxEditor) {
+ fn remove_wildcard_arms(&mut self, ctx: &AssistContext<'_, '_>, editor: &SyntaxEditor) {
for arm in self.match_arm_list.arms() {
if !matches!(arm.pat(), Some(Pat::WildcardPat(_))) {
self.last_arm = Some(arm);
@@ -417,7 +417,7 @@ impl ArmsEdit {
fn add_comma_after_last_arm(
&self,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
make: &SyntaxFactory,
editor: &SyntaxEditor,
) {
@@ -432,7 +432,7 @@ impl ArmsEdit {
fn cover_edit_range(
&self,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
node: &impl AstNode,
) -> Option<std::ops::RangeInclusive<syntax::SyntaxElement>> {
let range = ctx.sema.original_range_opt(node.syntax())?;
@@ -581,7 +581,7 @@ fn resolve_array_of_enum_def(
}
fn build_pat(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
make: &SyntaxFactory,
module: hir::Module,
var: ExtendedVariant,
@@ -602,7 +602,11 @@ fn build_pat(
false,
)
} else {
- mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition)
+ mod_path_to_ast_with_factory(
+ make,
+ &module.find_path(db, ModuleDef::from(var), cfg)?,
+ edition,
+ )
};
let fields = var.fields(db);
let pat: ast::Pat = match var.kind(db) {
diff --git a/crates/ide-assists/src/handlers/add_return_type.rs b/crates/ide-assists/src/handlers/add_return_type.rs
index 7934a80bfa..e7203a96bb 100644
--- a/crates/ide-assists/src/handlers/add_return_type.rs
+++ b/crates/ide-assists/src/handlers/add_return_type.rs
@@ -16,7 +16,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
// fn foo() -> i32 { 42i32 }
// ```
-pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let (fn_type, tail_expr, builder_edit_pos) = extract_tail(ctx)?;
let module = ctx.sema.scope(tail_expr.syntax())?.module();
let ty = ctx.sema.type_of_expr(&peel_blocks(tail_expr.clone()))?.adjusted();
@@ -133,7 +133,7 @@ fn peel_blocks(mut expr: ast::Expr) -> ast::Expr {
expr
}
-fn extract_tail(ctx: &AssistContext<'_>) -> Option<(FnType, ast::Expr, InsertOrReplace)> {
+fn extract_tail(ctx: &AssistContext<'_, '_>) -> Option<(FnType, ast::Expr, InsertOrReplace)> {
let node = ctx.find_node_at_offset::<Either<ast::ClosureExpr, ast::Fn>>()?;
let (fn_type, tail_expr, return_type_range, action) = match node {
Either::Left(closure) => {
diff --git a/crates/ide-assists/src/handlers/add_turbo_fish.rs b/crates/ide-assists/src/handlers/add_turbo_fish.rs
index dcd2124f7b..53205910c8 100644
--- a/crates/ide-assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ide-assists/src/handlers/add_turbo_fish.rs
@@ -28,7 +28,7 @@ use crate::{
// let x = make::<${0:_}>();
// }
// ```
-pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let turbofish_target =
ctx.find_node_at_offset::<ast::PathSegment>().map(Either::Left).or_else(|| {
let callable_expr = ctx.find_node_at_offset::<ast::CallableExpr>()?;
diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs
index b87a757047..10262445a2 100644
--- a/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -37,7 +37,7 @@ use crate::{AssistContext, AssistId, Assists, utils::invert_boolean_expression};
// if !(x == 4 && y >= 3.14) {}
// }
// ```
-pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let mut bin_expr = if let Some(not) = ctx.find_token_syntax_at_offset(T![!])
&& let Some(NodeOrToken::Node(next)) = not.next_sibling_or_token()
&& let Some(paren) = ast::ParenExpr::cast(next)
@@ -189,8 +189,17 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
// }
// }
// ```
-pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
+pub(crate) fn apply_demorgan_iterator(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
+ let method_call: ast::MethodCallExpr = ctx.find_node_at_offset().or_else(|| {
+ let parent = ctx.find_token_syntax_at_offset(T![!])?.parent()?;
+ match ast::PrefixExpr::cast(parent)?.expr()? {
+ ast::Expr::MethodCallExpr(method_call) => Some(method_call),
+ _ => None,
+ }
+ })?;
let (name, arg_expr) = validate_method_call_expr(ctx, &method_call)?;
let ast::Expr::ClosureExpr(closure_expr) = arg_expr else { return None };
@@ -210,6 +219,8 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
let new_name = match name.text().as_str() {
"all" => make.name_ref("any"),
"any" => make.name_ref("all"),
+ "is_some_and" => make.name_ref("is_none_or"),
+ "is_none_or" => make.name_ref("is_some_and"),
_ => unreachable!(),
};
editor.replace(name.syntax(), new_name.syntax());
@@ -245,14 +256,17 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
/// Ensures that the method call is to `Iterator::all` or `Iterator::any`.
fn validate_method_call_expr(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
method_call: &ast::MethodCallExpr,
) -> Option<(ast::NameRef, ast::Expr)> {
let name_ref = method_call.name_ref()?;
+ let arg_expr = method_call.arg_list()?.args().next()?;
+ if name_ref.text() == "is_some_and" || name_ref.text() == "is_none_or" {
+ return Some((name_ref, arg_expr));
+ }
if name_ref.text() != "all" && name_ref.text() != "any" {
return None;
}
- let arg_expr = method_call.arg_list()?.args().next()?;
let sema = &ctx.sema;
@@ -394,6 +408,26 @@ fn f() { if let 1 = 1 &&$0 true { } }
}
#[test]
+ fn demorgan_iterator_on_not() {
+ check_assist(
+ apply_demorgan_iterator,
+ r#"
+//- minicore: iterator
+fn main() {
+ let arr = [1, 2, 3];
+ let cond = $0!arr.into_iter().all(|num| num != 4);
+}
+"#,
+ r#"
+fn main() {
+ let arr = [1, 2, 3];
+ let cond = arr.into_iter().any(|num| num == 4);
+}
+"#,
+ );
+ }
+
+ #[test]
fn demorgan_keep_pars_for_op_precedence() {
check_assist(
apply_demorgan,
@@ -644,6 +678,51 @@ fn main() {
}
#[test]
+ fn demorgan_option_is_some_and() {
+ check_assist(
+ apply_demorgan_iterator,
+ r#"
+//- minicore: option
+fn main() {
+ let cond = Some(2);
+ if !cond.$0is_some_and(|num| num > 3) {
+ println!("foo");
+ }
+}
+"#,
+ r#"
+fn main() {
+ let cond = Some(2);
+ if cond.is_none_or(|num| num <= 3) {
+ println!("foo");
+ }
+}
+"#,
+ );
+
+ check_assist(
+ apply_demorgan_iterator,
+ r#"
+//- minicore: option
+fn main() {
+ let cond = Some(2);
+ if !cond.$0is_none_or(|num| num > 3) {
+ println!("foo");
+ }
+}
+"#,
+ r#"
+fn main() {
+ let cond = Some(2);
+ if cond.is_some_and(|num| num <= 3) {
+ println!("foo");
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn demorgan_method_call_receiver() {
check_assist(
apply_demorgan,
diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs
index f9d618790c..ac0bae7cd9 100644
--- a/crates/ide-assists/src/handlers/auto_import.rs
+++ b/crates/ide-assists/src/handlers/auto_import.rs
@@ -7,7 +7,7 @@ use ide_db::{
helpers::mod_path_to_ast,
imports::{
import_assets::{ImportAssets, ImportCandidate, LocatedImport, TraitImportCandidate},
- insert_use::{ImportScope, insert_use, insert_use_as_alias},
+ insert_use::{ImportScope, insert_use_as_alias_with_editor, insert_use_with_editor},
},
};
use syntax::{AstNode, Edition, SyntaxNode, ast, match_ast};
@@ -91,7 +91,7 @@ use crate::{AssistContext, AssistId, Assists, GroupLabel};
// }
// # pub mod std { pub mod collections { pub struct HashMap { } } }
// ```
-pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let cfg = ctx.config.import_path_config();
let (import_assets, syntax_under_caret, expected) = find_importable_node(ctx)?;
@@ -125,8 +125,14 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
(AssistId::quick_fix("auto_import"), import_path.display(ctx.db(), edition));
let add_normal_import = |acc: &mut Assists, label| {
acc.add_group(&group_label, assist_id, label, range, |builder| {
- let scope = builder.make_import_scope_mut(scope.clone());
- insert_use(&scope, mod_path_to_ast(&import_path, edition), &ctx.config.insert_use);
+ let editor = builder.make_editor(scope.as_syntax_node());
+ insert_use_with_editor(
+ &scope,
+ mod_path_to_ast(&import_path, edition),
+ &ctx.config.insert_use,
+ &editor,
+ );
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
})
};
let add_underscore_import = |acc: &mut Assists, name: &TraitImportCandidate<'_>, label| {
@@ -139,13 +145,15 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
name.assoc_item_name.text()
));
acc.add_group(&group_label, assist_id, label, range, |builder| {
- let scope = builder.make_import_scope_mut(scope.clone());
- insert_use_as_alias(
+ let editor = builder.make_editor(scope.as_syntax_node());
+ insert_use_as_alias_with_editor(
&scope,
mod_path_to_ast(&import_path, edition),
&ctx.config.insert_use,
edition,
+ &editor,
);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
});
};
@@ -170,8 +178,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
Some(())
}
-pub(super) fn find_importable_node<'a: 'db, 'db>(
- ctx: &'a AssistContext<'db>,
+pub(super) fn find_importable_node<'db>(
+ ctx: &AssistContext<'_, 'db>,
) -> Option<(ImportAssets<'db>, SyntaxNode, Option<Type<'db>>)> {
// Deduplicate this with the `expected_type_and_name` logic for completions
let expected = |expr_or_pat: Either<ast::Expr, ast::Pat>| match expr_or_pat {
@@ -248,7 +256,7 @@ fn group_label(import_candidate: &ImportCandidate<'_>) -> GroupLabel {
/// Determine how relevant a given import is in the current context. Higher scores are more
/// relevant.
pub(crate) fn relevance_score(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
import: &LocatedImport,
expected: Option<&Type<'_>>,
current_module: Option<&Module>,
diff --git a/crates/ide-assists/src/handlers/bind_unused_param.rs b/crates/ide-assists/src/handlers/bind_unused_param.rs
index 50e4a367e9..5d3d8127ba 100644
--- a/crates/ide-assists/src/handlers/bind_unused_param.rs
+++ b/crates/ide-assists/src/handlers/bind_unused_param.rs
@@ -18,7 +18,7 @@ use syntax::{
// let _ = x;
// }
// ```
-pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let param: ast::Param = ctx.find_node_at_offset()?;
let Some(ast::Pat::IdentPat(ident_pat)) = param.pat() else { return None };
diff --git a/crates/ide-assists/src/handlers/change_visibility.rs b/crates/ide-assists/src/handlers/change_visibility.rs
index 7119d5b9c2..f17197a750 100644
--- a/crates/ide-assists/src/handlers/change_visibility.rs
+++ b/crates/ide-assists/src/handlers/change_visibility.rs
@@ -21,14 +21,14 @@ use crate::{AssistContext, AssistId, Assists, utils::vis_offset};
// ```
// pub(crate) fn frobnicate() {}
// ```
-pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
if let Some(vis) = ctx.find_node_at_offset::<ast::Visibility>() {
return change_vis(acc, vis);
}
add_vis(acc, ctx)
}
-fn add_vis(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+fn add_vis(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let item_keyword = ctx.token_at_offset().find(|leaf| {
matches!(
leaf.kind(),
diff --git a/crates/ide-assists/src/handlers/convert_bool_then.rs b/crates/ide-assists/src/handlers/convert_bool_then.rs
index a2a71bcba6..5ecc11ccc5 100644
--- a/crates/ide-assists/src/handlers/convert_bool_then.rs
+++ b/crates/ide-assists/src/handlers/convert_bool_then.rs
@@ -38,7 +38,10 @@ use crate::{
// cond.then(|| val)
// }
// ```
-pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_if_to_bool_then(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
// FIXME applies to match as well
let expr = ctx.find_node_at_offset::<ast::IfExpr>()?;
if !expr.if_token()?.text_range().contains_inclusive(ctx.offset()) {
@@ -153,7 +156,10 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_>
// }
// }
// ```
-pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_bool_then_to_if(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let name_ref = ctx.find_node_at_offset::<ast::NameRef>()?;
let mcall = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
let receiver = mcall.receiver()?;
diff --git a/crates/ide-assists/src/handlers/convert_bool_to_enum.rs b/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
index e88778a62e..4c3168219f 100644
--- a/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
+++ b/crates/ide-assists/src/handlers/convert_bool_to_enum.rs
@@ -1,12 +1,13 @@
use either::Either;
use hir::ModuleDef;
+use ide_db::imports::insert_use::insert_use_with_editor;
use ide_db::text_edit::TextRange;
use ide_db::{
- FxHashSet,
+ FileId, FxHashSet,
assists::AssistId,
defs::Definition,
- helpers::mod_path_to_ast,
- imports::insert_use::{ImportScope, insert_use},
+ helpers::mod_path_to_ast_with_factory,
+ imports::insert_use::ImportScope,
search::{FileReference, UsageSearchResult},
source_change::SourceChangeBuilder,
};
@@ -52,7 +53,7 @@ use crate::{
// }
// }
// ```
-pub(crate) fn convert_bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let BoolNodeData { target_node, name, ty_annotation, initializer, definition } =
find_bool_node(ctx)?;
let target_module = ctx.sema.scope(&target_node)?.module().nearest_non_block_module(ctx.db());
@@ -85,8 +86,10 @@ pub(crate) fn convert_bool_to_enum(acc: &mut Assists, ctx: &AssistContext<'_>) -
&mut delayed_mutations,
&make,
);
- for (scope, path) in delayed_mutations {
- insert_use(&scope, path, &ctx.config.insert_use);
+ for (file_id, scope, path) in delayed_mutations {
+ let editor = edit.make_editor(scope.as_syntax_node());
+ insert_use_with_editor(&scope, path, &ctx.config.insert_use, &editor);
+ edit.add_file_edits(file_id, editor);
}
},
)
@@ -101,7 +104,7 @@ struct BoolNodeData {
}
/// Attempts to find an appropriate node to apply the action to.
-fn find_bool_node(ctx: &AssistContext<'_>) -> Option<BoolNodeData> {
+fn find_bool_node(ctx: &AssistContext<'_, '_>) -> Option<BoolNodeData> {
let name = ctx.find_node_at_offset::<ast::Name>()?;
if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
@@ -208,15 +211,16 @@ fn bool_expr_to_enum_expr(expr: ast::Expr, make: &SyntaxFactory) -> ast::Expr {
/// Replaces all usages of the target identifier, both when read and written to.
fn replace_usages(
edit: &mut SourceChangeBuilder,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
usages: UsageSearchResult,
target_definition: Definition,
target_module: &hir::Module,
- delayed_mutations: &mut Vec<(ImportScope, ast::Path)>,
+ delayed_mutations: &mut Vec<(FileId, ImportScope, ast::Path)>,
make: &SyntaxFactory,
) {
for (file_id, references) in usages {
- edit.edit_file(file_id.file_id(ctx.db()));
+ let vfs_file_id = file_id.file_id(ctx.db());
+ edit.edit_file(vfs_file_id);
let refs_with_imports =
augment_references_with_imports(ctx, references, target_module, make);
@@ -323,8 +327,7 @@ fn replace_usages(
// add imports across modules where needed
if let Some((scope, path)) = import_data {
- let scope = edit.make_import_scope_mut(scope);
- delayed_mutations.push((scope, path));
+ delayed_mutations.push((vfs_file_id, scope, path));
}
},
)
@@ -338,7 +341,7 @@ struct FileReferenceWithImport {
}
fn augment_references_with_imports(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
references: Vec<FileReference>,
target_module: &hir::Module,
make: &SyntaxFactory,
@@ -373,7 +376,7 @@ fn augment_references_with_imports(
)
.map(|mod_path| {
make.path_concat(
- mod_path_to_ast(&mod_path, edition),
+ mod_path_to_ast_with_factory(make, &mod_path, edition),
make.path_from_text("Bool"),
)
})?;
@@ -469,7 +472,7 @@ fn find_method_call_expr_usage(name: &ast::NameLike) -> Option<ast::Expr> {
/// Adds the definition of the new enum before the target node.
fn add_enum_def(
edit: &mut SourceChangeBuilder,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
usages: &UsageSearchResult,
target_node: SyntaxNode,
target_module: &hir::Module,
diff --git a/crates/ide-assists/src/handlers/convert_char_literal.rs b/crates/ide-assists/src/handlers/convert_char_literal.rs
index 0a50ba86ba..b23525665a 100644
--- a/crates/ide-assists/src/handlers/convert_char_literal.rs
+++ b/crates/ide-assists/src/handlers/convert_char_literal.rs
@@ -12,7 +12,7 @@ use crate::{AssistContext, AssistId, Assists, GroupLabel};
// ```
// const _: char = '\x61';
// ```
-pub(crate) fn convert_char_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_char_literal(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
if !ctx.has_empty_selection() {
return None;
}
diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
index acade43397..e6d31b9660 100644
--- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
+++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -11,10 +11,10 @@ use syntax::{
ast::{
self, HasArgList, HasGenericParams, HasName,
edit::{AstNodeEdit, IndentLevel},
- make,
+ syntax_factory::SyntaxFactory,
},
hacks::parse_expr_from_str,
- ted,
+ syntax_editor::SyntaxEditor,
};
use crate::assist_context::{AssistContext, Assists};
@@ -51,7 +51,7 @@ use crate::assist_context::{AssistContext, Assists};
// closure("abc", &mut s);
// }
// ```
-pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let closure = ctx.find_node_at_offset::<ast::ClosureExpr>()?;
if ctx.find_node_at_offset::<ast::Expr>() != Some(ast::Expr::ClosureExpr(closure.clone())) {
// Not inside the parameter list.
@@ -64,6 +64,10 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
_ => None,
}
});
+
+ let (editor, source_root) = SyntaxEditor::new(ctx.source_file().syntax().clone());
+ let make = editor.make();
+
let module = ctx.sema.scope(closure.syntax())?.module();
let closure_ty = ctx.sema.type_of_expr(&closure.clone().into())?;
let callable = closure_ty.original.as_callable(ctx.db())?;
@@ -85,14 +89,15 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
let ty = param_ty
.display_source_code(ctx.db(), module.into(), true)
.unwrap_or_else(|_| "_".to_owned());
- Some(make::param(node.pat()?, make::ty(&ty)))
+ Some(make.param(node.pat()?, make.ty(&ty)))
}
}
})
.collect::<Option<Vec<_>>>()?;
let capture_params_start = params.len();
- let mut body = closure.body()?.clone_for_update();
+ let closure_param_list = syntax::AstPtr::new(&closure.param_list()?);
+ let body = closure.body()?;
let mut is_gen = false;
let mut is_async = closure.async_token().is_some();
if is_async {
@@ -107,37 +112,30 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
{
is_async = true;
ret_ty = ret_ty.future_output(ctx.db())?;
- let token_idx = async_token.index();
- let whitespace_tokens_after_count = async_token
+ let end = async_token
.siblings_with_tokens(Direction::Next)
.skip(1)
- .take_while(|token| token.kind() == SyntaxKind::WHITESPACE)
- .count();
- body.syntax().splice_children(
- token_idx..token_idx + whitespace_tokens_after_count + 1,
- Vec::new(),
- );
+ .take_while(|it| it.kind() == SyntaxKind::WHITESPACE)
+ .last()
+ .unwrap_or_else(|| async_token.clone().into());
+ editor.delete_all(async_token.into()..=end);
}
if let Some(gen_token) = block.gen_token() {
is_gen = true;
ret_ty = ret_ty.iterator_item(ctx.db())?;
- let token_idx = gen_token.index();
- let whitespace_tokens_after_count = gen_token
+ let end = gen_token
.siblings_with_tokens(Direction::Next)
.skip(1)
- .take_while(|token| token.kind() == SyntaxKind::WHITESPACE)
- .count();
- body.syntax().splice_children(
- token_idx..token_idx + whitespace_tokens_after_count + 1,
- Vec::new(),
- );
+ .take_while(|it| it.kind() == SyntaxKind::WHITESPACE)
+ .last()
+ .unwrap_or_else(|| gen_token.clone().into());
+ editor.delete_all(gen_token.into()..=end);
}
if block.try_block_modifier().is_none()
&& block.unsafe_token().is_none()
&& block.label().is_none()
&& block.const_token().is_none()
- && block.async_token().is_none()
{
wrap_body_in_block = false;
}
@@ -148,17 +146,17 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
"Convert closure to fn",
closure.param_list()?.syntax().text_range(),
|builder| {
+ let make = editor.make();
let closure_name_or_default = closure_name
.as_ref()
.map(|(_, _, it)| it.clone())
- .unwrap_or_else(|| make::name("fun_name"));
+ .unwrap_or_else(|| make.name("fun_name"));
let captures = closure_ty.captured_items(ctx.db());
let capture_tys =
captures.iter().map(|capture| capture.captured_ty(ctx.db())).collect::<Vec<_>>();
let mut captures_as_args = Vec::with_capacity(captures.len());
- let body_root = body.syntax().ancestors().last().unwrap();
// We need to defer this work because otherwise the text range of elements is being messed up, and
// replacements for the next captures won't work.
let mut capture_usages_replacement_map = Vec::with_capacity(captures.len());
@@ -167,9 +165,9 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
// FIXME: Allow configuring the replacement of `self`.
let is_self = capture.local().is_self(ctx.db()) && !capture.has_field_projections();
let capture_name = if is_self {
- make::name("this")
+ make.name("this")
} else {
- make::name(&capture.place_to_name(ctx.db(), ctx.edition()))
+ make.name(&capture.place_to_name(ctx.db(), ctx.edition()))
};
closure_mentioned_generic_params.extend(capture_ty.generic_params(ctx.db()));
@@ -177,9 +175,9 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
let capture_ty = capture_ty
.display_source_code(ctx.db(), module.into(), true)
.unwrap_or_else(|_| "_".to_owned());
- let param = make::param(
- ast::Pat::IdentPat(make::ident_pat(false, false, capture_name.clone_subtree())),
- make::ty(&capture_ty),
+ let param = make.param(
+ ast::Pat::IdentPat(make.ident_pat(false, false, capture_name.clone())),
+ make.ty(&capture_ty),
);
if is_self {
// Always put `this` first.
@@ -195,7 +193,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
}
let capture_usage_source = capture_usage.source();
- let capture_usage_source = capture_usage_source.to_node(&body_root);
+ let capture_usage_source = capture_usage_source.to_node(&source_root);
let mut expr = match capture_usage_source {
Either::Left(expr) => expr,
Either::Right(pat) => {
@@ -207,16 +205,16 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
expr = peel_ref(expr);
}
let replacement = wrap_capture_in_deref_if_needed(
+ make,
&expr,
&capture_name,
capture.kind(),
matches!(expr, ast::Expr::RefExpr(_)) || capture_usage.is_ref(),
- )
- .clone_for_update();
+ );
capture_usages_replacement_map.push((expr, replacement));
}
- let capture_as_arg = capture_as_arg(ctx, capture);
+ let capture_as_arg = capture_as_arg(make, ctx, capture);
if is_self {
captures_as_args.insert(0, capture_as_arg);
} else {
@@ -225,32 +223,38 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
}
let (closure_type_params, closure_where_clause) =
- compute_closure_type_params(ctx, closure_mentioned_generic_params, &closure);
+ compute_closure_type_params(make, ctx, closure_mentioned_generic_params, &closure);
for (old, new) in capture_usages_replacement_map {
- if old == body {
- body = new;
- } else {
- ted::replace(old.syntax(), new.syntax());
- }
+ editor.replace(old.syntax(), new.syntax());
}
+ let body = closure_param_list
+ .to_node(editor.finish().new_root())
+ .syntax()
+ .parent()
+ .and_then(ast::ClosureExpr::cast)
+ .and_then(|closure| closure.body())
+ .unwrap();
+
+ let make = SyntaxFactory::without_mappings();
+
let body = if wrap_body_in_block {
- make::block_expr([], Some(body.reset_indent().indent(1.into())))
+ make.block_expr([], Some(body.reset_indent().indent(1.into())))
} else {
ast::BlockExpr::cast(body.syntax().clone()).unwrap()
};
- let params = make::param_list(None, params);
+ let params = make.param_list(None, params);
let ret_ty = if ret_ty.is_unit() {
None
} else {
let ret_ty = ret_ty
.display_source_code(ctx.db(), module.into(), true)
.unwrap_or_else(|_| "_".to_owned());
- Some(make::ret_type(make::ty(&ret_ty)))
+ Some(make.ret_type(make.ty(&ret_ty)))
};
- let mut fn_ = make::fn_(
+ let mut fn_ = make.fn_(
None,
None,
closure_name_or_default.clone(),
@@ -266,7 +270,6 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
);
fn_ = fn_.dedent(IndentLevel::from_token(&fn_.syntax().last_token().unwrap()));
- builder.edit_file(ctx.vfs_file_id());
match &closure_name {
Some((closure_decl, _, _)) => {
fn_ = fn_.indent(closure_decl.indent_level());
@@ -346,7 +349,8 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
}
fn compute_closure_type_params(
- ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, '_>,
mentioned_generic_params: FxHashSet<hir::GenericParam>,
closure: &ast::ClosureExpr,
) -> (Option<ast::GenericParamList>, Option<ast::WhereClause>) {
@@ -472,11 +476,11 @@ fn compute_closure_type_params(
}))
.collect::<Vec<_>>();
let where_clause =
- (!include_where_bounds.is_empty()).then(|| make::where_clause(include_where_bounds));
+ (!include_where_bounds.is_empty()).then(|| make.where_clause(include_where_bounds));
// FIXME: Consider generic parameters that do not appear in params/return type/captures but
// written explicitly inside the closure.
- (Some(make::generic_param_list(include_params)), where_clause)
+ (Some(make.generic_param_list(include_params)), where_clause)
}
fn peel_parens(mut expr: ast::Expr) -> ast::Expr {
@@ -497,12 +501,13 @@ fn peel_ref(mut expr: ast::Expr) -> ast::Expr {
}
fn wrap_capture_in_deref_if_needed(
+ make: &SyntaxFactory,
expr: &ast::Expr,
capture_name: &ast::Name,
capture_kind: CaptureKind,
is_ref: bool,
) -> ast::Expr {
- let capture_name = make::expr_path(make::path_from_text(&capture_name.text()));
+ let capture_name = make.expr_path(make.path_from_text(&capture_name.text()));
if capture_kind == CaptureKind::Move || is_ref {
return capture_name;
}
@@ -524,10 +529,14 @@ fn wrap_capture_in_deref_if_needed(
if does_autoderef {
return capture_name;
}
- make::expr_prefix(T![*], capture_name).into()
+ make.expr_prefix(T![*], capture_name).into()
}
-fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture<'_>) -> ast::Expr {
+fn capture_as_arg(
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, '_>,
+ capture: &ClosureCapture<'_>,
+) -> ast::Expr {
let place = parse_expr_from_str(
&capture.display_place_source_code(ctx.db(), ctx.edition()),
ctx.edition(),
@@ -543,12 +552,12 @@ fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture<'_>) -> ast:
{
return expr.expr().expect("`display_place_source_code()` produced an invalid expr");
}
- make::expr_ref(place, needs_mut)
+ make.expr_ref(place, needs_mut)
}
fn handle_calls(
builder: &mut SourceChangeBuilder,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
closure_name: Option<&ast::IdentPat>,
captures_as_args: &[ast::Expr],
closure: &ast::ClosureExpr,
@@ -590,7 +599,7 @@ fn handle_calls(
fn handle_call(
builder: &mut SourceChangeBuilder,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
closure_ref: ast::Expr,
captures_as_args: &[ast::Expr],
) -> Option<()> {
diff --git a/crates/ide-assists/src/handlers/convert_comment_block.rs b/crates/ide-assists/src/handlers/convert_comment_block.rs
index f242fe8314..d950b6df21 100644
--- a/crates/ide-assists/src/handlers/convert_comment_block.rs
+++ b/crates/ide-assists/src/handlers/convert_comment_block.rs
@@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists};
// comment
// */
// ```
-pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let comment = ctx.find_token_at_offset::<ast::Comment>()?;
// Only allow comments which are alone on their line
if let Some(prev) = comment.syntax().prev_token() {
diff --git a/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs b/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs
index 187cc74306..11a3c64188 100644
--- a/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs
+++ b/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs
@@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
pub(crate) fn convert_comment_from_or_to_doc(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let comment = ctx.find_token_at_offset::<ast::Comment>()?;
diff --git a/crates/ide-assists/src/handlers/convert_for_to_while_let.rs b/crates/ide-assists/src/handlers/convert_for_to_while_let.rs
index 9eb4c0584b..d45c4292b8 100644
--- a/crates/ide-assists/src/handlers/convert_for_to_while_let.rs
+++ b/crates/ide-assists/src/handlers/convert_for_to_while_let.rs
@@ -32,7 +32,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
pub(crate) fn convert_for_loop_to_while_let(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?;
let iterable = for_loop.iterable()?;
diff --git a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
index 18f3ced414..3e11ee804a 100644
--- a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
+++ b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
@@ -33,7 +33,10 @@ use crate::{AssistContext, AssistId, Assists};
// }
// }
// ```
-pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_from_to_tryfrom(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let impl_ = ctx.find_node_at_offset::<ast::Impl>()?;
let trait_ty = impl_.trait_()?;
diff --git a/crates/ide-assists/src/handlers/convert_integer_literal.rs b/crates/ide-assists/src/handlers/convert_integer_literal.rs
index bc76ade97f..c92ad2a7c9 100644
--- a/crates/ide-assists/src/handlers/convert_integer_literal.rs
+++ b/crates/ide-assists/src/handlers/convert_integer_literal.rs
@@ -13,7 +13,10 @@ use crate::{AssistContext, AssistId, Assists, GroupLabel};
// ```
// const _: i32 = 0b1010;
// ```
-pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_integer_literal(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
if !ctx.has_empty_selection() {
return None;
}
diff --git a/crates/ide-assists/src/handlers/convert_into_to_from.rs b/crates/ide-assists/src/handlers/convert_into_to_from.rs
index e330102423..a01a66e7b1 100644
--- a/crates/ide-assists/src/handlers/convert_into_to_from.rs
+++ b/crates/ide-assists/src/handlers/convert_into_to_from.rs
@@ -1,4 +1,6 @@
-use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast, traits::resolve_target_trait};
+use ide_db::{
+ famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory, traits::resolve_target_trait,
+};
use syntax::ast::{self, AstNode, HasGenericArgs, HasName};
use crate::{AssistContext, AssistId, Assists};
@@ -31,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists};
// }
// }
// ```
-pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let impl_ = ctx.find_node_at_offset::<ast::Impl>()?;
let src_type = impl_.self_ty()?;
let ast_trait = impl_.trait_()?;
@@ -44,17 +46,15 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) -
}
let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate(ctx.sema.db)));
+ let current_edition = module.krate(ctx.db()).edition(ctx.db());
- let src_type_path = {
+ let src_type_mod_path = {
let src_type_path = src_type.syntax().descendants().find_map(ast::Path::cast)?;
let src_type_def = match ctx.sema.resolve_path(&src_type_path) {
Some(hir::PathResolution::Def(module_def)) => module_def,
_ => return None,
};
- mod_path_to_ast(
- &module.find_path(ctx.db(), src_type_def, cfg)?,
- module.krate(ctx.db()).edition(ctx.db()),
- )
+ module.find_path(ctx.db(), src_type_def, cfg)?
};
let dest_type = match &ast_trait {
@@ -89,19 +89,45 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) -
"Convert Into to From",
impl_.syntax().text_range(),
|builder| {
- builder.replace(src_type.syntax().text_range(), dest_type.to_string());
- builder.replace(ast_trait.syntax().text_range(), format!("From<{src_type}>"));
- builder.replace(into_fn_return.syntax().text_range(), "-> Self");
- builder.replace(into_fn_params.syntax().text_range(), format!("(val: {src_type})"));
- builder.replace(into_fn_name.syntax().text_range(), "from");
+ let editor = builder.make_editor(impl_.syntax());
+ let make = editor.make();
+ let src_type_path =
+ mod_path_to_ast_with_factory(make, &src_type_mod_path, current_edition);
+
+ editor.replace(src_type.syntax(), make.ty(&dest_type.to_string()).syntax());
+ editor.replace(ast_trait.syntax(), make.ty(&format!("From<{src_type}>")).syntax());
+ editor.replace(into_fn_return.syntax(), make.ret_type(make.ty("Self")).syntax());
+
+ editor.replace(
+ into_fn_params.syntax(),
+ make.param_list(
+ None,
+ [make.param(make.simple_ident_pat(make.name("val")).into(), src_type.clone())],
+ )
+ .syntax(),
+ );
+ editor.replace(into_fn_name.syntax(), make.name("from").syntax());
for s in selfs {
match s.text().as_ref() {
- "self" => builder.replace(s.syntax().text_range(), "val"),
- "Self" => builder.replace(s.syntax().text_range(), src_type_path.to_string()),
+ "self" => editor.replace(s.syntax(), make.name_ref("val").syntax()),
+ "Self" => {
+ if let Some(path_segment) =
+ s.syntax().parent().and_then(ast::PathSegment::cast)
+ {
+ let self_path = path_segment.parent_path();
+ if self_path.qualifier().is_none()
+ && path_segment.generic_arg_list().is_none()
+ {
+ editor.replace(self_path.syntax(), src_type_path.syntax());
+ }
+ }
+ }
_ => {}
}
}
+
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
diff --git a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
index cc5cc490f1..9506a96318 100644
--- a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
+++ b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs
@@ -33,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists, utils::wrap_paren};
// ```
pub(crate) fn convert_iter_for_each_to_for(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let method = ctx.find_node_at_offset::<ast::MethodCallExpr>()?;
@@ -98,7 +98,7 @@ pub(crate) fn convert_iter_for_each_to_for(
// ```
pub(crate) fn convert_for_loop_with_for_each(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?;
let iterable = for_loop.iterable()?;
@@ -205,7 +205,7 @@ fn impls_core_iter(sema: &hir::Semantics<'_, ide_db::RootDatabase>, iterable: &a
}
fn validate_method_call_expr(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
expr: ast::MethodCallExpr,
) -> Option<(ast::Expr, ast::Expr)> {
let name_ref = expr.name_ref()?;
diff --git a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
index 1ae12390ee..07e12f0320 100644
--- a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
+++ b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs
@@ -24,7 +24,10 @@ use crate::{AssistContext, AssistId, Assists};
// };
// }
// ```
-pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_let_else_to_match(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone());
// Should focus on the `else` token to trigger
let let_stmt = ctx
@@ -143,6 +146,10 @@ fn remove_mut_and_collect_idents(
let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?;
make.box_pat(pat).into()
}
+ ast::Pat::DerefPat(p) => {
+ let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?;
+ make.deref_pat(pat)
+ }
ast::Pat::OrPat(p) => {
let pats = p
.pats()
diff --git a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
index bc49acc1ef..a93ab138e5 100644
--- a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
+++ b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -29,7 +29,10 @@ use crate::{
// let Some(val) = opt else { return };
// }
// ```
-pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+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 pat = let_stmt.pat()?;
if ctx.offset() > pat.syntax().text_range().end() {
@@ -61,9 +64,17 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'
|builder| {
let extracting_arm_pat =
rename_variable(&extracting_arm_pat, &extracted_variable_positions, pat);
+ let (open_paren, close_paren) = if ast::OrPat::can_cast(extracting_arm_pat.kind()) {
+ // Or patterns cannot put put directly under let statements.
+ // FIXME: Do this with `SyntaxEditor` in `rename_variable()`, it's just difficult right now
+ // since it re-roots nodes.
+ ("(", ")")
+ } else {
+ ("", "")
+ };
builder.replace(
let_stmt.syntax().text_range(),
- format!("let {extracting_arm_pat} = {initializer_expr} else {diverging_arm_expr};"),
+ format!("let {open_paren}{extracting_arm_pat}{close_paren} = {initializer_expr} else {diverging_arm_expr};"),
)
},
)
@@ -71,7 +82,7 @@ pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'
// Given a match expression, find extracting and diverging arms.
fn find_arms(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
match_expr: &ast::MatchExpr,
) -> Option<(ast::MatchArm, ast::MatchArm)> {
let arms = match_expr.match_arm_list()?.arms().collect::<Vec<_>>();
@@ -99,7 +110,7 @@ fn find_arms(
}
// Given an extracting arm, find the extracted variable.
-fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option<Vec<Name>> {
+fn find_extracted_variable(ctx: &AssistContext<'_, '_>, arm: &ast::MatchArm) -> Option<Vec<Name>> {
match arm.expr()? {
ast::Expr::PathExpr(path) => {
let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?;
@@ -543,4 +554,38 @@ fn f() {
"#,
);
}
+
+ #[test]
+ fn top_level_or_pat() {
+ check_assist(
+ convert_match_to_let_else,
+ r#"
+enum E {
+ A(u32),
+ B(u32),
+ C,
+}
+
+fn foo() {
+ let e = E::A(0);
+ let _$0 = match e {
+ E::A(v) | E::B(v) => v,
+ _ => return,
+ };
+}
+ "#,
+ r#"
+enum E {
+ A(u32),
+ B(u32),
+ C,
+}
+
+fn foo() {
+ let e = E::A(0);
+ let (E::A(_) | E::B(_)) = e else { return };
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
index 5b691dba5e..72418f1559 100644
--- a/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
+++ b/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
@@ -54,7 +54,7 @@ use crate::{
// ```
pub(crate) fn convert_named_struct_to_tuple_struct(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
// 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
@@ -92,7 +92,7 @@ pub(crate) fn convert_named_struct_to_tuple_struct(
}
fn edit_struct_def(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
builder: &mut SourceChangeBuilder,
strukt: &Either<ast::Struct, ast::Variant>,
record_fields: ast::RecordFieldList,
@@ -153,7 +153,7 @@ fn edit_struct_def(
}
fn edit_struct_references(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
builder: &mut SourceChangeBuilder,
strukt: Either<hir::Struct, hir::EnumVariant>,
) {
@@ -174,7 +174,7 @@ fn edit_struct_references(
}
fn process_struct_name_reference(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
r: FileReference,
edit: &SyntaxEditor,
source: &ast::SourceFile,
@@ -227,7 +227,7 @@ fn process_struct_name_reference(
}
fn record_to_tuple_struct_like<T, I>(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
source: &ast::SourceFile,
editor: &SyntaxEditor,
field_list: T,
@@ -282,7 +282,7 @@ where
}
fn edit_field_references(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
builder: &mut SourceChangeBuilder,
fields: impl Iterator<Item = ast::RecordField>,
) {
diff --git a/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs b/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs
index c0fd69779a..d0e82d69eb 100644
--- a/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs
+++ b/crates/ide-assists/src/handlers/convert_nested_function_to_closure.rs
@@ -29,7 +29,7 @@ use crate::assist_context::{AssistContext, Assists};
// ```
pub(crate) fn convert_nested_function_to_closure(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let name = ctx.find_node_at_offset::<ast::Name>()?;
let function = name.syntax().parent().and_then(ast::Fn::cast)?;
diff --git a/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
index c83f8b0765..7026b5bafd 100644
--- a/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
+++ b/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
@@ -35,7 +35,10 @@ use crate::assist_context::{AssistContext, Assists};
// }
// }
// ```
-pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_range_for_to_while(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone());
let make = editor.make();
let for_kw = ctx.find_token_syntax_at_offset(T![for])?;
diff --git a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
index 791a6a26af..74392ca7c1 100644
--- a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
+++ b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
@@ -59,7 +59,10 @@ use crate::{
// let Some(x) = foo() else { return };
// }
// ```
-pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_to_guarded_return(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
match ctx.find_node_at_offset::<Either<ast::LetStmt, ast::IfExpr>>()? {
Either::Left(let_stmt) => let_stmt_to_guarded_return(let_stmt, acc, ctx),
Either::Right(if_expr) => if_expr_to_guarded_return(if_expr, acc, ctx),
@@ -69,15 +72,9 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
fn if_expr_to_guarded_return(
if_expr: ast::IfExpr,
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let make = SyntaxFactory::without_mappings();
- let else_block = match if_expr.else_branch() {
- Some(ast::ElseBranch::Block(block_expr)) => Some(block_expr),
- Some(_) => return None,
- _ => None,
- };
-
let cond = if_expr.condition()?;
let if_token_range = if_expr.if_token()?.text_range();
@@ -102,7 +99,7 @@ fn if_expr_to_guarded_return(
}
let container = container_of(&parent_block)?;
- let else_block = ElseBlock::new(&ctx.sema, else_block, &container)?;
+ let else_block = ElseBlock::new(&ctx.sema, if_expr.else_branch(), &container)?;
if parent_block.tail_expr() != Some(if_expr.clone().into())
&& !(else_block.is_never_block
@@ -180,7 +177,7 @@ fn if_expr_to_guarded_return(
fn let_stmt_to_guarded_return(
let_stmt: ast::LetStmt,
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let pat = let_stmt.pat()?;
let expr = let_stmt.initializer()?;
@@ -237,7 +234,7 @@ fn container_of(block: &ast::BlockExpr) -> Option<SyntaxNode> {
}
struct ElseBlock<'db> {
- exist_else_block: Option<ast::BlockExpr>,
+ exist_else_branch: Option<ast::ElseBranch>,
is_never_block: bool,
kind: EarlyKind<'db>,
}
@@ -245,13 +242,23 @@ struct ElseBlock<'db> {
impl<'db> ElseBlock<'db> {
fn new(
sema: &Semantics<'db, RootDatabase>,
- exist_else_block: Option<ast::BlockExpr>,
+ exist_else_branch: Option<ast::ElseBranch>,
parent_container: &SyntaxNode,
) -> Option<Self> {
- let is_never_block = exist_else_block.as_ref().is_some_and(|it| is_never_block(sema, it));
+ let is_never_block =
+ exist_else_branch.as_ref().is_some_and(|it| is_never_else_branch(sema, it));
let kind = EarlyKind::from_node(parent_container, sema)?;
- Some(Self { exist_else_block, is_never_block, kind })
+ Some(Self { exist_else_branch, is_never_block, kind })
+ }
+
+ fn make_else_block_from_exist_branch(&self, make: &SyntaxFactory) -> Option<ast::BlockExpr> {
+ match self.exist_else_branch.as_ref()? {
+ ast::ElseBranch::Block(block_expr) => Some(block_expr.reset_indent()),
+ ast::ElseBranch::IfExpr(if_expr) => {
+ Some(make.block_expr(None, Some(if_expr.reset_indent().indent(1.into()).into())))
+ }
+ }
}
fn make_early_block(
@@ -259,15 +266,15 @@ impl<'db> ElseBlock<'db> {
sema: &Semantics<'_, RootDatabase>,
make: &SyntaxFactory,
) -> ast::BlockExpr {
- let Some(block_expr) = self.exist_else_block else {
+ let Some(block_expr) = self.make_else_block_from_exist_branch(make) else {
return make.tail_only_block_expr(self.kind.make_early_expr(sema, make, None));
};
if self.is_never_block {
- return block_expr.reset_indent();
+ return block_expr;
}
- let (editor, block_expr) = SyntaxEditor::with_ast_node(&block_expr.reset_indent());
+ let (editor, block_expr) = SyntaxEditor::with_ast_node(&block_expr);
let make = editor.make();
let last_stmt = block_expr.statements().last().map(|it| it.syntax().clone());
@@ -284,8 +291,8 @@ impl<'db> ElseBlock<'db> {
editor.replace(tail_expr.syntax(), early_expr.syntax());
} else {
let last_stmt = match block_expr.tail_expr() {
- Some(expr) => make.expr_stmt(expr).syntax().clone(),
- None => last_element.clone(),
+ Some(expr) if !expr.is_block_like() => make.expr_stmt(expr).syntax().clone(),
+ _ => last_element.clone(),
};
let whitespace =
make.whitespace(&whitespace.map_or(String::new(), |it| it.to_string()));
@@ -401,6 +408,27 @@ fn is_early_block(then_block: &ast::StmtList) -> bool {
|| then_block.statements().filter_map(into_expr).any(is_early_expr)
}
+fn is_never_else_branch(sema: &Semantics<'_, RootDatabase>, it: &ast::ElseBranch) -> bool {
+ match it {
+ ast::ElseBranch::Block(block_expr) => is_never_block(sema, block_expr),
+ ast::ElseBranch::IfExpr(if_expr) => {
+ let mut if_exprs =
+ std::iter::successors(Some(if_expr.clone()), |it| match it.else_branch()? {
+ ast::ElseBranch::Block(_) => None,
+ ast::ElseBranch::IfExpr(if_expr) => Some(if_expr),
+ });
+ if_exprs.all(|it| {
+ let else_is_never = match it.else_branch() {
+ None => false,
+ Some(ast::ElseBranch::IfExpr(_)) => true,
+ Some(ast::ElseBranch::Block(block)) => is_never_block(sema, &block),
+ };
+ else_is_never && it.then_branch().is_some_and(|it| is_never_block(sema, &it))
+ })
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
@@ -1025,6 +1053,42 @@ fn main() {
}
#[test]
+ fn convert_inside_loop_with_else_if() {
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+fn main() {
+ loop {
+ if$0 guard() {
+ foo();
+ bar();
+ } else if cond() {
+ break;
+ } else {
+ return
+ }
+ }
+}
+"#,
+ r#"
+fn main() {
+ loop {
+ if !guard() {
+ if cond() {
+ break;
+ } else {
+ return
+ }
+ }
+ foo();
+ bar();
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn convert_let_inside_loop() {
check_assist(
convert_to_guarded_return,
@@ -1055,6 +1119,7 @@ fn main() {
check_assist(
convert_to_guarded_return,
r#"
+//- minicore: iterator
fn main() {
for n in ns {
if$0 let Some(n) = n {
@@ -1107,6 +1172,7 @@ fn main() {
check_assist(
convert_to_guarded_return,
r#"
+//- minicore: iterator
fn main() {
for n in ns {
if$0 let Some(n) = n {
@@ -1134,6 +1200,109 @@ fn main() {
}
#[test]
+ fn convert_let_inside_for_with_else_if() {
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+//- minicore: iterator
+fn main() {
+ for n in ns {
+ if$0 let Some(n) = n {
+ foo(n);
+ bar();
+ } else if cond() {
+ return
+ } else {
+ baz()
+ }
+ }
+}
+"#,
+ r#"
+fn main() {
+ for n in ns {
+ let Some(n) = n else {
+ if cond() {
+ return
+ } else {
+ baz()
+ }
+ continue
+ };
+ foo(n);
+ bar();
+ }
+}
+"#,
+ );
+
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+//- minicore: iterator
+fn main() {
+ for n in ns {
+ if$0 let Some(n) = n {
+ foo(n);
+ bar();
+ } else if cond() {
+ return
+ }
+ }
+}
+"#,
+ r#"
+fn main() {
+ for n in ns {
+ let Some(n) = n else {
+ if cond() {
+ return
+ }
+ continue
+ };
+ foo(n);
+ bar();
+ }
+}
+"#,
+ );
+
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+//- minicore: iterator
+fn main() {
+ for n in ns {
+ if$0 let Some(n) = n {
+ foo(n);
+ bar();
+ } else if cond() {
+ return
+ } else {
+ break
+ }
+ }
+}
+"#,
+ r#"
+fn main() {
+ for n in ns {
+ let Some(n) = n else {
+ if cond() {
+ return
+ } else {
+ break
+ }
+ };
+ foo(n);
+ bar();
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn convert_let_stmt_inside_fn() {
check_assist(
convert_to_guarded_return,
@@ -1415,7 +1584,7 @@ fn main() {
}
#[test]
- fn ignore_else_if() {
+ fn ignore_on_else_if() {
check_assist_not_applicable(
convert_to_guarded_return,
r#"
diff --git a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs
index 0af0cbc32a..46e2917f72 100644
--- a/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs
+++ b/crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs
@@ -4,7 +4,7 @@ use ide_db::{
FxHashSet,
assists::AssistId,
defs::Definition,
- helpers::mod_path_to_ast,
+ helpers::mod_path_to_ast_with_factory,
imports::insert_use::{ImportScope, insert_use_with_editor},
search::{FileReference, UsageSearchResult},
source_change::SourceChangeBuilder,
@@ -51,7 +51,7 @@ use crate::assist_context::{AssistContext, Assists};
// ```
pub(crate) fn convert_tuple_return_type_to_struct(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
let type_ref = ret_type.ty()?;
@@ -104,7 +104,7 @@ pub(crate) fn convert_tuple_return_type_to_struct(
/// Replaces tuple usages with the corresponding tuple struct pattern.
fn replace_usages(
edit: &mut SourceChangeBuilder,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
usages: &UsageSearchResult,
struct_name: &str,
target_module: &hir::Module,
@@ -176,8 +176,8 @@ fn node_to_pats(node: SyntaxNode) -> Option<Vec<ast::Pat>> {
}
fn augment_references_with_imports(
- syntax_factory: &SyntaxFactory,
- ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, '_>,
references: &[FileReference],
struct_name: &str,
target_module: &hir::Module,
@@ -208,12 +208,13 @@ fn augment_references_with_imports(
cfg,
)
.map(|mod_path| {
- syntax_factory.path_concat(
- mod_path_to_ast(
+ make.path_concat(
+ mod_path_to_ast_with_factory(
+ make,
&mod_path,
target_module.krate(ctx.db()).edition(ctx.db()),
),
- syntax_factory.path_from_text(struct_name),
+ make.path_from_text(struct_name),
)
});
@@ -231,7 +232,7 @@ fn augment_references_with_imports(
fn add_tuple_struct_def(
edit: &mut SourceChangeBuilder,
syntax_factory: &SyntaxFactory,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
usages: &UsageSearchResult,
parent: &SyntaxNode,
tuple_ty: &ast::TupleType,
diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index afbcf024b9..a6a47d78a8 100644
--- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -58,7 +58,7 @@ use crate::{
// ```
pub(crate) fn convert_tuple_struct_to_named_struct(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let strukt_or_variant = ctx
.find_node_at_offset::<ast::Struct>()
@@ -140,7 +140,7 @@ fn edit_struct_def(
}
fn edit_struct_references(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
edit: &mut SourceChangeBuilder,
strukt: Either<hir::Struct, hir::EnumVariant>,
names: &[ast::Name],
@@ -164,7 +164,7 @@ fn edit_struct_references(
}
fn process_struct_name_reference(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
r: FileReference,
editor: &SyntaxEditor,
source: &ast::SourceFile,
@@ -229,7 +229,7 @@ fn process_struct_name_reference(
}
fn process_delimiter(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
source: &ast::SourceFile,
editor: &SyntaxEditor,
list: &impl AstNode,
@@ -270,7 +270,7 @@ fn process_delimiter(
}
fn edit_field_references(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
edit: &mut SourceChangeBuilder,
fields: impl Iterator<Item = ast::TupleField>,
names: &[ast::Name],
diff --git a/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs b/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs
index 1af5db17f0..73b373dbcb 100644
--- a/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs
+++ b/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs
@@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
pub(crate) fn convert_two_arm_bool_match_to_matches_macro(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
use ArmBodyExpression::*;
let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?;
diff --git a/crates/ide-assists/src/handlers/convert_while_to_loop.rs b/crates/ide-assists/src/handlers/convert_while_to_loop.rs
index 793e7465c1..94920569a0 100644
--- a/crates/ide-assists/src/handlers/convert_while_to_loop.rs
+++ b/crates/ide-assists/src/handlers/convert_while_to_loop.rs
@@ -38,7 +38,7 @@ use crate::{
// }
// }
// ```
-pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let while_kw = ctx.find_token_syntax_at_offset(T![while])?;
let while_expr = while_kw.parent().and_then(ast::WhileExpr::cast)?;
let while_body = while_expr.loop_body()?;
diff --git a/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/crates/ide-assists/src/handlers/destructure_struct_binding.rs
index 9ffce445d1..2b5b2ca13b 100644
--- a/crates/ide-assists/src/handlers/destructure_struct_binding.rs
+++ b/crates/ide-assists/src/handlers/destructure_struct_binding.rs
@@ -3,7 +3,7 @@ use ide_db::{
FxHashMap, FxHashSet, RootDatabase,
assists::AssistId,
defs::Definition,
- helpers::mod_path_to_ast,
+ helpers::mod_path_to_ast_with_factory,
search::{FileReference, SearchScope},
};
use itertools::Itertools;
@@ -47,7 +47,10 @@ use crate::{
// let baz2 = &baz;
// }
// ```
-pub(crate) fn destructure_struct_binding(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn destructure_struct_binding(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let target = ctx.find_node_at_offset::<Target>()?;
let data = collect_data(target, ctx)?;
@@ -119,7 +122,7 @@ impl AstNode for Target {
}
fn destructure_struct_binding_impl(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
builder: &mut SourceChangeBuilder,
data: &StructEditData,
) {
@@ -173,7 +176,7 @@ impl StructEditData {
}
}
-fn collect_data(target: Target, ctx: &AssistContext<'_>) -> Option<StructEditData> {
+fn collect_data(target: Target, ctx: &AssistContext<'_, '_>) -> Option<StructEditData> {
let ty = target.ty(&ctx.sema)?;
let hir::Adt::Struct(struct_type) = ty.strip_references().as_adt()? else { return None };
@@ -246,7 +249,7 @@ fn collect_data(target: Target, ctx: &AssistContext<'_>) -> Option<StructEditDat
}
fn get_names_in_scope(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
target: &Target,
usages: &[FileReference],
) -> Option<FxHashSet<SmolStr>> {
@@ -270,13 +273,13 @@ fn get_names_in_scope(
}
fn destructure_pat(
- _ctx: &AssistContext<'_>,
+ _ctx: &AssistContext<'_, '_>,
editor: &SyntaxEditor,
data: &StructEditData,
field_names: &[(SmolStr, SmolStr)],
) {
let make = editor.make();
- let struct_path = mod_path_to_ast(&data.struct_def_path, data.edition);
+ let struct_path = mod_path_to_ast_with_factory(make, &data.struct_def_path, data.edition);
let is_ref = data.target.is_ref();
let is_mut = data.target.is_mut();
@@ -313,7 +316,10 @@ fn destructure_pat(
data.apply_to_destruct(new_pat, editor);
}
-fn generate_field_names(ctx: &AssistContext<'_>, data: &StructEditData) -> Vec<(SmolStr, SmolStr)> {
+fn generate_field_names(
+ ctx: &AssistContext<'_, '_>,
+ data: &StructEditData,
+) -> Vec<(SmolStr, SmolStr)> {
match data.kind {
hir::StructKind::Tuple => data
.visible_fields
@@ -348,7 +354,7 @@ fn new_field_name(base_name: SmolStr, names_in_scope: &FxHashSet<SmolStr>) -> Sm
}
fn update_usages(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
editor: &SyntaxEditor,
data: &StructEditData,
field_names: &FxHashMap<SmolStr, SmolStr>,
@@ -367,7 +373,7 @@ fn update_usages(
}
fn build_usage_edit(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
make: &SyntaxFactory,
data: &StructEditData,
usage: &FileReference,
diff --git a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
index 291605056b..2755962362 100644
--- a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
+++ b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
@@ -34,7 +34,10 @@ use crate::{
// let v = _0;
// }
// ```
-pub(crate) fn destructure_tuple_binding(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn destructure_tuple_binding(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
destructure_tuple_binding_impl(acc, ctx, false)
}
@@ -58,7 +61,7 @@ pub(crate) fn destructure_tuple_binding(acc: &mut Assists, ctx: &AssistContext<'
// ```
pub(crate) fn destructure_tuple_binding_impl(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
with_sub_pattern: bool,
) -> Option<()> {
let ident_pat = ctx.find_node_at_offset::<ast::IdentPat>()?;
@@ -84,7 +87,7 @@ pub(crate) fn destructure_tuple_binding_impl(
}
fn destructure_tuple_edit_impl(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
edit: &mut SourceChangeBuilder,
data: &TupleData,
in_sub_pattern: bool,
@@ -102,7 +105,7 @@ fn destructure_tuple_edit_impl(
edit.add_file_edits(ctx.vfs_file_id(), editor);
}
-fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleData> {
+fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_, '_>) -> Option<TupleData> {
if ident_pat.at_token().is_some() {
// Cannot destructure pattern with sub-pattern:
// Only IdentPat can have sub-pattern,
@@ -168,7 +171,7 @@ struct TupleData {
usages: Option<Vec<FileReference>>,
}
fn edit_tuple_assignment(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
edit: &mut SourceChangeBuilder,
editor: &SyntaxEditor,
data: &TupleData,
@@ -235,7 +238,7 @@ impl AssignmentEdit {
fn edit_tuple_usages(
data: &TupleData,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
make: &SyntaxFactory,
in_sub_pattern: bool,
) -> Option<Vec<EditTupleUsage>> {
@@ -258,7 +261,7 @@ fn edit_tuple_usages(
Some(edits)
}
fn edit_tuple_usage(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
make: &SyntaxFactory,
usage: &FileReference,
data: &TupleData,
@@ -275,7 +278,7 @@ fn edit_tuple_usage(
}
fn edit_tuple_field_usage(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
make: &SyntaxFactory,
data: &TupleData,
index: TupleIndex,
@@ -305,7 +308,7 @@ enum EditTupleUsage {
impl EditTupleUsage {
fn apply(
self,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
edit: &mut SourceChangeBuilder,
syntax_editor: &SyntaxEditor,
) {
@@ -371,7 +374,7 @@ mod tests {
// Tests for direct tuple destructure:
// `let $0t = (1,2);` -> `let (_0, _1) = (1,2);`
- fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ fn assist(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
destructure_tuple_binding_impl(acc, ctx, false)
}
@@ -1180,10 +1183,10 @@ fn main {
use super::*;
use crate::tests::check_assist_by_label;
- fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ fn assist(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
destructure_tuple_binding_impl(acc, ctx, true)
}
- fn in_place_assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ fn in_place_assist(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
destructure_tuple_binding_impl(acc, ctx, false)
}
@@ -1251,7 +1254,7 @@ fn main() {
#[test]
fn trigger_both_destructure_tuple_assists() {
- fn assist(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ fn assist(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
destructure_tuple_binding_impl(acc, ctx, true)
}
let text = r#"
@@ -1787,7 +1790,7 @@ fn main() {
// * `?`
check_in_place_assist(
r#"
-//- minicore: option
+//- minicore: try, option
fn f1(v: i32) {}
fn f2(v: &i32) {}
trait T {
diff --git a/crates/ide-assists/src/handlers/desugar_doc_comment.rs b/crates/ide-assists/src/handlers/desugar_doc_comment.rs
index 74bb0ba3f6..e6784a0c3b 100644
--- a/crates/ide-assists/src/handlers/desugar_doc_comment.rs
+++ b/crates/ide-assists/src/handlers/desugar_doc_comment.rs
@@ -24,7 +24,7 @@ use crate::{
// #[doc = r"Multi-line
// comment"]
// ```
-pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let comment = ctx.find_token_at_offset::<ast::Comment>()?;
// Only allow doc comments
let placement = comment.kind().doc?;
diff --git a/crates/ide-assists/src/handlers/desugar_try_expr.rs b/crates/ide-assists/src/handlers/desugar_try_expr.rs
index fc894f0fe9..5e1cd7700d 100644
--- a/crates/ide-assists/src/handlers/desugar_try_expr.rs
+++ b/crates/ide-assists/src/handlers/desugar_try_expr.rs
@@ -50,7 +50,7 @@ use crate::assist_context::{AssistContext, Assists};
// };
// }
// ```
-pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let question_tok = ctx.find_token_syntax_at_offset(T![?])?;
let try_expr = question_tok.parent().and_then(ast::TryExpr::cast)?;
diff --git a/crates/ide-assists/src/handlers/expand_glob_import.rs b/crates/ide-assists/src/handlers/expand_glob_import.rs
index 1c8cbf5af5..f5d2a6cfc5 100644
--- a/crates/ide-assists/src/handlers/expand_glob_import.rs
+++ b/crates/ide-assists/src/handlers/expand_glob_import.rs
@@ -41,7 +41,7 @@ use crate::{
//
// fn qux(bar: Bar, baz: Baz) {}
// ```
-pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let star = ctx.find_token_syntax_at_offset(T![*])?;
let use_tree = star.parent().and_then(ast::UseTree::cast)?;
let use_item = star.parent_ancestors().find_map(ast::Use::cast)?;
@@ -99,7 +99,7 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext<'_>) ->
//
// pub use foo::{Bar, Baz};
// ```
-pub(crate) fn expand_glob_reexport(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn expand_glob_reexport(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let star = ctx.find_token_syntax_at_offset(T![*])?;
let use_tree = star.parent().and_then(ast::UseTree::cast)?;
let use_item = star.parent_ancestors().find_map(ast::Use::cast)?;
@@ -140,7 +140,7 @@ pub(crate) fn expand_glob_reexport(acc: &mut Assists, ctx: &AssistContext<'_>) -
}
fn build_expanded_import(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
builder: &mut SourceChangeBuilder,
use_tree: UseTree,
use_item: Use,
@@ -162,7 +162,8 @@ fn build_expanded_import(
}
};
- let refs_in_target = find_refs_in_mod(ctx, target_module, visible_from, must_be_pub);
+ let refs_in_target =
+ find_refs_in_mod(ctx, target_module, current_module, visible_from, must_be_pub);
let imported_defs = find_imported_defs(ctx, use_item);
let filtered_defs =
@@ -237,7 +238,7 @@ fn find_parent_and_path(
}
}
-fn def_is_referenced_in(def: Definition, ctx: &AssistContext<'_>) -> bool {
+fn def_is_referenced_in(def: Definition, ctx: &AssistContext<'_, '_>) -> bool {
let search_scope = SearchScope::single_file(ctx.file_id());
def.usages(&ctx.sema).in_scope(&search_scope).at_least_one()
}
@@ -251,7 +252,11 @@ struct Ref {
}
impl Ref {
- fn from_scope_def(ctx: &AssistContext<'_>, name: Name, scope_def: ScopeDef) -> Option<Self> {
+ fn from_scope_def(
+ ctx: &AssistContext<'_, '_>,
+ name: Name,
+ scope_def: ScopeDef,
+ ) -> Option<Self> {
match scope_def {
ScopeDef::ModuleDef(def) => Some(Ref {
visible_name: name,
@@ -267,7 +272,7 @@ impl Ref {
struct Refs(Vec<Ref>);
impl Refs {
- fn used_refs(&self, ctx: &AssistContext<'_>) -> Refs {
+ fn used_refs(&self, ctx: &AssistContext<'_, '_>) -> Refs {
Refs(
self.0
.clone()
@@ -297,8 +302,9 @@ impl Refs {
}
fn find_refs_in_mod(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
expandable: Expandable,
+ current_module: Module,
visible_from: Module,
must_be_pub: bool,
) -> Refs {
@@ -308,6 +314,10 @@ fn find_refs_in_mod(
let refs = module_scope
.into_iter()
.filter_map(|(n, d)| Ref::from_scope_def(ctx, n, d))
+ .filter(|r| match r.def {
+ Definition::Module(it) => it != current_module,
+ _ => r.def.module(ctx.db()).map_or(false, |it| it != current_module),
+ })
.filter(|r| !must_be_pub || r.is_pub)
.collect();
Refs(refs)
@@ -325,8 +335,8 @@ fn find_refs_in_mod(
}
}
-fn is_visible_from(ctx: &AssistContext<'_>, expandable: &Expandable, from: Module) -> bool {
- fn is_mod_visible_from(ctx: &AssistContext<'_>, module: Module, from: Module) -> bool {
+fn is_visible_from(ctx: &AssistContext<'_, '_>, expandable: &Expandable, from: Module) -> bool {
+ fn is_mod_visible_from(ctx: &AssistContext<'_, '_>, module: Module, from: Module) -> bool {
match module.parent(ctx.db()) {
Some(parent) => {
module.visibility(ctx.db()).is_visible_from(ctx.db(), from.into())
@@ -366,7 +376,7 @@ fn is_visible_from(ctx: &AssistContext<'_>, expandable: &Expandable, from: Modul
// use foo::*$0;
// use baz::Baz;
// ↑ ---------------
-fn find_imported_defs(ctx: &AssistContext<'_>, use_item: Use) -> Vec<Definition> {
+fn find_imported_defs(ctx: &AssistContext<'_, '_>, use_item: Use) -> Vec<Definition> {
[Direction::Prev, Direction::Next]
.into_iter()
.flat_map(|dir| {
@@ -440,6 +450,31 @@ fn qux(bar: Bar, baz: Baz) {
}
#[test]
+ fn expanding_glob_import_on_cycle_import() {
+ check_assist(
+ expand_glob_import,
+ r"
+mod foo {
+ pub use crate::*$0;
+ pub struct Foo;
+ pub fn bar() -> Bar { _ = Foo; Bar }
+}
+pub use foo::*;
+pub struct Bar;
+",
+ r"
+mod foo {
+ pub use crate::Bar;
+ pub struct Foo;
+ pub fn bar() -> Bar { _ = Foo; Bar }
+}
+pub use foo::*;
+pub struct Bar;
+",
+ )
+ }
+
+ #[test]
fn expanding_glob_import_unused() {
check_assist(
expand_glob_import,
diff --git a/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/crates/ide-assists/src/handlers/expand_rest_pattern.rs
index dc4976e8c2..4aa11b4e03 100644
--- a/crates/ide-assists/src/handlers/expand_rest_pattern.rs
+++ b/crates/ide-assists/src/handlers/expand_rest_pattern.rs
@@ -29,7 +29,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
fn expand_record_rest_pattern(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
record_pat: ast::RecordPat,
rest_pat: ast::RestPat,
) -> Option<()> {
@@ -92,7 +92,7 @@ fn expand_record_rest_pattern(
// ```
fn expand_tuple_struct_rest_pattern(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
pat: ast::TupleStructPat,
rest_pat: ast::RestPat,
) -> Option<()> {
@@ -171,7 +171,7 @@ fn expand_tuple_struct_rest_pattern(
// ```
fn expand_tuple_rest_pattern(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
pat: ast::TuplePat,
rest_pat: ast::RestPat,
) -> Option<()> {
@@ -233,7 +233,7 @@ fn expand_tuple_rest_pattern(
// ```
fn expand_slice_rest_pattern(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
pat: ast::SlicePat,
rest_pat: ast::RestPat,
) -> Option<()> {
@@ -277,7 +277,7 @@ fn expand_slice_rest_pattern(
)
}
-pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let rest_pat = ctx.find_node_at_offset::<ast::RestPat>()?;
let parent = rest_pat.syntax().parent()?;
match_ast! {
@@ -292,7 +292,7 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
}
fn gen_unnamed_pat(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
make: &SyntaxFactory,
name_gen: &mut NameGenerator,
ty: &hir::Type<'_>,
diff --git a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
index c87ded9dc4..178477c746 100644
--- a/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
+++ b/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
@@ -30,7 +30,7 @@ use syntax::{
pub(crate) fn extract_expressions_from_format_string(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let fmt_string = ctx.find_token_at_offset::<ast::String>()?;
let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?;
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index 4219e6845f..44123dc20d 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -1,6 +1,5 @@
use std::{iter, ops::RangeInclusive};
-use ast::make;
use either::Either;
use hir::{
HasSource, HirDisplay, InFile, Local, LocalSource, ModuleDef, PathResolution, Semantics,
@@ -11,10 +10,9 @@ use ide_db::{
assists::GroupLabel,
defs::Definition,
famous_defs::FamousDefs,
- helpers::mod_path_to_ast,
- imports::insert_use::{ImportScope, insert_use},
+ helpers::mod_path_to_ast_with_factory,
+ imports::insert_use::{ImportScope, insert_use_with_editor},
search::{FileReference, ReferenceCategory, SearchScope},
- source_change::SourceChangeBuilder,
syntax_helpers::node_ext::{
for_each_tail_expr, preorder_expr, walk_pat, walk_patterns_in_expr,
},
@@ -25,16 +23,18 @@ use syntax::{
SyntaxKind::{self, COMMENT},
SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, WalkEvent,
ast::{
- self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, edit::IndentLevel,
- edit_in_place::Indent,
+ self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName,
+ edit::{AstNodeEdit, IndentLevel},
+ syntax_factory::SyntaxFactory,
},
- match_ast, ted,
+ match_ast,
+ syntax_editor::{Position, SyntaxEditor},
};
use crate::{
AssistId,
- assist_context::{AssistContext, Assists, TreeMutator},
- utils::generate_impl,
+ assist_context::{AssistContext, Assists},
+ utils::generate_impl_with_item,
};
// Assist: extract_function
@@ -64,7 +64,7 @@ use crate::{
// let k = m + n;
// }
// ```
-pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let range = ctx.selection_trimmed();
if range.is_empty() {
return None;
@@ -97,8 +97,9 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
let module = semantics_scope.module();
let edition = semantics_scope.krate().edition(ctx.db());
+ let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone());
let (container_info, contains_tail_expr) =
- body.analyze_container(&ctx.sema, edition, trait_name)?;
+ body.analyze_container(editor.make(), &ctx.sema, edition, trait_name)?;
let ret_ty = body.return_ty(ctx)?;
let control_flow = body.external_control_flow(ctx, &container_info)?;
@@ -114,6 +115,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
"Extract into function",
target_range,
move |builder| {
+ let make = editor.make();
let outliving_locals: Vec<_> = ret_values.collect();
if stdx::never!(!outliving_locals.is_empty() && !ret_ty.is_unit()) {
// We should not have variables that outlive body if we have expression block
@@ -122,7 +124,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
let params = body.extracted_function_params(ctx, &container_info, locals_used);
- let name = make_function_name(&semantics_scope, &body);
+ let name = make_function_name(make, &semantics_scope, &body);
let fun = Function {
name,
@@ -139,67 +141,47 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
let new_indent = IndentLevel::from_node(&insert_after);
let old_indent = fun.body.indent_level();
- let insert_after = builder.make_syntax_mut(insert_after);
+ let call_expr = make_call(make, ctx, &fun, old_indent);
- let call_expr = make_call(ctx, &fun, old_indent);
-
- // Map the element range to replace into the mutable version
let elements = match &fun.body {
FunctionBody::Expr(expr) => {
- // expr itself becomes the replacement target
- let expr = &builder.make_mut(expr.clone());
let node = SyntaxElement::Node(expr.syntax().clone());
-
node.clone()..=node
}
- FunctionBody::Span { parent, elements, .. } => {
- // Map the element range into the mutable versions
- let parent = builder.make_mut(parent.clone());
-
- let start = parent
- .syntax()
- .children_with_tokens()
- .nth(elements.start().index())
- .expect("should be able to find mutable start element");
-
- let end = parent
- .syntax()
- .children_with_tokens()
- .nth(elements.end().index())
- .expect("should be able to find mutable end element");
-
- start..=end
- }
+ FunctionBody::Span { elements, .. } => elements.clone(),
};
let has_impl_wrapper =
insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after);
- let fn_def = format_function(ctx, module, &fun, old_indent).clone_for_update();
-
- if let Some(cap) = ctx.config.snippet_cap
- && let Some(name) = fn_def.name()
- {
- builder.add_tabstop_before(cap, name);
- }
+ let fn_def = format_function(ctx, module, &fun, old_indent, make);
// FIXME: wrap non-adt types
let fn_def = match fun.self_param_adt(ctx) {
Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => {
- fn_def.indent(1.into());
+ let fn_def = fn_def.indent_with_mapping(1.into(), make);
- let impl_ = generate_impl(&adt);
- impl_.indent(new_indent);
- impl_.get_or_create_assoc_item_list().add_item(fn_def.into());
+ let body = make.assoc_item_list([fn_def.into()]);
+ let impl_ = generate_impl_with_item(make, &adt, Some(body)).indent(new_indent);
impl_.syntax().clone()
}
- _ => {
- fn_def.indent(new_indent);
-
- fn_def.syntax().clone()
- }
+ _ => fn_def.indent_with_mapping(new_indent, make).syntax().clone(),
};
+ if let Some(cap) = ctx.config.snippet_cap {
+ let extracted_fn = fn_def.descendants().find_map(ast::Fn::cast);
+ if let Some(fn_) = extracted_fn {
+ if let Some(ws) = fn_
+ .fn_token()
+ .and_then(|tok| tok.next_token())
+ .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE)
+ {
+ editor.add_annotation(ws, builder.make_tabstop_after(cap));
+ } else if let Some(name) = fn_.name() {
+ editor.add_annotation(name.syntax(), builder.make_tabstop_before(cap));
+ }
+ }
+ }
// There are external control flows
if fun
@@ -207,7 +189,6 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
.kind
.is_some_and(|kind| matches!(kind, FlowKind::Break(_, _) | FlowKind::Continue(_)))
{
- let scope = builder.make_import_scope_mut(scope);
let control_flow_enum =
FamousDefs(&ctx.sema, module.krate(ctx.db())).core_ops_ControlFlow();
@@ -222,29 +203,45 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
);
if let Some(mod_path) = mod_path {
- insert_use(
+ insert_use_with_editor(
&scope,
- mod_path_to_ast(&mod_path, edition),
+ mod_path_to_ast_with_factory(make, &mod_path, edition),
&ctx.config.insert_use,
+ &editor,
);
}
}
}
// Replace the call site with the call to the new function
- fixup_call_site(builder, &fun.body);
- ted::replace_all(elements, vec![call_expr.into()]);
+ let needs_match_arm_comma = fun
+ .body
+ .parent()
+ .and_then(ast::MatchArm::cast)
+ .is_some_and(|arm| arm.comma_token().is_none());
+ match &fun.body {
+ FunctionBody::Expr(expr) => {
+ let mut replacement = vec![call_expr.clone().into()];
+ if needs_match_arm_comma {
+ replacement.push(make.token(T![,]).into());
+ }
+ editor.replace_with_many(expr.syntax(), replacement);
+ }
+ FunctionBody::Span { .. } => editor.replace_all(elements, vec![call_expr.into()]),
+ }
// Insert the newly extracted function (or impl)
- ted::insert_all_raw(
- ted::Position::after(insert_after),
- vec![make::tokens::whitespace(&format!("\n\n{new_indent}")).into(), fn_def.into()],
+ editor.insert_all(
+ Position::after(insert_after),
+ vec![make.whitespace(&format!("\n\n{new_indent}")).into(), fn_def.into()],
);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
fn make_function_name(
+ make: &SyntaxFactory,
semantics_scope: &hir::SemanticsScope<'_>,
body: &FunctionBody,
) -> ast::NameRef {
@@ -267,7 +264,7 @@ fn make_function_name(
counter += 1;
name = format!("{default_name}{counter}")
}
- make::name_ref(&name)
+ make.name_ref(&name)
}
/// Try to guess what user wants to extract
@@ -455,7 +452,7 @@ struct OutlivedLocal {
struct LocalUsages(ide_db::search::UsageSearchResult);
impl LocalUsages {
- fn find_local_usages(ctx: &AssistContext<'_>, var: Local) -> Self {
+ fn find_local_usages(ctx: &AssistContext<'_, '_>, var: Local) -> Self {
Self(
Definition::Local(var)
.usages(&ctx.sema)
@@ -470,7 +467,7 @@ impl LocalUsages {
}
impl<'db> Function<'db> {
- fn return_type(&self, ctx: &AssistContext<'db>) -> FunType<'db> {
+ fn return_type(&self, ctx: &AssistContext<'_, 'db>) -> FunType<'db> {
match &self.ret_ty {
RetType::Expr(ty) if ty.is_unit() => FunType::Unit,
RetType::Expr(ty) => FunType::Single(ty.clone()),
@@ -485,7 +482,7 @@ impl<'db> Function<'db> {
}
}
- fn self_param_adt(&self, ctx: &AssistContext<'_>) -> Option<ast::Adt> {
+ fn self_param_adt(&self, ctx: &AssistContext<'_, '_>) -> Option<ast::Adt> {
let self_param = self.self_param.as_ref()?;
let def = ctx.sema.to_def(self_param)?;
let adt = def.ty(ctx.db()).strip_references().as_adt()?;
@@ -510,45 +507,51 @@ impl<'db> Param<'db> {
}
}
- fn to_arg(&self, ctx: &AssistContext<'db>, edition: Edition) -> ast::Expr {
- let var = path_expr_from_local(ctx, self.var, edition);
+ fn to_arg(
+ &self,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, 'db>,
+ edition: Edition,
+ ) -> ast::Expr {
+ let var = path_expr_from_local(make, ctx, self.var, edition);
match self.kind() {
ParamKind::Value | ParamKind::MutValue => var,
- ParamKind::SharedRef => make::expr_ref(var, false),
- ParamKind::MutRef => make::expr_ref(var, true),
+ ParamKind::SharedRef => make.expr_ref(var, false),
+ ParamKind::MutRef => make.expr_ref(var, true),
}
}
fn to_param(
&self,
- ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, '_>,
module: hir::Module,
edition: Edition,
) -> ast::Param {
let var = self.var.name(ctx.db()).display(ctx.db(), edition).to_string();
- let var_name = make::name(&var);
+ let var_name = make.name(&var);
let pat = match self.kind() {
- ParamKind::MutValue => make::ident_pat(false, true, var_name),
+ ParamKind::MutValue => make.ident_pat(false, true, var_name),
ParamKind::Value | ParamKind::SharedRef | ParamKind::MutRef => {
- make::ext::simple_ident_pat(var_name)
+ make.simple_ident_pat(var_name)
}
};
- let ty = make_ty(&self.ty, ctx, module);
+ let ty = make_ty(make, &self.ty, ctx, module);
let ty = match self.kind() {
ParamKind::Value | ParamKind::MutValue => ty,
- ParamKind::SharedRef => make::ty_ref(ty, false),
- ParamKind::MutRef => make::ty_ref(ty, true),
+ ParamKind::SharedRef => make.ty_ref(ty, false),
+ ParamKind::MutRef => make.ty_ref(ty, true),
};
- make::param(pat.into(), ty)
+ make.param(pat.into(), ty)
}
}
impl<'db> TryKind<'db> {
fn of_ty(
ty: hir::Type<'db>,
- ctx: &AssistContext<'db>,
+ ctx: &AssistContext<'_, 'db>,
edition: Edition,
) -> Option<TryKind<'db>> {
if ty.is_unknown() {
@@ -569,22 +572,22 @@ impl<'db> TryKind<'db> {
}
impl<'db> FlowKind<'db> {
- fn make_result_handler(&self, expr: Option<ast::Expr>) -> ast::Expr {
+ fn make_result_handler(&self, make: &SyntaxFactory, expr: Option<ast::Expr>) -> ast::Expr {
match self {
- FlowKind::Return(_) => make::expr_return(expr),
- FlowKind::Break(label, _) => make::expr_break(label.clone(), expr),
+ FlowKind::Return(_) => make.expr_return(expr).into(),
+ FlowKind::Break(label, _) => make.expr_break(label.clone(), expr).into(),
FlowKind::Try { .. } => {
stdx::never!("cannot have result handler with try");
- expr.unwrap_or_else(|| make::expr_return(None))
+ expr.unwrap_or_else(|| make.expr_return(None).into())
}
FlowKind::Continue(label) => {
stdx::always!(expr.is_none(), "continue with value is not possible");
- make::expr_continue(label.clone())
+ make.expr_continue(label.clone()).into()
}
}
}
- fn expr_ty(&self, ctx: &AssistContext<'db>) -> Option<hir::Type<'db>> {
+ fn expr_ty(&self, ctx: &AssistContext<'_, 'db>) -> Option<hir::Type<'db>> {
match self {
FlowKind::Return(Some(expr)) | FlowKind::Break(_, Some(expr)) => {
ctx.sema.type_of_expr(expr).map(TypeInfo::adjusted)
@@ -840,6 +843,7 @@ impl FunctionBody {
fn analyze_container<'db>(
&self,
+ make: &SyntaxFactory,
sema: &Semantics<'db, RootDatabase>,
edition: Edition,
trait_name: Option<ast::Name>,
@@ -930,7 +934,7 @@ impl FunctionBody {
};
// FIXME: make trait arguments
- let trait_name = trait_name.map(|name| make::ty_path(make::ext::ident_path(&name.text())));
+ let trait_name = trait_name.map(|name| make.ty_path(make.ident_path(&name.text())).into());
let parent = self.parent()?;
let parents = generic_parents(&parent);
@@ -953,7 +957,7 @@ impl FunctionBody {
))
}
- fn return_ty<'db>(&self, ctx: &AssistContext<'db>) -> Option<RetType<'db>> {
+ fn return_ty<'db>(&self, ctx: &AssistContext<'_, 'db>) -> Option<RetType<'db>> {
match self.tail_expr() {
Some(expr) => ctx.sema.type_of_expr(&expr).map(TypeInfo::original).map(RetType::Expr),
None => Some(RetType::Stmt),
@@ -963,7 +967,7 @@ impl FunctionBody {
/// Local variables defined inside `body` that are accessed outside of it
fn ret_values<'a>(
&self,
- ctx: &'a AssistContext<'_>,
+ ctx: &'a AssistContext<'_, '_>,
parent: &SyntaxNode,
) -> impl Iterator<Item = OutlivedLocal> + 'a {
let parent = parent.clone();
@@ -976,7 +980,7 @@ impl FunctionBody {
/// Analyses the function body for external control flow.
fn external_control_flow<'db>(
&self,
- ctx: &AssistContext<'db>,
+ ctx: &AssistContext<'_, 'db>,
container_info: &ContainerInfo<'db>,
) -> Option<ControlFlow<'db>> {
let mut ret_expr = None;
@@ -1066,7 +1070,7 @@ impl FunctionBody {
/// Computes additional info that affects param type and mutability
fn extracted_function_params<'db>(
&self,
- ctx: &AssistContext<'db>,
+ ctx: &AssistContext<'_, 'db>,
container_info: &ContainerInfo<'db>,
locals: FxIndexSet<Local>,
) -> Vec<Param<'db>> {
@@ -1163,7 +1167,7 @@ fn generic_parents(parent: &SyntaxNode) -> Vec<GenericParent> {
/// checks if relevant var is used with `&mut` access inside body
fn has_exclusive_usages(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
usages: &LocalUsages,
body: &FunctionBody,
) -> bool {
@@ -1177,7 +1181,7 @@ fn has_exclusive_usages(
fn reference_is_exclusive(
reference: &FileReference,
node: &dyn HasTokenAtOffset,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> bool {
// FIXME: this quite an incorrect way to go about doing this :-)
// `FileReference` is an IDE-type --- it encapsulates data communicated to the human,
@@ -1191,7 +1195,7 @@ fn reference_is_exclusive(
}
// we take `&mut` reference to variable: `&mut v`
- let path = match path_element_of_reference(node, reference) {
+ let path = match path_element_of_reference(node, reference.range) {
Some(path) => path,
None => return false,
};
@@ -1200,7 +1204,7 @@ fn reference_is_exclusive(
}
/// checks if this expr requires `&mut` access, recurses on field access
-fn expr_require_exclusive_access(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option<bool> {
+fn expr_require_exclusive_access(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option<bool> {
if let ast::Expr::MacroExpr(_) = expr {
// FIXME: expand macro and check output for mutable usages of the variable?
return None;
@@ -1282,10 +1286,10 @@ impl HasTokenAtOffset for FunctionBody {
/// `node` must cover `reference`, that is `node.text_range().contains_range(reference.range)`
fn path_element_of_reference(
node: &dyn HasTokenAtOffset,
- reference: &FileReference,
+ reference_range: TextRange,
) -> Option<ast::Expr> {
- let token = node.token_at_offset(reference.range.start()).right_biased().or_else(|| {
- stdx::never!(false, "cannot find token at variable usage: {:?}", reference);
+ let token = node.token_at_offset(reference_range.start()).right_biased().or_else(|| {
+ stdx::never!(false, "cannot find token at variable usage: {:?}", reference_range);
None
})?;
let path = token.parent_ancestors().find_map(ast::Expr::cast).or_else(|| {
@@ -1320,7 +1324,7 @@ fn locals_defined_in_body(
/// Returns usage details if local variable is used after(outside of) body
fn local_outlives_body(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
body_range: TextRange,
local: Local,
parent: &SyntaxNode,
@@ -1345,7 +1349,7 @@ fn local_outlives_body(
/// checks if the relevant local was defined before(outside of) body
fn is_defined_outside_of_body(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
body: &FunctionBody,
src: &LocalSource,
) -> bool {
@@ -1413,58 +1417,50 @@ fn impl_type_name(impl_node: &ast::Impl) -> Option<String> {
Some(impl_node.self_ty()?.to_string())
}
-/// Fixes up the call site before the target expressions are replaced with the call expression
-fn fixup_call_site(builder: &mut SourceChangeBuilder, body: &FunctionBody) {
- let parent_match_arm = body.parent().and_then(ast::MatchArm::cast);
-
- if let Some(parent_match_arm) = parent_match_arm
- && parent_match_arm.comma_token().is_none()
- {
- let parent_match_arm = builder.make_mut(parent_match_arm);
- ted::append_child_raw(parent_match_arm.syntax(), make::token(T![,]));
- }
-}
-
-fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) -> SyntaxNode {
+fn make_call<'db>(
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, 'db>,
+ fun: &Function<'db>,
+ indent: IndentLevel,
+) -> SyntaxNode {
let ret_ty = fun.return_type(ctx);
let name = fun.name.clone();
- let args = fun.params.iter().map(|param| param.to_arg(ctx, fun.mods.edition));
+ let args = fun.params.iter().map(|param| param.to_arg(make, ctx, fun.mods.edition));
let mut call_expr = if fun.make_this_param().is_some() {
- let self_arg = make::expr_path(make::ext::ident_path("self"));
- let func = make::expr_path(make::path_unqualified(make::path_segment(name)));
- make::expr_call(func, make::arg_list(Some(self_arg).into_iter().chain(args))).into()
+ let self_arg = make.expr_path(make.ident_path("self"));
+ let func = make.expr_path(make.path_unqualified(make.path_segment(name)));
+ make.expr_call(func, make.arg_list(Some(self_arg).into_iter().chain(args))).into()
} else if fun.self_param.is_some() {
- let self_arg = make::expr_path(make::ext::ident_path("self"));
- make::expr_method_call(self_arg, name, make::arg_list(args)).into()
+ let self_arg = make.expr_path(make.ident_path("self"));
+ make.expr_method_call(self_arg, name, make.arg_list(args)).into()
} else {
- let func = make::expr_path(make::path_unqualified(make::path_segment(name)));
- make::expr_call(func, make::arg_list(args)).into()
+ let func = make.expr_path(make.path_unqualified(make.path_segment(name)));
+ make.expr_call(func, make.arg_list(args)).into()
};
let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
if fun.control_flow.is_async {
- call_expr = make::expr_await(call_expr);
+ call_expr = make.expr_await(call_expr).into();
}
- let expr = handler.make_call_expr(call_expr).clone_for_update();
- expr.indent(indent);
+ let expr = handler.make_call_expr(make, call_expr).indent_with_mapping(indent, make);
let outliving_bindings = match fun.outliving_locals.as_slice() {
[] => None,
[var] => {
let name = var.local.name(ctx.db());
- let name = make::name(&name.display(ctx.db(), fun.mods.edition).to_string());
- Some(ast::Pat::IdentPat(make::ident_pat(false, var.mut_usage_outside_body, name)))
+ let name = make.name(&name.display(ctx.db(), fun.mods.edition).to_string());
+ Some(ast::Pat::IdentPat(make.ident_pat(false, var.mut_usage_outside_body, name)))
}
vars => {
let binding_pats = vars.iter().map(|var| {
let name = var.local.name(ctx.db());
- let name = make::name(&name.display(ctx.db(), fun.mods.edition).to_string());
- make::ident_pat(false, var.mut_usage_outside_body, name).into()
+ let name = make.name(&name.display(ctx.db(), fun.mods.edition).to_string());
+ make.ident_pat(false, var.mut_usage_outside_body, name).into()
});
- Some(ast::Pat::TuplePat(make::tuple_pat(binding_pats)))
+ Some(ast::Pat::TuplePat(make.tuple_pat(binding_pats)))
}
};
@@ -1472,7 +1468,7 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) -
if let Some(bindings) = outliving_bindings {
// with bindings that outlive it
- make::let_stmt(bindings, None, Some(expr)).syntax().clone_for_update()
+ make.let_stmt(bindings, None, Some(expr)).syntax().clone()
} else if parent_match_arm.as_ref().is_some() {
// as a tail expr for a match arm
expr.syntax().clone()
@@ -1481,7 +1477,7 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) -
&& (!fun.outliving_locals.is_empty() || !expr.is_block_like())
{
// as an expr stmt
- make::expr_stmt(expr).syntax().clone_for_update()
+ make.expr_stmt(expr).syntax().clone()
} else {
// as a tail expr, or a block
expr.syntax().clone()
@@ -1527,100 +1523,106 @@ impl<'db> FlowHandler<'db> {
}
}
- fn make_call_expr(&self, call_expr: ast::Expr) -> ast::Expr {
+ fn make_call_expr(&self, make: &SyntaxFactory, call_expr: ast::Expr) -> ast::Expr {
match self {
FlowHandler::None => call_expr,
- FlowHandler::Try { kind: _ } => make::expr_try(call_expr),
+ FlowHandler::Try { kind: _ } => make.expr_try(call_expr),
FlowHandler::If { action } => {
- let action = action.make_result_handler(None);
- let stmt = make::expr_stmt(action);
- let block = make::block_expr(iter::once(stmt.into()), None);
- let controlflow_break_path = make::path_from_text("ControlFlow::Break");
- let condition = make::expr_let(
- make::tuple_struct_pat(
+ let action = action.make_result_handler(make, None);
+ let stmt = make.expr_stmt(action);
+ let block = make.block_expr(iter::once(stmt.into()), None);
+ let controlflow_break_path = make.path_from_text("ControlFlow::Break");
+ let condition = make.expr_let(
+ make.tuple_struct_pat(
controlflow_break_path,
- iter::once(make::wildcard_pat().into()),
+ iter::once(make.wildcard_pat().into()),
)
.into(),
call_expr,
);
- make::expr_if(condition.into(), block, None).into()
+ make.expr_if(condition.into(), block, None).into()
}
FlowHandler::IfOption { action } => {
- let path = make::ext::ident_path("Some");
- let value_pat = make::ext::simple_ident_pat(make::name("value"));
- let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into()));
- let cond = make::expr_let(pattern.into(), call_expr);
- let value = make::expr_path(make::ext::ident_path("value"));
- let action_expr = action.make_result_handler(Some(value));
- let action_stmt = make::expr_stmt(action_expr);
- let then = make::block_expr(iter::once(action_stmt.into()), None);
- make::expr_if(cond.into(), then, None).into()
+ let path = make.ident_path("Some");
+ let value_pat = make.simple_ident_pat(make.name("value"));
+ let pattern = make.tuple_struct_pat(path, iter::once(value_pat.into()));
+ let cond = make.expr_let(pattern.into(), call_expr);
+ let value = make.expr_path(make.ident_path("value"));
+ let action_expr = action.make_result_handler(make, Some(value));
+ let action_stmt = make.expr_stmt(action_expr);
+ let then = make.block_expr(iter::once(action_stmt.into()), None);
+ make.expr_if(cond.into(), then, None).into()
}
FlowHandler::MatchOption { none } => {
let some_name = "value";
let some_arm = {
- let path = make::ext::ident_path("Some");
- let value_pat = make::ext::simple_ident_pat(make::name(some_name));
- let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
- let value = make::expr_path(make::ext::ident_path(some_name));
- make::match_arm(pat.into(), None, value)
+ let path = make.ident_path("Some");
+ let value_pat = make.simple_ident_pat(make.name(some_name));
+ let pat = make.tuple_struct_pat(path, iter::once(value_pat.into()));
+ let value = make.expr_path(make.ident_path(some_name));
+ make.match_arm(pat.into(), None, value)
};
let none_arm = {
- let path = make::ext::ident_path("None");
- let pat = make::path_pat(path);
- make::match_arm(pat, None, none.make_result_handler(None))
+ let path = make.ident_path("None");
+ let pat = make.path_pat(path);
+ make.match_arm(pat, None, none.make_result_handler(make, None))
};
- let arms = make::match_arm_list(vec![some_arm, none_arm]);
- make::expr_match(call_expr, arms).into()
+ let arms = make.match_arm_list(vec![some_arm, none_arm]);
+ make.expr_match(call_expr, arms).into()
}
FlowHandler::MatchResult { err } => {
let ok_name = "value";
let err_name = "value";
let ok_arm = {
- let path = make::ext::ident_path("Ok");
- let value_pat = make::ext::simple_ident_pat(make::name(ok_name));
- let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
- let value = make::expr_path(make::ext::ident_path(ok_name));
- make::match_arm(pat.into(), None, value)
+ let path = make.ident_path("Ok");
+ let value_pat = make.simple_ident_pat(make.name(ok_name));
+ let pat = make.tuple_struct_pat(path, iter::once(value_pat.into()));
+ let value = make.expr_path(make.ident_path(ok_name));
+ make.match_arm(pat.into(), None, value)
};
let err_arm = {
- let path = make::ext::ident_path("Err");
- let value_pat = make::ext::simple_ident_pat(make::name(err_name));
- let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
- let value = make::expr_path(make::ext::ident_path(err_name));
- make::match_arm(pat.into(), None, err.make_result_handler(Some(value)))
+ let path = make.ident_path("Err");
+ let value_pat = make.simple_ident_pat(make.name(err_name));
+ let pat = make.tuple_struct_pat(path, iter::once(value_pat.into()));
+ let value = make.expr_path(make.ident_path(err_name));
+ make.match_arm(pat.into(), None, err.make_result_handler(make, Some(value)))
};
- let arms = make::match_arm_list(vec![ok_arm, err_arm]);
- make::expr_match(call_expr, arms).into()
+ let arms = make.match_arm_list(vec![ok_arm, err_arm]);
+ make.expr_match(call_expr, arms).into()
}
}
}
}
-fn path_expr_from_local(ctx: &AssistContext<'_>, var: Local, edition: Edition) -> ast::Expr {
+fn path_expr_from_local(
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, '_>,
+ var: Local,
+ edition: Edition,
+) -> ast::Expr {
let name = var.name(ctx.db()).display(ctx.db(), edition).to_string();
- make::expr_path(make::ext::ident_path(&name))
+ make.expr_path(make.ident_path(&name))
}
-fn format_function(
- ctx: &AssistContext<'_>,
+fn format_function<'db>(
+ ctx: &AssistContext<'_, 'db>,
module: hir::Module,
- fun: &Function<'_>,
+ fun: &Function<'db>,
old_indent: IndentLevel,
+ make: &SyntaxFactory,
) -> ast::Fn {
- let fun_name = make::name(&fun.name.text());
- let params = fun.make_param_list(ctx, module, fun.mods.edition);
- let ret_ty = fun.make_ret_ty(ctx, module);
- let body = make_body(ctx, old_indent, fun);
- let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun);
+ let fun_name = make.name(&fun.name.text());
+ let params = fun.make_param_list(make, ctx, module, fun.mods.edition);
+ let ret_ty = fun.make_ret_ty(make, ctx, module);
+ let body = make_body(make, ctx, old_indent, fun);
+ let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, make, fun);
- make::fn_(
+ make.fn_(
fun.mods.attrs.clone(),
None,
- fun_name,
+ fun_name.clone(),
generic_params,
where_clause,
params,
@@ -1633,21 +1635,23 @@ fn format_function(
)
}
-fn make_generic_params_and_where_clause(
- ctx: &AssistContext<'_>,
- fun: &Function<'_>,
+fn make_generic_params_and_where_clause<'db>(
+ ctx: &AssistContext<'_, 'db>,
+ make: &SyntaxFactory,
+ fun: &Function<'db>,
) -> (Option<ast::GenericParamList>, Option<ast::WhereClause>) {
let used_type_params = fun.type_params(ctx);
- let generic_param_list = make_generic_param_list(ctx, fun, &used_type_params);
- let where_clause = make_where_clause(ctx, fun, &used_type_params);
+ let generic_param_list = make_generic_param_list(ctx, make, fun, &used_type_params);
+ let where_clause = make_where_clause(ctx, make, fun, &used_type_params);
(generic_param_list, where_clause)
}
-fn make_generic_param_list(
- ctx: &AssistContext<'_>,
- fun: &Function<'_>,
+fn make_generic_param_list<'db>(
+ ctx: &AssistContext<'_, 'db>,
+ make: &SyntaxFactory,
+ fun: &Function<'db>,
used_type_params: &[TypeParam],
) -> Option<ast::GenericParamList> {
let mut generic_params = fun
@@ -1662,14 +1666,14 @@ fn make_generic_param_list(
.peekable();
if generic_params.peek().is_some() {
- Some(make::generic_param_list(generic_params))
+ Some(make.generic_param_list(generic_params))
} else {
None
}
}
fn param_is_required(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
param: &ast::GenericParam,
used_type_params: &[TypeParam],
) -> bool {
@@ -1683,7 +1687,8 @@ fn param_is_required(
}
fn make_where_clause(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
+ make: &SyntaxFactory,
fun: &Function<'_>,
used_type_params: &[TypeParam],
) -> Option<ast::WhereClause> {
@@ -1698,11 +1703,11 @@ fn make_where_clause(
})
.peekable();
- if predicates.peek().is_some() { Some(make::where_clause(predicates)) } else { None }
+ if predicates.peek().is_some() { Some(make.where_clause(predicates)) } else { None }
}
fn pred_is_required(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
pred: &ast::WherePred,
used_type_params: &[TypeParam],
) -> bool {
@@ -1712,7 +1717,7 @@ fn pred_is_required(
}
}
-fn resolved_type_param(ctx: &AssistContext<'_>, pred: &ast::WherePred) -> Option<TypeParam> {
+fn resolved_type_param(ctx: &AssistContext<'_, '_>, pred: &ast::WherePred) -> Option<TypeParam> {
let path = match pred.ty()? {
ast::Type::PathType(path_type) => path_type.path(),
_ => None,
@@ -1726,7 +1731,7 @@ fn resolved_type_param(ctx: &AssistContext<'_>, pred: &ast::WherePred) -> Option
impl<'db> Function<'db> {
/// Collect all the `TypeParam`s used in the `body` and `params`.
- fn type_params(&self, ctx: &AssistContext<'db>) -> Vec<TypeParam> {
+ fn type_params(&self, ctx: &AssistContext<'_, 'db>) -> Vec<TypeParam> {
let type_params_in_descendant_paths =
self.body.descendant_paths().filter_map(|it| match ctx.sema.resolve_path(&it) {
Some(PathResolution::TypeParam(type_param)) => Some(type_param),
@@ -1738,35 +1743,41 @@ impl<'db> Function<'db> {
fn make_param_list(
&self,
- ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, '_>,
module: hir::Module,
edition: Edition,
) -> ast::ParamList {
- let this_param = self.make_this_param().map(|f| f());
+ let this_param = self.make_this_param().map(|f| f(make));
let self_param = self.self_param.clone().filter(|_| this_param.is_none());
- let params = self.params.iter().map(|param| param.to_param(ctx, module, edition));
- make::param_list(self_param, this_param.into_iter().chain(params))
+ let params = self.params.iter().map(|param| param.to_param(make, ctx, module, edition));
+ make.param_list(self_param, this_param.into_iter().chain(params))
}
- fn make_this_param(&self) -> Option<impl FnOnce() -> ast::Param> {
+ fn make_this_param(&self) -> Option<impl FnOnce(&SyntaxFactory) -> ast::Param> {
if let Some(name) = self.mods.trait_name.clone()
&& let Some(self_param) = &self.self_param
{
- Some(|| {
- let bounds = make::type_bound_list([make::type_bound(name)]);
- let pat = make::path_pat(make::ext::ident_path("this"));
- let mut ty = make::impl_trait_type(bounds.unwrap()).into();
+ Some(move |make: &SyntaxFactory| {
+ let bounds = make.type_bound_list([make.type_bound(name)]);
+ let pat = make.path_pat(make.ident_path("this"));
+ let mut ty = make.impl_trait_type(bounds.unwrap()).into();
if self_param.amp_token().is_some() {
- ty = make::ty_ref(ty, self_param.mut_token().is_some());
+ ty = make.ty_ref(ty, self_param.mut_token().is_some());
}
- make::param(pat, ty)
+ make.param(pat, ty)
})
} else {
None
}
}
- fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> {
+ fn make_ret_ty(
+ &self,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, 'db>,
+ module: hir::Module,
+ ) -> Option<ast::RetType> {
let fun_ty = self.return_type(ctx);
let handler = FlowHandler::from_ret_ty(self, &fun_ty);
let ret_ty = match &handler {
@@ -1774,67 +1785,75 @@ impl<'db> Function<'db> {
if matches!(fun_ty, FunType::Unit) {
return None;
}
- fun_ty.make_ty(ctx, module)
+ fun_ty.make_ty(make, ctx, module)
}
FlowHandler::Try { kind: TryKind::Option } => {
- make::ext::ty_option(fun_ty.make_ty(ctx, module))
+ make.ty_option(fun_ty.make_ty(make, ctx, module)).into()
}
FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => {
let handler_ty = parent_ret_ty
.type_arguments()
.nth(1)
- .map(|ty| make_ty(&ty, ctx, module))
- .unwrap_or_else(make::ty_placeholder);
- make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
+ .map(|ty| make_ty(make, &ty, ctx, module))
+ .unwrap_or_else(|| make.ty_placeholder());
+ make.ty_result(fun_ty.make_ty(make, ctx, module), handler_ty).into()
}
- FlowHandler::If { .. } => make::ty("ControlFlow<()>"),
+ FlowHandler::If { .. } => make.ty("ControlFlow<()>"),
FlowHandler::IfOption { action } => {
let handler_ty = action
.expr_ty(ctx)
- .map(|ty| make_ty(&ty, ctx, module))
- .unwrap_or_else(make::ty_placeholder);
- make::ext::ty_option(handler_ty)
+ .map(|ty| make_ty(make, &ty, ctx, module))
+ .unwrap_or_else(|| make.ty_placeholder());
+ make.ty_option(handler_ty).into()
+ }
+ FlowHandler::MatchOption { .. } => {
+ make.ty_option(fun_ty.make_ty(make, ctx, module)).into()
}
- FlowHandler::MatchOption { .. } => make::ext::ty_option(fun_ty.make_ty(ctx, module)),
FlowHandler::MatchResult { err } => {
let handler_ty = err
.expr_ty(ctx)
- .map(|ty| make_ty(&ty, ctx, module))
- .unwrap_or_else(make::ty_placeholder);
- make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
+ .map(|ty| make_ty(make, &ty, ctx, module))
+ .unwrap_or_else(|| make.ty_placeholder());
+ make.ty_result(fun_ty.make_ty(make, ctx, module), handler_ty).into()
}
};
- Some(make::ret_type(ret_ty))
+ Some(make.ret_type(ret_ty))
}
}
impl<'db> FunType<'db> {
- fn make_ty(&self, ctx: &AssistContext<'db>, module: hir::Module) -> ast::Type {
+ fn make_ty(
+ &self,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, 'db>,
+ module: hir::Module,
+ ) -> ast::Type {
match self {
- FunType::Unit => make::ty_unit(),
- FunType::Single(ty) => make_ty(ty, ctx, module),
+ FunType::Unit => make.ty_unit(),
+ FunType::Single(ty) => make_ty(make, ty, ctx, module),
FunType::Tuple(types) => match types.as_slice() {
[] => {
stdx::never!("tuple type with 0 elements");
- make::ty_unit()
+ make.ty_unit()
}
[ty] => {
stdx::never!("tuple type with 1 element");
- make_ty(ty, ctx, module)
+ make_ty(make, ty, ctx, module)
}
types => {
- let types = types.iter().map(|ty| make_ty(ty, ctx, module));
- make::ty_tuple(types)
+ let types = types.iter().map(|ty| make_ty(make, ty, ctx, module));
+ make.ty_tuple(types)
}
},
}
}
}
-fn make_body(
- ctx: &AssistContext<'_>,
+fn make_body<'db>(
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, 'db>,
old_indent: IndentLevel,
- fun: &Function<'_>,
+ fun: &Function<'db>,
) -> ast::BlockExpr {
let ret_ty = fun.return_type(ctx);
let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
@@ -1848,7 +1867,7 @@ fn make_body(
match expr {
ast::Expr::BlockExpr(block) => {
// If the extracted expression is itself a block, there is no need to wrap it inside another block.
- block.dedent(old_indent);
+ let block = block.dedent(old_indent);
let elements = block.stmt_list().map_or_else(
|| Either::Left(iter::empty()),
|stmt_list| {
@@ -1865,12 +1884,12 @@ fn make_body(
Either::Right(elements)
},
);
- make::hacky_block_expr(elements, block.tail_expr())
+ make.hacky_block_expr(elements, block.tail_expr())
}
_ => {
- expr.reindent_to(1.into());
+ let expr = expr.dedent(old_indent).indent(1.into());
- make::block_expr(Vec::new(), Some(expr))
+ make.block_expr(Vec::new(), Some(expr))
}
}
}
@@ -1901,13 +1920,14 @@ fn make_body(
None => match fun.outliving_locals.as_slice() {
[] => {}
[var] => {
- tail_expr = Some(path_expr_from_local(ctx, var.local, fun.mods.edition));
+ tail_expr =
+ Some(path_expr_from_local(make, ctx, var.local, fun.mods.edition));
}
vars => {
- let exprs = vars
- .iter()
- .map(|var| path_expr_from_local(ctx, var.local, fun.mods.edition));
- let expr = make::expr_tuple(exprs);
+ let exprs = vars.iter().map(|var| {
+ path_expr_from_local(make, ctx, var.local, fun.mods.edition)
+ });
+ let expr = make.expr_tuple(exprs);
tail_expr = Some(expr.into());
}
},
@@ -1919,80 +1939,90 @@ fn make_body(
.map(|node_or_token| match &node_or_token {
syntax::NodeOrToken::Node(node) => match ast::Stmt::cast(node.clone()) {
Some(stmt) => {
- stmt.reindent_to(body_indent);
- let ast_node = stmt.syntax().clone_subtree();
- syntax::NodeOrToken::Node(ast_node)
+ let stmt = stmt.dedent(old_indent).indent(body_indent);
+ syntax::NodeOrToken::Node(stmt.syntax().clone())
}
_ => node_or_token,
},
_ => node_or_token,
})
.collect::<Vec<SyntaxElement>>();
- if let Some(tail_expr) = &mut tail_expr {
- tail_expr.reindent_to(body_indent);
- }
+ tail_expr = tail_expr.map(|expr| expr.dedent(old_indent).indent(body_indent));
- make::hacky_block_expr(elements, tail_expr)
+ make.hacky_block_expr(elements, tail_expr)
}
};
match &handler {
FlowHandler::None => block,
FlowHandler::Try { kind } => {
- let block = with_default_tail_expr(block, make::ext::expr_unit());
- map_tail_expr(block, |tail_expr| {
+ let block = with_default_tail_expr(make, block, make.expr_unit());
+ map_tail_expr(make, block, |tail_expr| {
let constructor = match kind {
TryKind::Option => "Some",
TryKind::Result { .. } => "Ok",
};
- let func = make::expr_path(make::ext::ident_path(constructor));
- let args = make::arg_list(iter::once(tail_expr));
- make::expr_call(func, args).into()
+ let func = make.expr_path(make.ident_path(constructor));
+ let args = make.arg_list(iter::once(tail_expr));
+ make.expr_call(func, args).into()
})
}
FlowHandler::If { .. } => {
- let controlflow_continue = make::expr_call(
- make::expr_path(make::path_from_text("ControlFlow::Continue")),
- make::arg_list([make::ext::expr_unit()]),
- )
- .into();
- with_tail_expr(block, controlflow_continue)
+ let controlflow_continue = make
+ .expr_call(
+ make.expr_path(make.path_from_text("ControlFlow::Continue")),
+ make.arg_list([make.expr_unit()]),
+ )
+ .into();
+ with_tail_expr(make, block, controlflow_continue)
}
FlowHandler::IfOption { .. } => {
- let none = make::expr_path(make::ext::ident_path("None"));
- with_tail_expr(block, none)
+ let none = make.expr_path(make.ident_path("None"));
+ with_tail_expr(make, block, none)
}
- FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| {
- let some = make::expr_path(make::ext::ident_path("Some"));
- let args = make::arg_list(iter::once(tail_expr));
- make::expr_call(some, args).into()
+ FlowHandler::MatchOption { .. } => map_tail_expr(make, block, |tail_expr| {
+ let some = make.expr_path(make.ident_path("Some"));
+ let args = make.arg_list(iter::once(tail_expr));
+ make.expr_call(some, args).into()
}),
- FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| {
- let ok = make::expr_path(make::ext::ident_path("Ok"));
- let args = make::arg_list(iter::once(tail_expr));
- make::expr_call(ok, args).into()
+ FlowHandler::MatchResult { .. } => map_tail_expr(make, block, |tail_expr| {
+ let ok = make.expr_path(make.ident_path("Ok"));
+ let args = make.arg_list(iter::once(tail_expr));
+ make.expr_call(ok, args).into()
}),
}
}
-fn map_tail_expr(block: ast::BlockExpr, f: impl FnOnce(ast::Expr) -> ast::Expr) -> ast::BlockExpr {
+fn map_tail_expr(
+ make: &SyntaxFactory,
+ block: ast::BlockExpr,
+ f: impl FnOnce(ast::Expr) -> ast::Expr,
+) -> ast::BlockExpr {
let tail_expr = match block.tail_expr() {
Some(tail_expr) => tail_expr,
None => return block,
};
- make::block_expr(block.statements(), Some(f(tail_expr)))
+ make.block_expr(block.statements(), Some(f(tail_expr)))
}
-fn with_default_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr {
+fn with_default_tail_expr(
+ make: &SyntaxFactory,
+ block: ast::BlockExpr,
+ tail_expr: ast::Expr,
+) -> ast::BlockExpr {
match block.tail_expr() {
Some(_) => block,
- None => make::block_expr(block.statements(), Some(tail_expr)),
+ None => make.block_expr(block.statements(), Some(tail_expr)),
}
}
-fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr {
+fn with_tail_expr(
+ make: &SyntaxFactory,
+ block: ast::BlockExpr,
+ tail_expr: ast::Expr,
+) -> ast::BlockExpr {
let stmt_tail_opt: Option<ast::Stmt> =
- block.tail_expr().map(|expr| make::expr_stmt(expr).into());
+ block.tail_expr().map(|expr| make.expr_stmt(expr).into());
let mut elements: Vec<SyntaxElement> = vec![];
@@ -2012,10 +2042,10 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr
elements.push(syntax::NodeOrToken::Node(stmt_tail.syntax().clone()));
}
- make::hacky_block_expr(elements, Some(tail_expr))
+ make.hacky_block_expr(elements, Some(tail_expr))
}
-fn format_type(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> String {
+fn format_type(ty: &hir::Type<'_>, ctx: &AssistContext<'_, '_>, module: hir::Module) -> String {
ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_owned())
}
@@ -2024,42 +2054,51 @@ fn is_inherit_attr(attr: &ast::Attr) -> bool {
matches!(name.as_str(), "track_caller" | "cfg")
}
-fn make_ty(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type {
+fn make_ty(
+ make: &SyntaxFactory,
+ ty: &hir::Type<'_>,
+ ctx: &AssistContext<'_, '_>,
+ module: hir::Module,
+) -> ast::Type {
let ty_str = format_type(ty, ctx, module);
- make::ty(&ty_str)
+ make.ty(&ty_str)
}
fn rewrite_body_segment(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
to_this_param: Option<ast::SelfParam>,
params: &[Param<'_>],
handler: &FlowHandler<'_>,
syntax: &SyntaxNode,
) -> SyntaxNode {
let to_this_param = to_this_param.and_then(|it| ctx.sema.to_def(&it));
- let syntax = fix_param_usages(ctx, to_this_param, params, syntax);
- update_external_control_flow(handler, &syntax);
- syntax
+ let (param_editor, param_root) = SyntaxEditor::new(syntax.clone());
+ fix_param_usages(&param_editor, syntax, &param_root, ctx, to_this_param, params);
+ let syntax = param_editor.finish().new_root().clone();
+
+ let (flow_editor, flow_root) = SyntaxEditor::new(syntax);
+ update_external_control_flow(&flow_editor, &flow_root, handler);
+ flow_editor.finish().new_root().clone()
}
/// change all usages to account for added `&`/`&mut` for some params
fn fix_param_usages(
- ctx: &AssistContext<'_>,
+ editor: &SyntaxEditor,
+ source_syntax: &SyntaxNode,
+ syntax: &SyntaxNode,
+ ctx: &AssistContext<'_, '_>,
to_this_param: Option<Local>,
params: &[Param<'_>],
- syntax: &SyntaxNode,
-) -> SyntaxNode {
+) {
let mut usages_for_param: Vec<(&Param<'_>, Vec<ast::Expr>)> = Vec::new();
let mut usages_for_self_param: Vec<ast::Expr> = Vec::new();
+ let source_range = source_syntax.text_range();
+ let source_start = source_range.start();
- let tm = TreeMutator::new(syntax);
let reference_filter = |reference: &FileReference| {
- syntax
- .text_range()
- .contains_range(reference.range)
- .then_some(())
- .and_then(|_| path_element_of_reference(syntax, reference))
- .map(|expr| tm.make_mut(&expr))
+ source_range.contains_range(reference.range).then_some(())?;
+ let local_range = reference.range - source_start;
+ path_element_of_reference(syntax, local_range)
};
if let Some(self_param) = to_this_param {
@@ -2079,11 +2118,11 @@ fn fix_param_usages(
usages_for_param.push((param, usages.unique().collect()));
}
- let res = tm.make_syntax_mut(syntax);
+ let make = editor.make();
for self_usage in usages_for_self_param {
- let this_expr = make::expr_path(make::ext::ident_path("this")).clone_for_update();
- ted::replace(self_usage.syntax(), this_expr.syntax());
+ let this_expr = make.expr_path(make.ident_path("this"));
+ editor.replace(self_usage.syntax(), this_expr.syntax());
}
for (param, usages) in usages_for_param {
for usage in usages {
@@ -2098,7 +2137,7 @@ fn fix_param_usages(
Some(ast::Expr::RefExpr(node))
if param.kind() == ParamKind::MutRef && node.mut_token().is_some() =>
{
- ted::replace(
+ editor.replace(
node.syntax(),
node.expr().expect("RefExpr::expr() cannot be None").syntax(),
);
@@ -2106,23 +2145,25 @@ fn fix_param_usages(
Some(ast::Expr::RefExpr(node))
if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() =>
{
- ted::replace(
+ editor.replace(
node.syntax(),
node.expr().expect("RefExpr::expr() cannot be None").syntax(),
);
}
Some(_) | None => {
- let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update();
- ted::replace(usage.syntax(), p.syntax())
+ let p = make.expr_prefix(T![*], usage.clone());
+ editor.replace(usage.syntax(), p.syntax())
}
}
}
}
-
- res
}
-fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode) {
+fn update_external_control_flow(
+ editor: &SyntaxEditor,
+ syntax: &SyntaxNode,
+ handler: &FlowHandler<'_>,
+) {
let mut nested_loop = None;
let mut nested_scope = None;
for event in syntax.preorder() {
@@ -2151,19 +2192,25 @@ fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode)
match expr {
ast::Expr::ReturnExpr(return_expr) => {
let expr = return_expr.expr();
- if let Some(replacement) = make_rewritten_flow(handler, expr) {
- ted::replace(return_expr.syntax(), replacement.syntax())
+ if let Some(replacement) =
+ make_rewritten_flow(handler, expr, editor.make())
+ {
+ editor.replace(return_expr.syntax(), replacement.syntax())
}
}
ast::Expr::BreakExpr(break_expr) if nested_loop.is_none() => {
let expr = break_expr.expr();
- if let Some(replacement) = make_rewritten_flow(handler, expr) {
- ted::replace(break_expr.syntax(), replacement.syntax())
+ if let Some(replacement) =
+ make_rewritten_flow(handler, expr, editor.make())
+ {
+ editor.replace(break_expr.syntax(), replacement.syntax())
}
}
ast::Expr::ContinueExpr(continue_expr) if nested_loop.is_none() => {
- if let Some(replacement) = make_rewritten_flow(handler, None) {
- ted::replace(continue_expr.syntax(), replacement.syntax())
+ if let Some(replacement) =
+ make_rewritten_flow(handler, None, editor.make())
+ {
+ editor.replace(continue_expr.syntax(), replacement.syntax())
}
}
_ => {
@@ -2186,27 +2233,29 @@ fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode)
fn make_rewritten_flow(
handler: &FlowHandler<'_>,
arg_expr: Option<ast::Expr>,
+ make: &SyntaxFactory,
) -> Option<ast::Expr> {
let value = match handler {
FlowHandler::None | FlowHandler::Try { .. } => return None,
- FlowHandler::If { .. } => make::expr_call(
- make::expr_path(make::path_from_text("ControlFlow::Break")),
- make::arg_list([make::ext::expr_unit()]),
- )
- .into(),
+ FlowHandler::If { .. } => make
+ .expr_call(
+ make.expr_path(make.path_from_text("ControlFlow::Break")),
+ make.arg_list([make.expr_unit()]),
+ )
+ .into(),
FlowHandler::IfOption { .. } => {
- let expr = arg_expr.unwrap_or_else(make::ext::expr_unit);
- let args = make::arg_list([expr]);
- make::expr_call(make::expr_path(make::ext::ident_path("Some")), args).into()
+ let expr = arg_expr.unwrap_or_else(|| make.expr_unit());
+ let args = make.arg_list([expr]);
+ make.expr_call(make.expr_path(make.ident_path("Some")), args).into()
}
- FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")),
+ FlowHandler::MatchOption { .. } => make.expr_path(make.ident_path("None")),
FlowHandler::MatchResult { .. } => {
- let expr = arg_expr.unwrap_or_else(make::ext::expr_unit);
- let args = make::arg_list([expr]);
- make::expr_call(make::expr_path(make::ext::ident_path("Err")), args).into()
+ let expr = arg_expr.unwrap_or_else(|| make.expr_unit());
+ let args = make.arg_list([expr]);
+ make.expr_call(make.expr_path(make.ident_path("Err")), args).into()
}
};
- Some(make::expr_return(Some(value)).clone_for_update())
+ Some(make.expr_return(Some(value)).into())
}
#[cfg(test)]
@@ -2450,6 +2499,7 @@ fn $0fun_name() {
check_assist(
extract_function,
r#"
+//- minicore: iterator
fn foo() {
$0for v in &[0, 1] { }$0
}
@@ -4697,7 +4747,7 @@ async fn some_function() {
check_assist(
extract_function,
r#"
-//- minicore: future, result
+//- minicore: future, result, try
async fn foo() -> Result<(), ()> {
$0async {}.await;
Err(())?$0
diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs
index dcbeaefa21..9e06a17337 100644
--- a/crates/ide-assists/src/handlers/extract_module.rs
+++ b/crates/ide-assists/src/handlers/extract_module.rs
@@ -52,7 +52,7 @@ use super::remove_unused_param::range_to_remove;
// name + 2
// }
// ```
-pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
if ctx.has_empty_selection() {
return None;
}
@@ -267,7 +267,7 @@ fn extract_child_target(
impl Module {
fn get_usages_and_record_fields(
&self,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
replace_range: TextRange,
) -> (FxHashMap<FileId, Vec<(TextRange, String)>>, Vec<SyntaxNode>, FxHashMap<TextSize, ast::Use>)
{
@@ -356,7 +356,7 @@ impl Module {
fn expand_and_group_usages_file_wise(
&self,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
replace_range: TextRange,
node_def: Definition,
refs_in_files: &mut FxHashMap<FileId, Vec<(TextRange, String)>>,
@@ -449,7 +449,7 @@ impl Module {
fn resolve_imports(
&mut self,
module: Option<ast::Module>,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Vec<TextRange> {
let mut imports_to_remove = vec![];
let mut node_set = FxHashSet::default();
@@ -491,7 +491,7 @@ impl Module {
def: Definition,
use_node: &SyntaxNode,
curr_parent_module: &Option<ast::Module>,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<TextRange> {
//We only need to find in the current file
let selection_range = ctx.selection_trimmed();
@@ -689,7 +689,7 @@ fn check_intersection_and_push(
fn check_def_in_mod_and_out_sel(
def: Definition,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
curr_parent_module: &Option<ast::Module>,
selection_range: TextRange,
curr_file_id: FileId,
diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
index 21013e2e61..50ce8d9b4d 100644
--- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -5,7 +5,7 @@ use hir::{EnumVariant, HasCrate, Module, ModuleDef, Name};
use ide_db::{
FxHashSet, RootDatabase,
defs::Definition,
- helpers::mod_path_to_ast,
+ helpers::mod_path_to_ast_with_factory,
imports::insert_use::{ImportScope, InsertUseConfig, insert_use_with_editor},
path_transform::PathTransform,
search::FileReference,
@@ -40,7 +40,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
pub(crate) fn extract_struct_from_enum_variant(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let variant = ctx.find_node_at_offset::<ast::Variant>()?;
let field_list = extract_field_list_if_applicable(&variant)?;
@@ -338,7 +338,7 @@ fn update_variant(
let name = variant.name()?;
let generic_args = generics
.filter(|generics| generics.generic_params().count() > 0)
- .map(|generics| generics.to_generic_args());
+ .map(|generics| generics.to_generic_args(make));
// FIXME: replace with a `ast::make` constructor
let ty = match generic_args {
Some(generic_args) => make.ty(&format!("{name}{generic_args}")),
@@ -401,7 +401,12 @@ fn apply_references(
) {
let make = editor.make();
if let Some((scope, path)) = import {
- insert_use_with_editor(&scope, mod_path_to_ast(&path, edition), &insert_use_cfg, editor);
+ insert_use_with_editor(
+ &scope,
+ mod_path_to_ast_with_factory(make, &path, edition),
+ &insert_use_cfg,
+ editor,
+ );
}
// deep clone to prevent cycle
let path = make.path_from_segments(iter::once(segment.clone()), false);
@@ -411,7 +416,7 @@ fn apply_references(
}
fn process_references(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
visited_modules: &mut FxHashSet<Module>,
enum_module_def: &ModuleDef,
variant_hir_name: &Name,
diff --git a/crates/ide-assists/src/handlers/extract_type_alias.rs b/crates/ide-assists/src/handlers/extract_type_alias.rs
index eda35eba45..ecb031e42d 100644
--- a/crates/ide-assists/src/handlers/extract_type_alias.rs
+++ b/crates/ide-assists/src/handlers/extract_type_alias.rs
@@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists};
// field: Type,
// }
// ```
-pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
if ctx.has_empty_selection() {
return None;
}
@@ -75,7 +75,7 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
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(|it| it.to_generic_args().generic_args());
+ let ty_args = generic_params.as_ref().map(|it| it.to_generic_args(make).generic_args());
let new_ty = if let Some(ty_args) = ty_args {
make.generic_ty_path_segment(make.name_ref(name), ty_args)
} else {
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index c5c57c76b4..d4a0490f16 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -65,7 +65,7 @@ use crate::{AssistContext, AssistId, Assists, utils::is_body_const};
// VAR_NAME * 4;
// }
// ```
-pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let node = if ctx.has_empty_selection() {
if let Some(t) = ctx.token_at_offset().find(|it| it.kind() == T![;]) {
t.parent().and_then(ast::ExprStmt::cast)?.syntax().clone()
@@ -332,7 +332,7 @@ fn peel_parens(mut expr: ast::Expr) -> ast::Expr {
/// Check whether the node is a valid expression which can be extracted to a variable.
/// In general that's true for any expression, but in some cases that would produce invalid code.
-fn valid_target_expr(ctx: &AssistContext<'_>) -> impl Fn(SyntaxNode) -> Option<ast::Expr> {
+fn valid_target_expr(ctx: &AssistContext<'_, '_>) -> impl Fn(SyntaxNode) -> Option<ast::Expr> {
let selection = ctx.selection_trimmed();
move |node| match node.kind() {
SyntaxKind::LOOP_EXPR | SyntaxKind::LET_EXPR => None,
@@ -383,7 +383,7 @@ impl ExtractionKind {
fn get_name_and_expr(
&self,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
to_extract: &ast::Expr,
) -> (String, SyntaxNode) {
// We only do this sort of extraction for fields because they should have lowercase names
@@ -416,7 +416,7 @@ impl ExtractionKind {
}
}
-fn get_literal_name(ctx: &AssistContext<'_>, expr: &ast::Expr) -> Option<String> {
+fn get_literal_name(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> Option<String> {
let ast::Expr::Literal(literal) = expr else {
return None;
};
@@ -512,7 +512,7 @@ impl Anchor {
}
}
-fn like_const_value(ctx: &AssistContext<'_>, path_resolution: hir::PathResolution) -> bool {
+fn like_const_value(ctx: &AssistContext<'_, '_>, path_resolution: hir::PathResolution) -> bool {
let db = ctx.db();
let adt_like_const_value = |adt: Option<hir::Adt>| matches!(adt, Some(hir::Adt::Struct(s)) if s.kind(db) == hir::StructKind::Unit);
match path_resolution {
diff --git a/crates/ide-assists/src/handlers/fix_visibility.rs b/crates/ide-assists/src/handlers/fix_visibility.rs
index d8714dd49c..d0f5c7c500 100644
--- a/crates/ide-assists/src/handlers/fix_visibility.rs
+++ b/crates/ide-assists/src/handlers/fix_visibility.rs
@@ -30,11 +30,11 @@ use crate::{AssistContext, AssistId, Assists};
// m::frobnicate();
// }
// ```
-pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
add_vis_to_referenced_module_def(acc, ctx)
}
-fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let path: ast::Path = ctx.find_node_at_offset()?;
let qualifier = path.qualifier()?;
let name_ref = path.segment()?.name_ref()?;
diff --git a/crates/ide-assists/src/handlers/flip_binexpr.rs b/crates/ide-assists/src/handlers/flip_binexpr.rs
index 17911150f5..d47f5c83cd 100644
--- a/crates/ide-assists/src/handlers/flip_binexpr.rs
+++ b/crates/ide-assists/src/handlers/flip_binexpr.rs
@@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists};
// let _ = 2 + 90;
// }
// ```
-pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let expr = ctx.find_node_at_offset::<BinExpr>()?;
let lhs = expr.lhs()?;
let rhs = expr.rhs()?;
@@ -114,7 +114,7 @@ impl From<ast::BinaryOp> for FlipAction {
// let _ = ..90;
// }
// ```
-pub(crate) fn flip_range_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn flip_range_expr(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let range_expr = ctx.find_node_at_offset::<ast::RangeExpr>()?;
let op = range_expr.op_token()?;
let start = range_expr.start();
diff --git a/crates/ide-assists/src/handlers/flip_comma.rs b/crates/ide-assists/src/handlers/flip_comma.rs
index 65dc36cdca..00d659adc1 100644
--- a/crates/ide-assists/src/handlers/flip_comma.rs
+++ b/crates/ide-assists/src/handlers/flip_comma.rs
@@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists};
// ((3, 4), (1, 2));
// }
// ```
-pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let comma = ctx.find_token_syntax_at_offset(T![,])?;
let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?;
let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?;
diff --git a/crates/ide-assists/src/handlers/flip_or_pattern.rs b/crates/ide-assists/src/handlers/flip_or_pattern.rs
index bd56331f41..c60c6a2a98 100644
--- a/crates/ide-assists/src/handlers/flip_or_pattern.rs
+++ b/crates/ide-assists/src/handlers/flip_or_pattern.rs
@@ -21,7 +21,7 @@ use crate::{AssistContext, AssistId, Assists};
// let (b | a) = 1;
// }
// ```
-pub(crate) fn flip_or_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn flip_or_pattern(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
// Only flip on the `|` token
let pipe = ctx.find_token_syntax_at_offset(T![|])?;
diff --git a/crates/ide-assists/src/handlers/flip_trait_bound.rs b/crates/ide-assists/src/handlers/flip_trait_bound.rs
index dfd280efa6..77d5c042c9 100644
--- a/crates/ide-assists/src/handlers/flip_trait_bound.rs
+++ b/crates/ide-assists/src/handlers/flip_trait_bound.rs
@@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
// fn foo<T: Copy + Clone>() { }
// ```
-pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
// Only flip on the `+` token
let plus = ctx.find_token_syntax_at_offset(T![+])?;
diff --git a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
index 0bb90f187c..4454e47013 100644
--- a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
@@ -58,7 +58,7 @@ use syntax::{
// ```
pub(crate) fn generate_blanket_trait_impl(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let name = ctx.find_node_at_offset::<ast::Name>()?;
let traitd = ast::Trait::cast(name.syntax().parent()?)?;
@@ -89,7 +89,24 @@ pub(crate) fn generate_blanket_trait_impl(
))]);
let trait_gen_args =
- traitd.generic_param_list().map(|param_list| param_list.to_generic_args());
+ traitd.generic_param_list().map(|param_list| param_list.to_generic_args(make));
+
+ let body = traitd.assoc_item_list().and_then(|trait_assoc_list| {
+ let items = trait_assoc_list
+ .assoc_items()
+ .filter_map(|item| {
+ let item = match item {
+ ast::AssocItem::Fn(method) if method.body().is_none() => {
+ todo_fn(make, &method, ctx.config).into()
+ }
+ ast::AssocItem::Const(_) | ast::AssocItem::TypeAlias(_) => item,
+ _ => return None,
+ };
+ Some(item.reset_indent().indent(1.into()))
+ })
+ .collect::<Vec<_>>();
+ (!items.is_empty()).then(|| make.assoc_item_list(items))
+ });
let impl_ = make.impl_trait(
cfg_attrs(&traitd),
@@ -103,23 +120,9 @@ pub(crate) fn generate_blanket_trait_impl(
thisty.into(),
trait_where_clause,
None,
- None,
+ body,
);
- if let Some(trait_assoc_list) = traitd.assoc_item_list() {
- let assoc_item_list = impl_.get_or_create_assoc_item_list_with_editor(&editor);
- for item in trait_assoc_list.assoc_items() {
- let item = match item {
- ast::AssocItem::Fn(method) if method.body().is_none() => {
- todo_fn(make, &method, ctx.config).into()
- }
- ast::AssocItem::Const(_) | ast::AssocItem::TypeAlias(_) => item,
- _ => continue,
- };
- assoc_item_list.add_item(item.reset_indent().indent(1.into()));
- }
- }
-
let impl_ = impl_.indent(indent);
editor.insert_all(
diff --git a/crates/ide-assists/src/handlers/generate_constant.rs b/crates/ide-assists/src/handlers/generate_constant.rs
index fce0ce3994..6c5042b14f 100644
--- a/crates/ide-assists/src/handlers/generate_constant.rs
+++ b/crates/ide-assists/src/handlers/generate_constant.rs
@@ -31,7 +31,7 @@ use syntax::{
// }
// ```
-pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let constant_token = ctx.find_node_at_offset::<ast::NameRef>()?;
if constant_token.to_string().chars().any(|it| !(it.is_uppercase() || it == '_')) {
cov_mark::hit!(not_constant_name);
@@ -113,7 +113,7 @@ fn get_text_for_generate_constant(
}
fn target_data_for_generate_constant(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
current_module: Module,
constant_module: Module,
) -> Option<(TextSize, IndentLevel, Option<FileId>, String)> {
diff --git a/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs b/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
index b4a17c376a..4470791f4d 100644
--- a/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
+++ b/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
@@ -30,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
pub(crate) fn generate_default_from_enum_variant(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let variant = ctx.find_node_at_offset::<ast::Variant>()?;
let variant_name = variant.name()?;
diff --git a/crates/ide-assists/src/handlers/generate_default_from_new.rs b/crates/ide-assists/src/handlers/generate_default_from_new.rs
index 739b631736..34ab3c4304 100644
--- a/crates/ide-assists/src/handlers/generate_default_from_new.rs
+++ b/crates/ide-assists/src/handlers/generate_default_from_new.rs
@@ -44,7 +44,10 @@ use crate::{
// }
// }
// ```
-pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_default_from_new(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let fn_node = ctx.find_node_at_offset::<ast::Fn>()?;
let fn_name = fn_node.name()?;
@@ -154,7 +157,7 @@ fn generate_default_impl(make: &SyntaxFactory, impl_: &ast::Impl, self_ty: ast::
)
}
-fn is_default_implemented(ctx: &AssistContext<'_>, impl_: &Impl) -> bool {
+fn is_default_implemented(ctx: &AssistContext<'_, '_>, impl_: &Impl) -> bool {
let db = ctx.sema.db;
let impl_ = ctx.sema.to_def(impl_);
let impl_def = match impl_ {
diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 9486aa6f01..6c9808fb1c 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -48,7 +48,10 @@ use crate::{
// }
// }
// ```
-pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_delegate_methods(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
if !ctx.config.code_action_grouping {
return None;
}
@@ -192,7 +195,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
None => {
let name = &strukt_name.to_string();
let ty_params = strukt.generic_param_list();
- let ty_args = ty_params.as_ref().map(|it| it.to_generic_args());
+ let ty_args = ty_params.as_ref().map(|it| it.to_generic_args(make));
let where_clause = strukt.where_clause();
let assoc_item_list = make.assoc_item_list(vec![item]);
@@ -210,7 +213,6 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
let impl_def = impl_def.indent(indent);
// Insert the impl block.
- let strukt = edit.make_mut(strukt.clone());
editor.insert_all(
Position::after(strukt.syntax()),
vec![
diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs
index 6639f10c1f..e21f1ab359 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs
@@ -86,7 +86,10 @@ use syntax::{
// }
// }
// ```
-pub(crate) fn generate_delegate_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_delegate_trait(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
if !ctx.config.code_action_grouping {
return None;
}
@@ -118,7 +121,7 @@ struct Field {
impl Field {
pub(crate) fn new(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
f: Either<ast::RecordField, (ast::TupleField, ast::TupleFieldList)>,
) -> Option<Field> {
let db = ctx.sema.db;
@@ -202,7 +205,7 @@ impl Struct {
Some(Struct { name, strukt: s })
}
- pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) {
+ pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_, '_>) {
let db = ctx.db();
for (index, delegee) in field.impls.iter().enumerate() {
@@ -249,7 +252,7 @@ impl Struct {
}
fn generate_impl(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
strukt: &Struct,
field_ty: &ast::Type,
field_name: &str,
@@ -271,9 +274,9 @@ fn generate_impl(
None,
delegee.is_unsafe(db),
bound_params.clone(),
- bound_params.map(|params| params.to_generic_args()),
+ bound_params.map(|params| params.to_generic_args(&make)),
strukt_params.clone(),
- strukt_params.map(|params| params.to_generic_args()),
+ strukt_params.map(|params| params.to_generic_args(&make)),
delegee.is_auto(db),
make.ty(&delegee.name(db).display_no_db(edition).to_smolstr()),
strukt_ty,
@@ -315,7 +318,7 @@ fn generate_impl(
let strukt_params = resolve_name_conflicts(strukt_params, &old_impl_params);
let (field_ty, ty_where_clause) = match &strukt_params {
Some(strukt_params) => {
- let args = strukt_params.to_generic_args();
+ let args = strukt_params.to_generic_args(&make);
let field_ty = rename_strukt_args(ctx, ast_strukt, field_ty, &args)?;
let where_clause = ast_strukt
.where_clause()
@@ -346,6 +349,7 @@ fn generate_impl(
// 2.2) Generate generic args applied on impl.
let (transform_args, trait_gen_params) = generate_args_for_impl(
+ &make,
old_impl_params,
&old_impl.self_ty()?,
&field_ty,
@@ -372,7 +376,7 @@ fn generate_impl(
}
});
- let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args());
+ let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args(&make));
let path_type = make.ty(&trait_.name(db).display_no_db(edition).to_smolstr());
let path_type = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type)?;
// 3) Generate delegate trait impl
@@ -412,7 +416,7 @@ fn generate_impl(
}
fn transform_impl<N: ast::AstNode>(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
strukt: &ast::Struct,
old_impl: &ast::Impl,
args: &Option<GenericArgList>,
@@ -590,13 +594,15 @@ fn finalize_delegate(
// While the last two generic args `B` and `C` doesn't change, it remains
// `<B, C>`. So we apply `<T, B, C>` as generic arguments to impl.
fn generate_args_for_impl(
+ make: &SyntaxFactory,
old_impl_gpl: Option<GenericParamList>,
self_ty: &ast::Type,
field_ty: &ast::Type,
trait_params: Option<GenericParamList>,
old_trait_args: &FxHashSet<String>,
) -> (Option<ast::GenericArgList>, Option<GenericParamList>) {
- let Some(old_impl_args) = old_impl_gpl.map(|gpl| gpl.to_generic_args().generic_args()) else {
+ let Some(old_impl_args) = old_impl_gpl.map(|gpl| gpl.to_generic_args(make).generic_args())
+ else {
return (None, trait_params);
};
@@ -638,7 +644,7 @@ fn generate_args_for_impl(
}
fn rename_strukt_args<N>(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
strukt: &ast::Struct,
item: &N,
args: &GenericArgList,
@@ -654,7 +660,7 @@ where
N::cast(transform.apply(item.syntax()))
}
-fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_>) -> bool {
+fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_, '_>) -> bool {
ctx.sema
.source(trait_)
.and_then(|src| {
diff --git a/crates/ide-assists/src/handlers/generate_deref.rs b/crates/ide-assists/src/handlers/generate_deref.rs
index a5bdf80ac7..9d1b257af4 100644
--- a/crates/ide-assists/src/handlers/generate_deref.rs
+++ b/crates/ide-assists/src/handlers/generate_deref.rs
@@ -38,11 +38,11 @@ use crate::{
// }
// }
// ```
-pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx))
}
-fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
let field = ctx.find_node_at_offset::<ast::RecordField>()?;
@@ -84,7 +84,7 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
)
}
-fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
let field = ctx.find_node_at_offset::<ast::TupleField>()?;
let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
diff --git a/crates/ide-assists/src/handlers/generate_derive.rs b/crates/ide-assists/src/handlers/generate_derive.rs
index 0129b1db39..f293e956bc 100644
--- a/crates/ide-assists/src/handlers/generate_derive.rs
+++ b/crates/ide-assists/src/handlers/generate_derive.rs
@@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists};
// y: u32,
// }
// ```
-pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let cap = ctx.config.snippet_cap?;
let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
let target = nominal.syntax().text_range();
diff --git a/crates/ide-assists/src/handlers/generate_documentation_template.rs b/crates/ide-assists/src/handlers/generate_documentation_template.rs
index 77232dfebd..89adda9386 100644
--- a/crates/ide-assists/src/handlers/generate_documentation_template.rs
+++ b/crates/ide-assists/src/handlers/generate_documentation_template.rs
@@ -43,7 +43,7 @@ use crate::assist_context::{AssistContext, Assists};
// ```
pub(crate) fn generate_documentation_template(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let name = ctx.find_node_at_offset::<ast::Name>()?;
let ast_func = name.syntax().parent().and_then(ast::Fn::cast)?;
@@ -95,7 +95,7 @@ pub(crate) fn generate_documentation_template(
// /// ```
// pub fn add(a: i32, b: i32) -> i32 { a + b }
// ```
-pub(crate) fn generate_doc_example(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_doc_example(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let tok: ast::Comment = ctx.find_token_at_offset()?;
let node = tok.syntax().parent()?;
let last_doc_token =
@@ -127,7 +127,7 @@ pub(crate) fn generate_doc_example(acc: &mut Assists, ctx: &AssistContext<'_>) -
)
}
-fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<String> {
+fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option<String> {
if !is_public(ast_func, ctx)? {
// Doctests for private items can't actually name the item, so they're pretty useless.
return None;
@@ -182,7 +182,7 @@ fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<St
Some(example)
}
-fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<String> {
+fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option<String> {
let hir_func = ctx.sema.to_def(ast_func)?;
let container = hir_func.as_assoc_item(ctx.db())?.container(ctx.db());
if let hir::AssocItemContainer::Impl(imp) = container {
@@ -281,7 +281,7 @@ fn safety_builder(ast_func: &ast::Fn) -> Option<Vec<String>> {
}
/// Checks if the function is public / exported
-fn is_public(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<bool> {
+fn is_public(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option<bool> {
let hir_func = ctx.sema.to_def(ast_func)?;
Some(
hir_func.visibility(ctx.db()) == Visibility::Public
@@ -290,7 +290,7 @@ fn is_public(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<bool> {
}
/// Checks that all parent modules of the function are public / exported
-fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext<'_>) -> bool {
+fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext<'_, '_>) -> bool {
let mut module = hir_func.module(ctx.db());
loop {
if let Some(parent) = module.parent(ctx.db()) {
@@ -305,7 +305,7 @@ fn all_parent_mods_public(hir_func: &hir::Function, ctx: &AssistContext<'_>) ->
}
/// Returns the name of the current crate
-fn crate_name(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<String> {
+fn crate_name(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> Option<String> {
let krate = ctx.sema.scope(ast_func.syntax())?.krate();
Some(krate.display_name(ctx.db())?.to_string())
}
@@ -378,7 +378,7 @@ fn self_partial_type(ast_func: &ast::Fn) -> Option<String> {
}
/// Helper function to determine if the function is in a trait implementation
-fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool {
+fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> bool {
ctx.sema
.to_def(ast_func)
.and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
@@ -387,7 +387,7 @@ fn is_in_trait_impl(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool {
}
/// Helper function to determine if the function definition is in a trait definition
-fn is_in_trait_def(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool {
+fn is_in_trait_def(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> bool {
ctx.sema
.to_def(ast_func)
.and_then(|hir_func| hir_func.as_assoc_item(ctx.db()))
@@ -490,7 +490,7 @@ fn string_vec_from(string_array: &[&str]) -> Vec<String> {
}
/// Helper function to build the path of the module in the which is the node
-fn build_path(ast_func: &ast::Fn, ctx: &AssistContext<'_>, edition: Edition) -> Option<String> {
+fn build_path(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>, edition: Edition) -> Option<String> {
let crate_name = crate_name(ast_func, ctx)?;
let leaf = self_partial_type(ast_func)
.or_else(|| ast_func.name().map(|n| n.to_string()))
@@ -508,7 +508,7 @@ fn return_type(ast_func: &ast::Fn) -> Option<ast::Type> {
}
/// Helper function to determine if the function returns some data
-fn returns_a_value(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> bool {
+fn returns_a_value(ast_func: &ast::Fn, ctx: &AssistContext<'_, '_>) -> bool {
ctx.sema
.to_def(ast_func)
.map(|hir_func| hir_func.ret_type(ctx.db()))
diff --git a/crates/ide-assists/src/handlers/generate_enum_is_method.rs b/crates/ide-assists/src/handlers/generate_enum_is_method.rs
index e2783811f7..867eaf4c29 100644
--- a/crates/ide-assists/src/handlers/generate_enum_is_method.rs
+++ b/crates/ide-assists/src/handlers/generate_enum_is_method.rs
@@ -40,7 +40,10 @@ use crate::{
// }
// }
// ```
-pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_enum_is_method(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let variant = ctx.find_node_at_offset::<ast::Variant>()?;
let parent_enum = ast::Adt::Enum(variant.parent_enum());
let variants = variant
diff --git a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
index 9a97ad1e8f..4cdc801ec1 100644
--- a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
+++ b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
@@ -40,7 +40,7 @@ use crate::{
// ```
pub(crate) fn generate_enum_try_into_method(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
generate_enum_projection_method(
acc,
@@ -85,7 +85,10 @@ pub(crate) fn generate_enum_try_into_method(
// }
// }
// ```
-pub(crate) fn generate_enum_as_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_enum_as_method(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
generate_enum_projection_method(
acc,
ctx,
@@ -113,7 +116,7 @@ struct ProjectionProps {
fn generate_enum_projection_method(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
assist_id: &'static str,
assist_description: &str,
props: ProjectionProps,
diff --git a/crates/ide-assists/src/handlers/generate_enum_variant.rs b/crates/ide-assists/src/handlers/generate_enum_variant.rs
index 9b4d44d8b5..fb43e3eaa3 100644
--- a/crates/ide-assists/src/handlers/generate_enum_variant.rs
+++ b/crates/ide-assists/src/handlers/generate_enum_variant.rs
@@ -32,7 +32,7 @@ use crate::assist_context::{AssistContext, Assists};
// let country = Countries::Lesotho;
// }
// ```
-pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let path: ast::Path = ctx.find_node_at_offset()?;
let parent = PathParent::new(&path)?;
@@ -104,7 +104,7 @@ impl PathParent {
fn make_field_list(
&self,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
make: &SyntaxFactory,
) -> Option<ast::FieldList> {
let scope = ctx.sema.scope(self.syntax())?;
@@ -156,7 +156,7 @@ fn name_from_field_shorthand(field: &ast::RecordExprField) -> Option<String> {
}
fn expr_ty(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
make: &SyntaxFactory,
arg: ast::Expr,
scope: &hir::SemanticsScope<'_>,
diff --git a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
index 55e5083811..a9f5ab6976 100644
--- a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
+++ b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
@@ -36,7 +36,7 @@ use crate::{AssistContext, Assists};
// unsafe fn foo(n: i32) -> i32 { 42i32 }
// ```
-pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let name = ctx.find_node_at_offset::<ast::Name>()?;
let func = &name.syntax().parent()?;
let func_node = ast::Fn::cast(func.clone())?;
diff --git a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
index 76246c3e8e..52df6182ac 100644
--- a/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
@@ -27,7 +27,7 @@ use crate::{
// ```
pub(crate) fn generate_from_impl_for_enum(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let variant = ctx.find_node_at_offset::<ast::Variant>()?;
let adt = ast::Adt::Enum(variant.parent_enum());
@@ -107,7 +107,10 @@ struct VariantInfo {
ty: ast::Type,
}
-fn selected_variants(ctx: &AssistContext<'_>, variant: &ast::Variant) -> Option<Vec<VariantInfo>> {
+fn selected_variants(
+ ctx: &AssistContext<'_, '_>,
+ variant: &ast::Variant,
+) -> Option<Vec<VariantInfo>> {
variant
.parent_enum()
.variant_list()?
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index 6ef492619b..14dd4061e7 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -16,9 +16,11 @@ use syntax::{
Edition, SyntaxKind, SyntaxNode, T, TextRange,
ast::{
self, AstNode, BlockExpr, CallExpr, HasArgList, HasGenericParams, HasModuleItem,
- HasTypeBounds, edit::IndentLevel, edit_in_place::Indent, make,
+ HasTypeBounds,
+ edit::{AstNodeEdit, IndentLevel},
+ syntax_factory::SyntaxFactory,
},
- ted,
+ syntax_editor::{Position, SyntaxEditor},
};
use crate::{
@@ -51,11 +53,11 @@ use crate::{
// }
//
// ```
-pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
gen_fn(acc, ctx).or_else(|| gen_method(acc, ctx))
}
-fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
let path = path_expr.path()?;
@@ -74,9 +76,10 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
{
return None;
}
+ let make = SyntaxFactory::without_mappings();
let function_builder =
- FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target, &adt_info)?;
+ FunctionBuilder::from_call(&make, ctx, &call, fn_name, target_module, target, &adt_info)?;
let text_range = call.syntax().text_range();
let label = format!("Generate {} function", function_builder.fn_name);
add_func_to_accumulator(acc, ctx, text_range, function_builder, file, adt_info, label)
@@ -101,7 +104,7 @@ impl TargetInfo {
}
fn fn_target_info(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
path: ast::Path,
call: &CallExpr,
fn_name: &str,
@@ -131,7 +134,7 @@ fn fn_target_info(
}
}
-fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
if ctx.sema.resolve_method_call(&call).is_some() {
return None;
@@ -148,7 +151,9 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let enclosing_impl = ctx.find_node_at_offset::<ast::Impl>();
let cursor_impl = enclosing_impl.filter(|impl_| {
- ctx.sema.to_def(impl_).map_or(false, |def| def.self_ty(ctx.sema.db).as_adt() == Some(adt))
+ ctx.sema.to_def(impl_).is_some_and(|def| {
+ def.self_ty(ctx.sema.db).as_adt() == Some(adt) && def.trait_(ctx.sema.db).is_none()
+ })
});
let (impl_, file) = if let Some(impl_) = cursor_impl {
@@ -158,7 +163,10 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
};
let target = get_method_target(ctx, &impl_, &adt)?;
+ let make = SyntaxFactory::without_mappings();
+
let function_builder = FunctionBuilder::from_method_call(
+ &make,
ctx,
&call,
&fn_name,
@@ -174,7 +182,7 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
fn add_func_to_accumulator(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
text_range: TextRange,
function_builder: FunctionBuilder,
file: FileId,
@@ -182,34 +190,21 @@ fn add_func_to_accumulator(
label: String,
) -> Option<()> {
acc.add(AssistId::generate("generate_function"), label, text_range, |edit| {
- edit.edit_file(file);
-
let target = function_builder.target.clone();
- let edition = function_builder.target_edition;
- let func = function_builder.render(ctx.config.snippet_cap, edit);
+ let snippet_cap = ctx.config.snippet_cap;
if let Some(adt) = adt_info
.and_then(|adt_info| if adt_info.impl_exists { None } else { Some(adt_info.adt) })
{
- let name = make::ty_path(make::ext::ident_path(&format!(
- "{}",
- adt.name(ctx.db()).display(ctx.db(), edition)
- )));
-
- // FIXME: adt may have generic params.
- let impl_ = make::impl_(None, None, None, name, None, None).clone_for_update();
-
- func.indent(IndentLevel(1));
- impl_.get_or_create_assoc_item_list().add_item(func.into());
- target.insert_impl_at(edit, impl_);
+ target.insert_impl_at(edit, file, ctx, &function_builder, adt, snippet_cap);
} else {
- target.insert_fn_at(edit, func);
+ target.insert_fn_at(edit, file, &function_builder, snippet_cap);
}
})
}
fn get_adt_source(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
adt: &hir::Adt,
fn_name: &str,
) -> Option<(Option<ast::Impl>, FileId)> {
@@ -240,7 +235,8 @@ impl FunctionBuilder {
/// Prepares a generated function that matches `call`.
/// The function is generated in `target_module` or next to `call`
fn from_call(
- ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, '_>,
call: &ast::CallExpr,
fn_name: &str,
target_module: Option<Module>,
@@ -253,9 +249,10 @@ impl FunctionBuilder {
let current_module = ctx.sema.scope(call.syntax())?.module();
let visibility = calculate_necessary_visibility(current_module, target_module, ctx);
- let fn_name = make::name(fn_name);
+ let fn_name = make.name(fn_name);
let mut necessary_generic_params = FxHashSet::default();
let params = fn_args(
+ make,
ctx,
target_module,
ast::CallableExpr::Call(call.clone()),
@@ -272,25 +269,26 @@ impl FunctionBuilder {
// If generated function has the name "new" and is an associated function, we generate fn body
// as a constructor and assume a "Self" return type.
if let Some(body) =
- make_fn_body_as_new_function(ctx, &fn_name.text(), adt_info, target_edition)
+ make_fn_body_as_new_function(make, ctx, &fn_name.text(), adt_info, target_edition)
{
- ret_type = Some(make::ret_type(make::ty_path(make::ext::ident_path("Self"))));
+ ret_type = Some(make.ret_type(make.ty_path(make.ident_path("Self")).into()));
should_focus_return_type = false;
fn_body = body;
} else {
let expr_for_ret_ty = await_expr.map_or_else(|| call.clone().into(), |it| it.into());
(ret_type, should_focus_return_type) = make_return_type(
+ make,
ctx,
&expr_for_ret_ty,
target_module,
&mut necessary_generic_params,
);
let placeholder_expr = expr_fill_default(ctx.config);
- fn_body = make::block_expr(vec![], Some(placeholder_expr));
+ fn_body = make.block_expr(vec![], Some(placeholder_expr));
};
let (generic_param_list, where_clause) =
- fn_generic_params(ctx, necessary_generic_params, &target)?;
+ fn_generic_params(make, ctx, necessary_generic_params, &target)?;
Some(Self {
target,
@@ -308,7 +306,8 @@ impl FunctionBuilder {
}
fn from_method_call(
- ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, '_>,
call: &ast::MethodCallExpr,
name: &ast::NameRef,
receiver_ty: Type<'_>,
@@ -320,10 +319,11 @@ impl FunctionBuilder {
let current_module = ctx.sema.scope(call.syntax())?.module();
let visibility = calculate_necessary_visibility(current_module, target_module, ctx);
- let fn_name = make::name(name.ident_token()?.text());
+ let fn_name = make.name(name.ident_token()?.text());
let mut necessary_generic_params = FxHashSet::default();
necessary_generic_params.extend(receiver_ty.generic_params(ctx.db()));
let params = fn_args(
+ make,
ctx,
target_module,
ast::CallableExpr::MethodCall(call.clone()),
@@ -334,14 +334,19 @@ impl FunctionBuilder {
let is_async = await_expr.is_some();
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 (ret_type, should_focus_return_type) = make_return_type(
+ make,
+ 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)?;
+ fn_generic_params(make, ctx, necessary_generic_params, &target)?;
let placeholder_expr = expr_fill_default(ctx.config);
- let fn_body = make::block_expr(vec![], Some(placeholder_expr));
+ let fn_body = make.block_expr(vec![], Some(placeholder_expr));
Some(Self {
target,
@@ -358,55 +363,28 @@ impl FunctionBuilder {
})
}
- fn render(self, cap: Option<SnippetCap>, edit: &mut SourceChangeBuilder) -> ast::Fn {
+ fn render(&self, make: &SyntaxFactory) -> ast::Fn {
let visibility = match self.visibility {
Visibility::None => None,
- Visibility::Crate => Some(make::visibility_pub_crate()),
- Visibility::Pub => Some(make::visibility_pub()),
+ Visibility::Crate => Some(make.visibility_pub_crate()),
+ Visibility::Pub => Some(make.visibility_pub()),
};
let type_params =
- self.generic_param_list.filter(|list| list.generic_params().next().is_some());
- let fn_def = make::fn_(
+ self.generic_param_list.clone().filter(|list| list.generic_params().next().is_some());
+ make.fn_(
None,
visibility,
- self.fn_name,
+ self.fn_name.clone(),
type_params,
- self.where_clause,
- self.params,
- self.fn_body,
- self.ret_type,
+ self.where_clause.clone(),
+ self.params.clone(),
+ self.fn_body.clone(),
+ self.ret_type.clone(),
self.is_async,
false, // FIXME : const and unsafe are not handled yet.
false,
false,
)
- .clone_for_update();
-
- let ret_type = fn_def.ret_type();
- // PANIC: we guarantee we always create a function body with a tail expr
- let tail_expr = fn_def
- .body()
- .expect("generated function should have a body")
- .tail_expr()
- .expect("function body should have a tail expression");
-
- if let Some(cap) = cap {
- if self.should_focus_return_type {
- // Focus the return type if there is one
- match ret_type {
- Some(ret_type) => {
- edit.add_placeholder_snippet(cap, ret_type);
- }
- None => {
- edit.add_placeholder_snippet(cap, tail_expr);
- }
- }
- } else {
- edit.add_placeholder_snippet(cap, tail_expr);
- }
- }
-
- fn_def
}
}
@@ -420,32 +398,33 @@ impl FunctionBuilder {
/// * If we could infer the return type, don't focus it (and thus focus the function body) so the
/// user can change the `todo!` function body.
fn make_return_type(
- ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, '_>,
expr: &ast::Expr,
target_module: Module,
necessary_generic_params: &mut FxHashSet<hir::GenericParam>,
) -> (Option<ast::RetType>, bool) {
- let (ret_ty, should_focus_return_type) = {
+ let (ret_ty, should_focus_return_type) =
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_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(), true);
match rendered {
- Ok(rendered) => (Some(make::ty(&rendered)), false),
- Err(_) => (Some(make::ty_placeholder()), true),
+ Ok(rendered) => (Some(make.ty(&rendered)), false),
+ Err(_) => (Some(make.ty_placeholder()), true),
}
}
- }
- };
- let ret_type = ret_ty.map(make::ret_type);
+ };
+ let ret_type = ret_ty.map(|ty| make.ret_type(ty));
(ret_type, should_focus_return_type)
}
fn make_fn_body_as_new_function(
- ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, '_>,
fn_name: &str,
adt_info: &Option<AdtInfo>,
edition: Edition,
@@ -455,7 +434,7 @@ fn make_fn_body_as_new_function(
};
let adt_info = adt_info.as_ref()?;
- let path_self = make::ext::ident_path("Self");
+ let path_self = make.ident_path("Self");
let placeholder_expr = expr_fill_default(ctx.config);
let tail_expr = if let Some(strukt) = adt_info.adt.as_struct() {
match strukt.kind(ctx.db()) {
@@ -464,8 +443,8 @@ fn make_fn_body_as_new_function(
.fields(ctx.db())
.iter()
.map(|field| {
- make::record_expr_field(
- make::name_ref(&format!(
+ make.record_expr_field(
+ make.name_ref(&format!(
"{}",
field.name(ctx.db()).display(ctx.db(), edition)
)),
@@ -474,7 +453,7 @@ fn make_fn_body_as_new_function(
})
.collect::<Vec<_>>();
- make::record_expr(path_self, make::record_expr_field_list(fields)).into()
+ make.record_expr(path_self, make.record_expr_field_list(fields)).into()
}
StructKind::Tuple => {
let args = strukt
@@ -483,20 +462,20 @@ fn make_fn_body_as_new_function(
.map(|_| placeholder_expr.clone())
.collect::<Vec<_>>();
- make::expr_call(make::expr_path(path_self), make::arg_list(args)).into()
+ make.expr_call(make.expr_path(path_self), make.arg_list(args)).into()
}
- StructKind::Unit => make::expr_path(path_self),
+ StructKind::Unit => make.expr_path(path_self),
}
} else {
placeholder_expr
};
- let fn_body = make::block_expr(vec![], Some(tail_expr));
+ let fn_body = make.block_expr(vec![], Some(tail_expr));
Some(fn_body)
}
fn get_fn_target_info(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
target_module: Option<Module>,
call: CallExpr,
) -> Option<TargetInfo> {
@@ -505,7 +484,7 @@ fn get_fn_target_info(
}
fn get_fn_target(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
target_module: Option<Module>,
call: CallExpr,
) -> Option<(GeneratedFunctionTarget, FileId)> {
@@ -522,7 +501,7 @@ fn get_fn_target(
}
fn get_method_target(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
impl_: &Option<ast::Impl>,
adt: &Adt,
) -> Option<GeneratedFunctionTarget> {
@@ -534,7 +513,7 @@ fn get_method_target(
}
fn assoc_fn_target_info(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
call: &CallExpr,
adt: hir::Adt,
fn_name: &str,
@@ -575,92 +554,236 @@ impl GeneratedFunctionTarget {
}
}
- fn insert_impl_at(&self, edit: &mut SourceChangeBuilder, impl_: ast::Impl) {
+ fn insert_impl_at(
+ &self,
+ edit: &mut SourceChangeBuilder,
+ file: FileId,
+ ctx: &AssistContext<'_, '_>,
+ function_builder: &FunctionBuilder,
+ adt: Adt,
+ cap: Option<SnippetCap>,
+ ) {
+ let editor = edit.make_editor(self.syntax());
+
match self {
GeneratedFunctionTarget::AfterItem(item) => {
- let item = edit.make_syntax_mut(item.clone());
let position = if item.parent().is_some() {
- ted::Position::after(&item)
+ Position::after(item)
} else {
- ted::Position::first_child_of(&item)
+ Position::first_child_of(item)
};
- let indent = IndentLevel::from_node(&item);
- let leading_ws = make::tokens::whitespace(&format!("\n{indent}"));
- impl_.indent(indent);
-
- ted::insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]);
+ let indent = IndentLevel::from_node(item);
+ insert_rendered_impl(
+ &editor,
+ edit,
+ ctx,
+ function_builder,
+ adt,
+ position,
+ indent,
+ indent,
+ cap,
+ );
}
GeneratedFunctionTarget::InEmptyItemList(item_list) => {
- let item_list = edit.make_syntax_mut(item_list.clone());
let insert_after =
item_list.children_with_tokens().find_or_first(|child| child.kind() == T!['{']);
let position = match insert_after {
- Some(child) => ted::Position::after(child),
- None => ted::Position::first_child_of(&item_list),
+ Some(child) => Position::after(child),
+ None => Position::first_child_of(item_list),
};
- let indent = IndentLevel::from_node(&item_list);
+ let indent = IndentLevel::from_node(item_list);
let leading_indent = indent + 1;
- let leading_ws = make::tokens::whitespace(&format!("\n{leading_indent}"));
- impl_.indent(indent);
-
- ted::insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]);
+ insert_rendered_impl(
+ &editor,
+ edit,
+ ctx,
+ function_builder,
+ adt,
+ position,
+ indent,
+ leading_indent,
+ cap,
+ );
}
GeneratedFunctionTarget::InImpl(_) => {
unreachable!("can't insert an impl inside an impl")
}
}
+ edit.add_file_edits(file, editor);
}
- fn insert_fn_at(&self, edit: &mut SourceChangeBuilder, func: ast::Fn) {
+ fn insert_fn_at(
+ &self,
+ edit: &mut SourceChangeBuilder,
+ file: FileId,
+ function_builder: &FunctionBuilder,
+ cap: Option<SnippetCap>,
+ ) {
+ let editor = edit.make_editor(self.syntax());
+ let make = editor.make();
+
match self {
GeneratedFunctionTarget::AfterItem(item) => {
- let item = edit.make_syntax_mut(item.clone());
let position = if item.parent().is_some() {
- ted::Position::after(&item)
+ Position::after(item)
} else {
- ted::Position::first_child_of(&item)
+ Position::first_child_of(item)
};
- let indent = IndentLevel::from_node(&item);
- let leading_ws = make::tokens::whitespace(&format!("\n\n{indent}"));
- func.indent(indent);
-
- ted::insert_all_raw(
+ let indent = IndentLevel::from_node(item);
+ insert_rendered_fn(
+ &editor,
+ edit,
+ function_builder,
position,
- vec![leading_ws.into(), func.syntax().clone().into()],
+ indent,
+ format!("\n\n{indent}"),
+ None,
+ cap,
);
}
GeneratedFunctionTarget::InEmptyItemList(item_list) => {
- let item_list = edit.make_syntax_mut(item_list.clone());
let insert_after =
item_list.children_with_tokens().find_or_first(|child| child.kind() == T!['{']);
let position = match insert_after {
- Some(child) => ted::Position::after(child),
- None => ted::Position::first_child_of(&item_list),
+ Some(child) => Position::after(child),
+ None => Position::first_child_of(item_list),
};
- let indent = IndentLevel::from_node(&item_list);
+ let indent = IndentLevel::from_node(item_list);
let leading_indent = indent + 1;
- let leading_ws = make::tokens::whitespace(&format!("\n{leading_indent}"));
- let trailing_ws = make::tokens::whitespace(&format!("\n{indent}"));
- func.indent(leading_indent);
-
- ted::insert_all(
+ insert_rendered_fn(
+ &editor,
+ edit,
+ function_builder,
position,
- vec![leading_ws.into(), func.syntax().clone().into(), trailing_ws.into()],
+ leading_indent,
+ format!("\n{leading_indent}"),
+ Some(format!("\n{indent}")),
+ cap,
);
}
GeneratedFunctionTarget::InImpl(impl_) => {
- let impl_ = edit.make_mut(impl_.clone());
-
let leading_indent = impl_.indent_level() + 1;
- func.indent(leading_indent);
- impl_.get_or_create_assoc_item_list().add_item(func.into());
+ if let Some(item_list) = impl_.assoc_item_list() {
+ let insert_after_item = item_list.assoc_items().last();
+ let insert_after = item_list
+ .assoc_items()
+ .last()
+ .map(|it| it.syntax().clone().into())
+ .or_else(|| {
+ item_list
+ .syntax()
+ .children_with_tokens()
+ .find_or_first(|child| child.kind() == T!['{'])
+ });
+ let position = match insert_after {
+ Some(child) => Position::after(child),
+ None => Position::first_child_of(item_list.syntax()),
+ };
+ let indent = impl_.indent_level();
+ let leading_ws = if insert_after_item.is_some() {
+ format!("\n\n{leading_indent}")
+ } else {
+ format!("\n{leading_indent}")
+ };
+ let trailing_ws = insert_after_item.is_none().then(|| format!("\n{indent}"));
+ insert_rendered_fn(
+ &editor,
+ edit,
+ function_builder,
+ position,
+ leading_indent,
+ leading_ws,
+ trailing_ws,
+ cap,
+ );
+ } else {
+ let func = function_builder.render(make).indent(leading_indent);
+ let item_list = make.assoc_item_list([func.into()]);
+ if let Some(fn_) = item_list.syntax().descendants().find_map(ast::Fn::cast) {
+ add_generated_fn_annotation(&editor, edit, function_builder, &fn_, cap);
+ }
+ editor.insert(Position::last_child_of(impl_.syntax()), item_list.syntax());
+ }
}
}
+ edit.add_file_edits(file, editor);
+ }
+}
+
+fn insert_rendered_impl(
+ editor: &SyntaxEditor,
+ edit: &mut SourceChangeBuilder,
+ ctx: &AssistContext<'_, '_>,
+ function_builder: &FunctionBuilder,
+ adt: Adt,
+ position: Position,
+ impl_indent: IndentLevel,
+ leading_ws_indent: IndentLevel,
+ cap: Option<SnippetCap>,
+) {
+ let make = editor.make();
+ let leading_ws = make.whitespace(&format!("\n{leading_ws_indent}"));
+ let name = make.ty_path(make.ident_path(&format!(
+ "{}",
+ adt.name(ctx.db()).display(ctx.db(), function_builder.target_edition)
+ )));
+
+ // FIXME: adt may have generic params.
+ let fn_ = function_builder.render(make).indent(IndentLevel(1));
+ let impl_ =
+ make.impl_(None, None, None, name.into(), None, Some(make.assoc_item_list([fn_.into()])));
+ let impl_ = impl_.indent(impl_indent);
+ if let Some(fn_) = impl_.syntax().descendants().find_map(ast::Fn::cast) {
+ add_generated_fn_annotation(editor, edit, function_builder, &fn_, cap);
+ }
+
+ editor.insert_all(position, vec![leading_ws.into(), impl_.syntax().clone().into()]);
+}
+
+fn insert_rendered_fn(
+ editor: &SyntaxEditor,
+ edit: &mut SourceChangeBuilder,
+ function_builder: &FunctionBuilder,
+ position: Position,
+ indent: IndentLevel,
+ leading_ws: String,
+ trailing_ws: Option<String>,
+ cap: Option<SnippetCap>,
+) {
+ let make = editor.make();
+ let leading_ws = make.whitespace(&leading_ws);
+ let func = function_builder.render(make).indent(indent);
+ add_generated_fn_annotation(editor, edit, function_builder, &func, cap);
+
+ let mut elements = vec![leading_ws.into(), func.syntax().clone().into()];
+ if let Some(trailing_ws) = trailing_ws {
+ elements.push(make.whitespace(&trailing_ws).into());
+ }
+ editor.insert_all(position, elements);
+}
+
+fn add_generated_fn_annotation(
+ editor: &SyntaxEditor,
+ edit: &mut SourceChangeBuilder,
+ function_builder: &FunctionBuilder,
+ fn_: &ast::Fn,
+ cap: Option<SnippetCap>,
+) {
+ let Some(cap) = cap else { return };
+
+ let annotation = edit.make_placeholder_snippet(cap);
+ if function_builder.should_focus_return_type
+ && let Some(ret_type) = fn_.ret_type()
+ {
+ editor.add_annotation(ret_type.syntax(), annotation);
+ } else if let Some(tail_expr) = fn_.body().and_then(|body| body.tail_expr()) {
+ editor.add_annotation(tail_expr.syntax(), annotation);
}
}
@@ -677,7 +800,8 @@ impl AdtInfo {
/// Computes parameter list for the generated function.
fn fn_args(
- ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, '_>,
target_module: Module,
call: ast::CallableExpr,
necessary_generic_params: &mut FxHashSet<hir::GenericParam>,
@@ -689,14 +813,15 @@ fn fn_args(
arg_types.push(fn_arg_type(ctx, target_module, &arg, necessary_generic_params));
}
deduplicate_arg_names(&mut arg_names);
- let params = arg_names.into_iter().zip(arg_types).map(|(name, ty)| {
- make::param(make::ext::simple_ident_pat(make::name(&name)).into(), make::ty(&ty))
- });
+ let params = arg_names
+ .into_iter()
+ .zip(arg_types)
+ .map(|(name, ty)| make.param(make.simple_ident_pat(make.name(&name)).into(), make.ty(&ty)));
- Some(make::param_list(
+ Some(make.param_list(
match call {
ast::CallableExpr::Call(_) => None,
- ast::CallableExpr::MethodCall(_) => Some(make::self_param()),
+ ast::CallableExpr::MethodCall(_) => Some(make.self_param()),
},
params,
))
@@ -711,7 +836,8 @@ fn fn_args(
/// currently do anything about it because it's actually easy to resolve it after the assist: just
/// use the Rename functionality.
fn fn_generic_params(
- ctx: &AssistContext<'_>,
+ make: &SyntaxFactory,
+ ctx: &AssistContext<'_, '_>,
necessary_params: FxHashSet<hir::GenericParam>,
target: &GeneratedFunctionTarget,
) -> Option<(Option<ast::GenericParamList>, Option<ast::WhereClause>)> {
@@ -737,45 +863,45 @@ fn fn_generic_params(
filter_unnecessary_bounds(&mut generic_params, &mut where_preds, necessary_params);
filter_bounds_in_scope(&mut generic_params, &mut where_preds, ctx, target);
+ let source_scope = generic_params.first().and_then(|param| ctx.sema.scope(param.node.syntax()));
+ let target_scope = source_scope.as_ref().and_then(|_| ctx.sema.scope(&target.parent()));
+
let generic_params: Vec<ast::GenericParam> =
- generic_params.into_iter().map(|it| it.node.clone_for_update()).collect();
- let where_preds: Vec<ast::WherePred> =
- where_preds.into_iter().map(|it| it.node.clone_for_update()).collect();
-
- let (generic_params, where_preds): (Vec<ast::GenericParam>, Vec<ast::WherePred>) =
- if let Some(param) = generic_params.first()
- && let source_scope = ctx.sema.scope(param.syntax())?
- && let target_scope = ctx.sema.scope(&target.parent())?
- && source_scope.module() != target_scope.module()
- {
- // 4. Rewrite paths
- let transform = PathTransform::generic_transformation(&target_scope, &source_scope);
- let generic_params = generic_params.iter().map(|it| it.syntax());
- let where_preds = where_preds.iter().map(|it| it.syntax());
- transform
- .apply_all(generic_params.chain(where_preds))
- .into_iter()
- .filter_map(|it| {
- if let Some(it) = ast::GenericParam::cast(it.clone()) {
- Some(either::Either::Left(it))
- } else {
- ast::WherePred::cast(it).map(either::Either::Right)
- }
- })
- .partition_map(|it| it)
- } else {
- (generic_params, where_preds)
- };
+ generic_params.into_iter().map(|it| it.node).collect();
+ let where_preds: Vec<ast::WherePred> = where_preds.into_iter().map(|it| it.node).collect();
+
+ let (generic_params, where_preds) = if let Some(source_scope) = source_scope
+ && let Some(target_scope) = target_scope
+ && source_scope.module() != target_scope.module()
+ {
+ // 4. Rewrite paths
+ let transform = PathTransform::generic_transformation(&target_scope, &source_scope);
+ let generic_params = generic_params.iter().map(|it| it.syntax());
+ let where_preds = where_preds.iter().map(|it| it.syntax());
+ transform
+ .apply_all(generic_params.chain(where_preds))
+ .into_iter()
+ .filter_map(|it| {
+ if let Some(it) = ast::GenericParam::cast(it.clone()) {
+ Some(either::Either::Left(it))
+ } else {
+ ast::WherePred::cast(it).map(either::Either::Right)
+ }
+ })
+ .partition_map(|it| it)
+ } else {
+ (generic_params, where_preds)
+ };
- let generic_param_list = make::generic_param_list(generic_params);
+ let generic_param_list = make.generic_param_list(generic_params);
let where_clause =
- if where_preds.is_empty() { None } else { Some(make::where_clause(where_preds)) };
+ if where_preds.is_empty() { None } else { Some(make.where_clause(where_preds)) };
Some((Some(generic_param_list), where_clause))
}
fn params_and_where_preds_in_scope(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> (Vec<ast::GenericParam>, Vec<ast::WherePred>) {
let Some(body) = containing_body(ctx) else {
return Default::default();
@@ -816,7 +942,7 @@ fn params_and_where_preds_in_scope(
(generic_params, where_clauses)
}
-fn containing_body(ctx: &AssistContext<'_>) -> Option<hir::DefWithBody> {
+fn containing_body(ctx: &AssistContext<'_, '_>) -> Option<hir::DefWithBody> {
let item: ast::Item = ctx.find_node_at_offset()?;
let def = match item {
ast::Item::Fn(it) => ctx.sema.to_def(&it)?.into(),
@@ -828,7 +954,7 @@ fn containing_body(ctx: &AssistContext<'_>) -> Option<hir::DefWithBody> {
}
fn get_bounds_in_scope<D>(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
def: D,
) -> (impl Iterator<Item = ast::GenericParam>, impl Iterator<Item = ast::WherePred>)
where
@@ -894,7 +1020,7 @@ struct WherePredWithParams {
}
fn compute_contained_params_in_generic_param(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
node: ast::GenericParam,
) -> Option<ParamBoundWithParams> {
match &node {
@@ -923,7 +1049,7 @@ fn compute_contained_params_in_generic_param(
}
fn compute_contained_params_in_where_pred(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
node: ast::WherePred,
) -> Option<WherePredWithParams> {
let self_ty = node.ty()?;
@@ -944,7 +1070,10 @@ fn compute_contained_params_in_where_pred(
Some(WherePredWithParams { node, self_ty_params, other_params })
}
-fn filter_generic_params(ctx: &AssistContext<'_>, node: SyntaxNode) -> Option<hir::GenericParam> {
+fn filter_generic_params(
+ ctx: &AssistContext<'_, '_>,
+ node: SyntaxNode,
+) -> Option<hir::GenericParam> {
let path = ast::Path::cast(node)?;
match ctx.sema.resolve_path(&path)? {
PathResolution::TypeParam(it) => Some(it.into()),
@@ -1039,7 +1168,7 @@ fn filter_unnecessary_bounds(
fn filter_bounds_in_scope(
generic_params: &mut Vec<ParamBoundWithParams>,
where_preds: &mut Vec<WherePredWithParams>,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
target: &GeneratedFunctionTarget,
) -> Option<()> {
let target_impl = target.parent().ancestors().find_map(ast::Impl::cast)?;
@@ -1122,13 +1251,13 @@ fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> Stri
}
fn fn_arg_type(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
target_module: Module,
fn_arg: &ast::Expr,
generic_params: &mut FxHashSet<hir::GenericParam>,
) -> String {
fn maybe_displayed_type(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
target_module: Module,
fn_arg: &ast::Expr,
generic_params: &mut FxHashSet<hir::GenericParam>,
@@ -1221,7 +1350,7 @@ enum Visibility {
fn calculate_necessary_visibility(
current_module: Module,
target_module: Module,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Visibility {
let db = ctx.db();
let current_module = current_module.nearest_non_block_module(db);
@@ -3242,4 +3371,82 @@ impl Foo {
",
)
}
+
+ #[test]
+ fn generate_method_skips_trait_impl_for_inherent() {
+ // regression: rust-lang/rust-analyzer#22123
+ check_assist(
+ generate_function,
+ r"
+struct Bar;
+
+impl Bar {
+ fn func1() {}
+}
+
+trait Foo { fn foo(&self); }
+
+impl Foo for Bar {
+ fn foo(&self) {
+ self.func2$0();
+ }
+}
+",
+ r"
+struct Bar;
+
+impl Bar {
+ fn func1() {}
+
+ fn func2(&self) ${0:-> _} {
+ todo!()
+ }
+}
+
+trait Foo { fn foo(&self); }
+
+impl Foo for Bar {
+ fn foo(&self) {
+ self.func2();
+ }
+}
+",
+ )
+ }
+
+ #[test]
+ fn generate_method_from_trait_impl_creates_new_inherent_impl() {
+ // #22123: no inherent impl exists, so the assist must synthesize one
+ // instead of inserting into the trait impl.
+ check_assist(
+ generate_function,
+ r"
+struct Bar;
+
+trait Foo { fn foo(&self); }
+
+impl Foo for Bar {
+ fn foo(&self) {
+ self.func2$0();
+ }
+}
+",
+ r"
+struct Bar;
+impl Bar {
+ fn func2(&self) ${0:-> _} {
+ todo!()
+ }
+}
+
+trait Foo { fn foo(&self); }
+
+impl Foo for Bar {
+ fn foo(&self) {
+ self.func2();
+ }
+}
+",
+ )
+ }
}
diff --git a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
index b884581041..7e5d5cec71 100644
--- a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
+++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -35,7 +35,7 @@ use crate::{
// }
// }
// ```
-pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
// This if condition denotes two modes this assist can work in:
// - First is acting upon selection of record fields
// - Next is acting upon a single record field
@@ -124,7 +124,7 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
// }
// }
// ```
-pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
generate_getter_impl(acc, ctx, false)
}
@@ -149,7 +149,7 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
// }
// }
// ```
-pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
generate_getter_impl(acc, ctx, true)
}
@@ -175,7 +175,7 @@ enum AssistType {
pub(crate) fn generate_getter_impl(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
mutable: bool,
) -> Option<()> {
let (strukt, info_of_record_fields, fn_names) =
@@ -215,7 +215,7 @@ pub(crate) fn generate_getter_impl(
}
fn generate_getter_from_info(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
info: &AssistInfo,
record_field_info: &RecordFieldInfo,
make: &SyntaxFactory,
@@ -329,7 +329,7 @@ fn generate_setter_from_info(
}
fn extract_and_parse(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
assist_type: AssistType,
) -> Option<(ast::Struct, Vec<RecordFieldInfo>, Vec<String>)> {
// This if condition denotes two modes assists can work in:
@@ -411,7 +411,7 @@ fn parse_record_field(
}
fn items(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
info_of_record_fields: Vec<RecordFieldInfo>,
assist_info: &AssistInfo,
make: &SyntaxFactory,
@@ -432,7 +432,7 @@ fn items(
fn build_source_change(
builder: &mut SourceChangeBuilder,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
info_of_record_fields: Vec<RecordFieldInfo>,
assist_info: AssistInfo,
) {
@@ -458,7 +458,7 @@ fn build_source_change(
let make = editor.make();
let items = items(ctx, info_of_record_fields, &assist_info, make);
let ty_params = assist_info.strukt.generic_param_list();
- let ty_args = ty_params.as_ref().map(|it| it.to_generic_args());
+ let ty_args = ty_params.as_ref().map(|it| it.to_generic_args(make));
let impl_def = make.impl_(
None,
ty_params,
diff --git a/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs
index c5a46f6981..ab0eb56fcf 100644
--- a/crates/ide-assists/src/handlers/generate_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_impl.rs
@@ -7,7 +7,7 @@ use crate::{
AssistContext, AssistId, Assists,
utils::{
self, DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl,
- generate_impl_with_factory, generate_trait_impl_intransitive,
+ generate_trait_impl_intransitive,
},
};
@@ -45,7 +45,7 @@ fn insert_impl(editor: &SyntaxEditor, impl_: &ast::Impl, nominal: &impl AstNodeE
//
// impl<T: Clone> Ctx<T> {$0}
// ```
-pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
let name = nominal.name()?;
let target = nominal.syntax().text_range();
@@ -61,7 +61,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
|edit| {
let editor = edit.make_editor(nominal.syntax());
let make = editor.make();
- let impl_ = generate_impl_with_factory(make, &nominal);
+ let impl_ = utils::generate_impl(make, &nominal);
let impl_ = insert_impl(&editor, &impl_, &nominal);
// Add a tabstop after the left curly brace
@@ -93,7 +93,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
//
// impl<T: Clone> ${1:_} for Ctx<T> {$0}
// ```
-pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
let name = nominal.name()?;
let target = nominal.syntax().text_range();
@@ -149,7 +149,7 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
// }
// }
// ```
-pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let name = ctx.find_node_at_offset::<ast::Name>()?;
let trait_ = ast::Trait::cast(name.syntax().parent()?)?;
let target_scope = ctx.sema.scope(trait_.syntax())?;
diff --git a/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
index f10b21b13e..39f304cac9 100644
--- a/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
+++ b/crates/ide-assists/src/handlers/generate_is_empty_from_len.rs
@@ -39,7 +39,10 @@ use crate::{
// }
// }
// ```
-pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_is_empty_from_len(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let fn_node = ctx.find_node_at_offset::<ast::Fn>()?;
let fn_name = fn_node.name()?;
@@ -86,7 +89,7 @@ pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext<
}
fn get_impl_method(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
impl_: &ast::Impl,
fn_name: &Name,
) -> Option<hir::Function> {
diff --git a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
index acf0819222..fd095dd9b2 100644
--- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
@@ -44,7 +44,10 @@ use crate::{AssistContext, AssistId, Assists};
// }
// }
// ```
-pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_mut_trait_impl(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?;
let indent = impl_def.indent_level();
diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs
index 520709adc5..40adfceaae 100644
--- a/crates/ide-assists/src/handlers/generate_new.rs
+++ b/crates/ide-assists/src/handlers/generate_new.rs
@@ -1,6 +1,6 @@
use ide_db::{
imports::import_assets::item_for_path_search, syntax_helpers::suggest_name::NameGenerator,
- use_trivial_constructor::use_trivial_constructor,
+ use_trivial_constructor::use_trivial_constructor_with_factory,
};
use syntax::{
ast::{self, AstNode, HasName, HasVisibility, StructKind, edit::AstNodeEdit},
@@ -33,7 +33,7 @@ use crate::{
// }
// }
// ```
-pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
let field_list: Vec<(String, ast::Type)> = match strukt.kind() {
StructKind::Record(named) => {
@@ -90,9 +90,10 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
let edition = current_module.krate(ctx.db()).edition(ctx.db());
- let expr = use_trivial_constructor(
+ let expr = use_trivial_constructor_with_factory(
+ make,
ctx.sema.db,
- ide_db::helpers::mod_path_to_ast(&type_path, edition),
+ ide_db::helpers::mod_path_to_ast_with_factory(make, &type_path, edition),
&ty,
edition,
)?;
diff --git a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
index 10c009a2ea..4348dfa212 100644
--- a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
+++ b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
@@ -1,8 +1,9 @@
use hir::next_solver::{DbInterner, TypingMode};
use hir::{HasCrate, ModuleDef, Semantics};
+use ide_db::use_trivial_constructor::use_trivial_constructor_with_factory;
use ide_db::{
- RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast,
- imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor,
+ RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory,
+ imports::import_assets::item_for_path_search,
};
use syntax::syntax_editor::{Position, SyntaxEditor};
use syntax::{
@@ -46,7 +47,7 @@ use crate::{
// ```
pub(crate) fn generate_single_field_struct_from(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let strukt_name = ctx.find_node_at_offset::<ast::Name>()?;
let adt = ast::Adt::cast(strukt_name.syntax().parent()?)?;
@@ -86,7 +87,7 @@ pub(crate) fn generate_single_field_struct_from(
let indent = strukt.indent_level();
let ty_where_clause = strukt.where_clause();
let type_gen_params = strukt.generic_param_list();
- let type_gen_args = type_gen_params.as_ref().map(|params| params.to_generic_args());
+ let type_gen_args = type_gen_params.as_ref().map(|params| params.to_generic_args(make));
let trait_gen_args = Some(make.generic_arg_list(
[ast::GenericArg::TypeArg(make.type_arg(main_field_ty.clone()))],
false,
@@ -178,7 +179,7 @@ fn make_adt_constructor(
}
fn make_constructors(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
module: hir::Module,
types: &[ast::Type],
) -> Vec<Option<ast::Expr>> {
@@ -197,7 +198,13 @@ fn make_constructors(
let ty_path = module.find_path(db, item_for_path_search(db, item_in_ns)?, cfg)?;
- use_trivial_constructor(db, mod_path_to_ast(&ty_path, edition), &ty, edition)
+ use_trivial_constructor_with_factory(
+ &make,
+ db,
+ mod_path_to_ast_with_factory(&make, &ty_path, edition),
+ &ty,
+ edition,
+ )
})
.collect()
}
diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
index 049398de8c..2493ba6632 100644
--- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
@@ -68,7 +68,10 @@ use syntax::{
// const_maker! {i32, 7}
// }
// ```
-pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn generate_trait_from_impl(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
// Get AST Node
let impl_ast = ctx.find_node_at_offset::<ast::Impl>()?;
@@ -131,7 +134,7 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
];
if let Some(params) = impl_ast.generic_param_list() {
- let gen_args = &params.to_generic_args();
+ let gen_args = &params.to_generic_args(make);
elements.insert(1, gen_args.syntax().clone().into());
}
diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs
index 21f2249a19..af048c6ae0 100644
--- a/crates/ide-assists/src/handlers/inline_call.rs
+++ b/crates/ide-assists/src/handlers/inline_call.rs
@@ -1,6 +1,5 @@
use std::collections::BTreeSet;
-use ast::make;
use either::Either;
use hir::{
FileRange, PathResolution, Semantics, TypeInfo,
@@ -8,22 +7,23 @@ use hir::{
sym,
};
use ide_db::{
- EditionedFileId, RootDatabase,
+ EditionedFileId, FileId, FxHashMap, RootDatabase,
base_db::Crate,
defs::Definition,
- imports::insert_use::remove_path_if_in_use_stmt,
+ imports::insert_use::remove_use_tree_if_simple,
path_transform::PathTransform,
search::{FileReference, FileReferenceNode, SearchScope},
- source_change::SourceChangeBuilder,
syntax_helpers::{node_ext::expr_as_name_ref, prettify_macro_expansion},
};
use itertools::{Itertools, izip};
use syntax::{
- AstNode, NodeOrToken, SyntaxKind,
+ AstNode, NodeOrToken, SyntaxKind, TextRange,
ast::{
- self, HasArgList, HasGenericArgs, Pat, PathExpr, edit::IndentLevel, edit_in_place::Indent,
+ self, HasArgList, HasGenericArgs, Pat, PathExpr,
+ edit::{AstNodeEdit, IndentLevel},
+ make,
},
- ted,
+ syntax_editor::SyntaxEditor,
};
use crate::{
@@ -69,7 +69,7 @@ use crate::{
// };
// }
// ```
-pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let def_file = ctx.file_id();
let vfs_def_file = ctx.vfs_file_id();
let name = ctx.find_node_at_offset::<ast::Name>()?;
@@ -106,37 +106,59 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let mut usages = usages.all();
let current_file_usage = usages.references.remove(&def_file);
+ let mut file_editors: FxHashMap<FileId, SyntaxEditor> = FxHashMap::default();
let mut remove_def = true;
let mut inline_refs_for_file = |file_id: EditionedFileId, refs: Vec<FileReference>| {
let file_id = file_id.file_id(ctx.db());
builder.edit_file(file_id);
let call_krate = ctx.sema.file_to_module_def(file_id).map(|it| it.krate(ctx.db()));
let count = refs.len();
- // The collects are required as we are otherwise iterating while mutating 🙅‍♀️🙅‍♂️
- let (name_refs, name_refs_use) = split_refs_and_uses(builder, refs, Some);
+ let (name_refs, use_trees) = split_refs_and_uses(refs, Some);
let call_infos: Vec<_> = name_refs
.into_iter()
.filter_map(|it| CallInfo::from_name_ref(it, call_krate?.into()))
// FIXME: do not handle callsites in macros' parameters, because
// directly inlining into macros may cause errors.
.filter(|call_info| !ctx.sema.hir_file_for(call_info.node.syntax()).is_macro())
- .map(|call_info| {
- let mut_node = builder.make_syntax_mut(call_info.node.syntax().clone());
- (call_info, mut_node)
- })
.collect();
- let replaced = call_infos
- .into_iter()
- .map(|(call_info, mut_node)| {
- let replacement =
- inline(&ctx.sema, def_file, function, &func_body, &params, &call_info);
- ted::replace(mut_node, replacement.syntax());
- })
- .count();
- if replaced + name_refs_use.len() == count {
- // we replaced all usages in this file, so we can remove the imports
- name_refs_use.iter().for_each(remove_path_if_in_use_stmt);
- } else {
+
+ // Skip calls nested inside other calls being inlined to avoid overlapping
+ // edits. Nested calls are implicitly replaced when the outer call is inlined.
+ let all_ranges: Vec<TextRange> =
+ call_infos.iter().map(|ci| ci.node.syntax().text_range()).collect();
+ let (call_infos, nested_infos): (Vec<_>, Vec<_>) =
+ call_infos.into_iter().partition(|ci| {
+ let r = ci.node.syntax().text_range();
+ !all_ranges.iter().any(|&other| other != r && other.contains_range(r))
+ });
+ let nested_count = nested_infos.len();
+
+ let anchor = call_infos
+ .first()
+ .map(|ci| ci.node.syntax().clone())
+ .or_else(|| use_trees.first().map(|ut| ut.syntax().clone()));
+ if let Some(anchor) = anchor {
+ let editor =
+ file_editors.entry(file_id).or_insert_with(|| builder.make_editor(&anchor));
+ let replaced = call_infos
+ .into_iter()
+ .map(|call_info| {
+ let replacement = inline(
+ &ctx.sema, def_file, function, &func_body, &params, &call_info,
+ editor,
+ );
+ editor.replace(call_info.node.syntax(), replacement.syntax());
+ })
+ .count();
+ if replaced + nested_count + use_trees.len() == count {
+ // we replaced all usages in this file, so we can remove the imports
+ for use_tree in &use_trees {
+ remove_use_tree_if_simple(use_tree, editor);
+ }
+ } else {
+ remove_def = false;
+ }
+ } else if use_trees.len() != count {
remove_def = false;
}
};
@@ -148,24 +170,29 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) ->
None => builder.edit_file(vfs_def_file),
}
if remove_def {
- builder.delete(ast_func.syntax().text_range());
+ let editor = file_editors
+ .entry(vfs_def_file)
+ .or_insert_with(|| builder.make_editor(ast_func.syntax()));
+ editor.delete(ast_func.syntax());
+ }
+ for (file_id, editor) in file_editors {
+ builder.add_file_edits(file_id, editor);
}
},
)
}
pub(super) fn split_refs_and_uses<T: ast::AstNode>(
- builder: &mut SourceChangeBuilder,
iter: impl IntoIterator<Item = FileReference>,
mut map_ref: impl FnMut(ast::NameRef) -> Option<T>,
-) -> (Vec<T>, Vec<ast::Path>) {
+) -> (Vec<T>, Vec<ast::UseTree>) {
iter.into_iter()
.filter_map(|file_ref| match file_ref.name {
FileReferenceNode::NameRef(name_ref) => Some(name_ref),
_ => None,
})
.filter_map(|name_ref| match name_ref.syntax().ancestors().find_map(ast::UseTree::cast) {
- Some(use_tree) => builder.make_mut(use_tree).path().map(Either::Right),
+ Some(use_tree) => Some(Either::Right(use_tree)),
None => map_ref(name_ref).map(Either::Left),
})
.partition_map(|either| either)
@@ -192,7 +219,7 @@ pub(super) fn split_refs_and_uses<T: ast::AstNode>(
// };
// }
// ```
-pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let name_ref: ast::NameRef = ctx.find_node_at_offset()?;
let call_info = CallInfo::from_name_ref(
name_ref.clone(),
@@ -235,14 +262,11 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
let syntax = call_info.node.syntax().clone();
acc.add(AssistId::refactor_inline("inline_call"), label, syntax.text_range(), |builder| {
- let replacement = inline(&ctx.sema, file_id, function, &fn_body, &params, &call_info);
- builder.replace_ast(
- match call_info.node {
- ast::CallableExpr::Call(it) => ast::Expr::CallExpr(it),
- ast::CallableExpr::MethodCall(it) => ast::Expr::MethodCallExpr(it),
- },
- replacement,
- );
+ let editor = builder.make_editor(call_info.node.syntax());
+ let replacement =
+ inline(&ctx.sema, file_id, function, &fn_body, &params, &call_info, &editor);
+ editor.replace(call_info.node.syntax(), replacement.syntax());
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
})
}
@@ -318,21 +342,25 @@ fn inline(
fn_body: &ast::BlockExpr,
params: &[(ast::Pat, Option<ast::Type>, hir::Param<'_>)],
CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo,
+ file_editor: &SyntaxEditor,
) -> ast::Expr {
+ let make = file_editor.make();
let file_id = sema.hir_file_for(fn_body.syntax());
- let mut body = if let Some(macro_file) = file_id.macro_file() {
+ let body_to_clone = if let Some(macro_file) = file_id.macro_file() {
cov_mark::hit!(inline_call_defined_in_macro);
let span_map = sema.db.expansion_span_map(macro_file);
let body_prettified =
- prettify_macro_expansion(sema.db, fn_body.syntax().clone(), &span_map, *krate);
- if let Some(body) = ast::BlockExpr::cast(body_prettified) {
- body
- } else {
- fn_body.clone_for_update()
- }
+ prettify_macro_expansion(sema.db, fn_body.syntax().clone(), span_map, *krate);
+ if let Some(body) = ast::BlockExpr::cast(body_prettified) { body } else { fn_body.clone() }
} else {
- fn_body.clone_for_update()
+ fn_body.clone()
};
+
+ // Capture before `with_ast_node` re-roots and loses the source-relative position.
+ let mut original_body_indent = IndentLevel::from_node(body_to_clone.syntax());
+ let body_offset = body_to_clone.syntax().text_range().start();
+ let (editor, body) = SyntaxEditor::with_ast_node(&body_to_clone);
+
let usages_for_locals = |local| {
Definition::Local(local)
.usages(sema)
@@ -355,7 +383,7 @@ fn inline(
.map(|FileReference { name, range, .. }| match name {
FileReferenceNode::NameRef(_) => body
.syntax()
- .covering_element(range)
+ .covering_element(range - body_offset)
.ancestors()
.nth(3)
.and_then(ast::PathExpr::cast),
@@ -368,48 +396,60 @@ fn inline(
})
.collect();
- if function.self_param(sema.db).is_some() {
- let this = || {
- make::name_ref("this")
- .syntax()
- .clone_for_update()
- .first_token()
- .expect("NameRef should have had a token.")
- };
- if let Some(self_local) = params[0].2.as_local(sema.db) {
- usages_for_locals(self_local)
- .filter_map(|FileReference { name, range, .. }| match name {
- FileReferenceNode::NameRef(_) => Some(body.syntax().covering_element(range)),
- _ => None,
- })
- .for_each(|usage| {
- ted::replace(usage, this());
- });
- }
- }
+ let has_self_param = function.self_param(sema.db).is_some();
+
+ // Collect all self token usages upfront (needed when a let binding is emitted).
+ // When self can be directly inlined, the parameter loop handles those PathExprs;
+ // when a let stmt is needed, we rename all self tokens to `this` here.
+ let self_token_usages: Vec<_> = if has_self_param {
+ params[0]
+ .2
+ .as_local(sema.db)
+ .map(|self_local| {
+ usages_for_locals(self_local)
+ .filter_map(|FileReference { name, range, .. }| match name {
+ FileReferenceNode::NameRef(_) => {
+ Some(body.syntax().covering_element(range - body_offset))
+ }
+ _ => None,
+ })
+ .collect()
+ })
+ .unwrap_or_default()
+ } else {
+ Vec::new()
+ };
- // We should place the following code after last usage of `usages_for_locals`
- // because `ted::replace` will change the offset in syntax tree, which makes
- // `FileReference` incorrect
+ // Replace `Self` type keywords with the actual impl type
if let Some(imp) =
sema.ancestors_with_macros(fn_body.syntax().clone()).find_map(ast::Impl::cast)
&& !node.syntax().ancestors().any(|anc| &anc == imp.syntax())
&& let Some(t) = imp.self_ty()
{
- while let Some(self_tok) = body
+ let self_tokens: Vec<_> = body
.syntax()
.descendants_with_tokens()
.filter_map(NodeOrToken::into_token)
- .find(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)
- {
- let replace_with = t.clone_subtree().syntax().clone_for_update();
- if !is_in_type_path(&self_tok)
- && let Some(ty) = ast::Type::cast(replace_with.clone())
- && let Some(generic_arg_list) = ty.generic_arg_list()
+ .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW)
+ .collect();
+ for self_tok in self_tokens {
+ let replace_with = if !is_in_type_path(&self_tok)
+ && let Some(generic_arg_list) = t.generic_arg_list()
{
- ted::remove(generic_arg_list.syntax());
- }
- ted::replace(self_tok, replace_with);
+ // Strip the outer generic arg list and reparse, since turbofish-less
+ // generics aren't valid in expression position. The outermost
+ // `GenericArgList` text is unique within `t`'s text (any inner generics
+ // are nested inside it), so `replacen(.., 1)` is safe.
+ let stripped = t.syntax().text().to_string().replacen(
+ &generic_arg_list.syntax().text().to_string(),
+ "",
+ 1,
+ );
+ editor.make().ty(&stripped).syntax().clone()
+ } else {
+ t.syntax().clone()
+ };
+ editor.replace(self_tok, replace_with);
}
}
@@ -428,7 +468,19 @@ fn inline(
}
}
- let mut let_stmts = Vec::new();
+ let mut let_stmts: Vec<ast::Stmt> = Vec::new();
+
+ let this_token = editor
+ .make()
+ .name_ref("this")
+ .syntax()
+ .first_token()
+ .expect("NameRef should have had a token.");
+ let rewrite_self_to_this = |editor: &SyntaxEditor| {
+ for usage in &self_token_usages {
+ editor.replace(usage.clone(), this_token.clone());
+ }
+ };
// Inline parameter expressions or generate `let` statements depending on whether inlining works or not.
for ((pat, param_ty, param), usages, expr) in izip!(params, param_use_nodes, arguments) {
@@ -444,7 +496,7 @@ fn inline(
let param_ty_prettified = prettify_macro_expansion(
sema.db,
param_ty.syntax().clone(),
- &span_map,
+ span_map,
*krate,
);
ast::Type::cast(param_ty_prettified).unwrap_or(param_ty)
@@ -458,7 +510,7 @@ fn inline(
let is_self = param.name(sema.db).is_some_and(|name| name == sym::self_);
if is_self {
- let mut this_pat = make::ident_pat(false, false, make::name("this"));
+ let mut this_pat = make.ident_pat(false, false, make.name("this"));
let mut expr = expr.clone();
if let Pat::IdentPat(pat) = pat {
match (pat.ref_token(), pat.mut_token()) {
@@ -466,11 +518,11 @@ fn inline(
(None, None) => {}
// mut self => let mut this = obj
(None, Some(_)) => {
- this_pat = make::ident_pat(false, true, make::name("this"));
+ this_pat = make.ident_pat(false, true, make.name("this"));
}
// &self => let this = &obj
(Some(_), None) => {
- expr = make::expr_ref(expr, false);
+ expr = make.expr_ref(expr, false);
}
// let foo = &mut X; &mut self => let this = &mut obj
// let mut foo = X; &mut self => let this = &mut *obj (reborrow)
@@ -479,35 +531,38 @@ fn inline(
.type_of_expr(&expr)
.map(|ty| ty.original.is_mutable_reference());
expr = if let Some(true) = should_reborrow {
- make::expr_reborrow(expr)
+ make.expr_reborrow(expr)
} else {
- make::expr_ref(expr, true)
+ make.expr_ref(expr, true)
};
}
}
};
- let_stmts
- .push(make::let_stmt(this_pat.into(), ty, Some(expr)).clone_for_update().into())
+ let_stmts.push(make.let_stmt(this_pat.into(), ty, Some(expr)).into())
} else {
- let_stmts.push(
- make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(),
- );
+ let_stmts.push(make.let_stmt(pat.clone(), ty, Some(expr.clone())).into());
}
};
+ let is_self_param =
+ has_self_param && param.name(sema.db).is_some_and(|name| name == sym::self_);
+
// check if there is a local var in the function that conflicts with parameter
// if it does then emit a let statement and continue
if func_let_vars.contains(&expr.syntax().text().to_string()) {
+ if is_self_param {
+ rewrite_self_to_this(&editor);
+ }
insert_let_stmt();
continue;
}
- let inline_direct = |usage, replacement: &ast::Expr| {
+ let inline_direct = |editor: &SyntaxEditor, usage: &PathExpr, replacement: &ast::Expr| {
if let Some(field) = path_expr_as_record_field(usage) {
cov_mark::hit!(inline_call_inline_direct_field);
- field.replace_expr(replacement.clone_for_update());
+ field.replace_expr(editor, replacement.clone());
} else {
- ted::replace(usage.syntax(), replacement.syntax().clone_for_update());
+ editor.replace(usage.syntax(), replacement.syntax());
}
};
@@ -518,66 +573,74 @@ fn inline(
&& usage.syntax().parent().and_then(ast::Expr::cast).is_some() =>
{
cov_mark::hit!(inline_call_inline_closure);
- let expr = make::expr_paren(expr.clone()).into();
- inline_direct(usage, &expr);
+ let expr = editor.make().expr_paren(expr.clone()).into();
+ inline_direct(&editor, usage, &expr);
}
// inline single use literals
[usage] if matches!(expr, ast::Expr::Literal(_)) => {
cov_mark::hit!(inline_call_inline_literal);
- inline_direct(usage, expr);
+ inline_direct(&editor, usage, expr);
}
// inline direct local arguments
[_, ..] if expr_as_name_ref(expr).is_some() => {
cov_mark::hit!(inline_call_inline_locals);
- usages.iter().for_each(|usage| inline_direct(usage, expr));
+ usages.iter().for_each(|usage| inline_direct(&editor, usage, expr));
}
// can't inline, emit a let statement
_ => {
+ if is_self_param {
+ // Rename all `self` tokens to `this` so the let binding matches.
+ rewrite_self_to_this(&editor);
+ }
insert_let_stmt();
}
}
}
+ // Apply all edits to get the transformed body
+ let edit = editor.finish();
+ let mut body = ast::BlockExpr::cast(edit.new_root().clone())
+ .expect("editor root should still be a BlockExpr");
+
+ // Apply generic substitution (needs immutable tree)
if let Some(generic_arg_list) = generic_arg_list.clone()
&& let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax()))
- {
- body.reindent_to(IndentLevel(0));
- if let Some(new_body) = ast::BlockExpr::cast(
+ && let Some(new_body) = ast::BlockExpr::cast(
PathTransform::function_call(target, source, function, generic_arg_list)
.apply(body.syntax()),
- ) {
- body = new_body;
- }
+ )
+ {
+ body = new_body;
}
let is_async_fn = function.is_async(sema.db);
if is_async_fn {
cov_mark::hit!(inline_call_async_fn);
- body = make::async_move_block_expr(body.statements(), body.tail_expr()).clone_for_update();
+ body = make.async_move_block_expr(body.statements(), body.tail_expr());
// Arguments should be evaluated outside the async block, and then moved into it.
if !let_stmts.is_empty() {
cov_mark::hit!(inline_call_async_fn_with_let_stmts);
- body.indent(IndentLevel(1));
- body = make::block_expr(let_stmts, Some(body.into())).clone_for_update();
+ body = body.indent(IndentLevel(1));
+ body = make.block_expr(let_stmts, Some(body.into()));
}
- } else if let Some(stmt_list) = body.stmt_list() {
- let position = stmt_list.l_curly_token().expect("L_CURLY for StatementList is missing.");
- let_stmts.into_iter().rev().for_each(|let_stmt| {
- ted::insert(ted::Position::after(position.clone()), let_stmt.syntax().clone());
- });
+ } else if !let_stmts.is_empty() {
+ // Prepend let statements to the body's existing statements
+ let stmts: Vec<ast::Stmt> = let_stmts.into_iter().chain(body.statements()).collect();
+ body = make.block_expr(stmts, body.tail_expr());
+ original_body_indent = IndentLevel(0);
}
let original_indentation = match node {
ast::CallableExpr::Call(it) => it.indent_level(),
ast::CallableExpr::MethodCall(it) => it.indent_level(),
};
- body.reindent_to(original_indentation);
+ body = body.dedent(original_body_indent).indent(original_indentation);
let no_stmts = body.statements().next().is_none();
match body.tail_expr() {
Some(expr) if matches!(expr, ast::Expr::ClosureExpr(_)) && no_stmts => {
- make::expr_paren(expr).clone_for_update().into()
+ make.expr_paren(expr).into()
}
Some(expr) if !is_async_fn && no_stmts => expr,
_ => match node
@@ -587,7 +650,7 @@ fn inline(
.and_then(|bin_expr| bin_expr.lhs())
{
Some(lhs) if lhs.syntax() == node.syntax() => {
- make::expr_paren(ast::Expr::BlockExpr(body)).clone_for_update().into()
+ make.expr_paren(ast::Expr::BlockExpr(body)).into()
}
_ => ast::Expr::BlockExpr(body),
},
@@ -1503,8 +1566,11 @@ async fn foo(arg: u32) -> u32 {
}
fn spawn<T>(_: T) {}
fn main() {
- spawn(async move {
- bar(42).await * 2
+ spawn({
+ let arg = 42;
+ async move {
+ bar(arg).await * 2
+ }
});
}
"#,
@@ -1535,9 +1601,12 @@ async fn foo(arg: u32) -> u32 {
}
fn spawn<T>(_: T) {}
fn main() {
- spawn(async move {
- bar(42).await;
- 42
+ spawn({
+ let arg = 42;
+ async move {
+ bar(arg).await;
+ 42
+ }
});
}
"#,
@@ -1572,10 +1641,11 @@ fn spawn<T>(_: T) {}
fn main() {
let var = 42;
spawn({
+ let x = var;
let y = var + 1;
let z: &u32 = &var;
async move {
- bar(var).await;
+ bar(x).await;
y + y + *z
}
});
@@ -1866,6 +1936,28 @@ fn _hash2(self_: &u64, state: &mut u64) {
}
#[test]
+ fn inline_callers_nested_calls() {
+ check_assist(
+ inline_into_callers,
+ r#"
+fn id$0(x: u32) -> u32 { x }
+fn main() {
+ let x = id(id(1));
+}
+"#,
+ r#"
+
+fn main() {
+ let x = {
+ let x = id(1);
+ x
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
fn inline_into_callers_in_macros_not_applicable() {
check_assist_not_applicable(
inline_into_callers,
diff --git a/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/crates/ide-assists/src/handlers/inline_const_as_literal.rs
index b11d3792bc..46cedea967 100644
--- a/crates/ide-assists/src/handlers/inline_const_as_literal.rs
+++ b/crates/ide-assists/src/handlers/inline_const_as_literal.rs
@@ -22,7 +22,10 @@ use crate::{AssistContext, AssistId, Assists};
// "Hello, World!"
// }
// ```
-pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+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)) =
@@ -57,7 +60,7 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_>
}
fn validate_type_recursively(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
ty_hir: Option<&hir::Type<'_>>,
refed: bool,
fuel: i32,
diff --git a/crates/ide-assists/src/handlers/inline_local_variable.rs b/crates/ide-assists/src/handlers/inline_local_variable.rs
index 2af074f1fc..531adf62ba 100644
--- a/crates/ide-assists/src/handlers/inline_local_variable.rs
+++ b/crates/ide-assists/src/handlers/inline_local_variable.rs
@@ -32,7 +32,7 @@ use crate::{
// (1 + 2) * 4;
// }
// ```
-pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let file_id = ctx.file_id();
let range = ctx.selection_trimmed();
let InlineData { let_stmt, delete_let, references, target } =
@@ -724,6 +724,7 @@ fn foo() {
check_assist(
inline_local_variable,
r"
+//- minicore: iterator
fn foo() {
let a$0 = vec![10, 20];
for i in a {}
diff --git a/crates/ide-assists/src/handlers/inline_macro.rs b/crates/ide-assists/src/handlers/inline_macro.rs
index 280bd7f2ca..002791b88d 100644
--- a/crates/ide-assists/src/handlers/inline_macro.rs
+++ b/crates/ide-assists/src/handlers/inline_macro.rs
@@ -1,6 +1,6 @@
use hir::db::ExpandDatabase;
use ide_db::syntax_helpers::prettify_macro_expansion;
-use syntax::ast::{self, AstNode};
+use syntax::ast::{self, AstNode, edit::AstNodeEdit};
use crate::{AssistContext, AssistId, Assists};
@@ -35,7 +35,7 @@ use crate::{AssistContext, AssistId, Assists};
// println!("{number}");
// }
// ```
-pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?;
let macro_call = ctx.sema.to_def(&unexpanded)?;
let target_crate_id = ctx.sema.file_to_module_def(ctx.vfs_file_id())?.krate(ctx.db()).into();
@@ -46,12 +46,15 @@ pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
"Inline macro".to_owned(),
text_range,
|builder| {
+ let editor = builder.make_editor(unexpanded.syntax());
let expanded = ctx.sema.parse_or_expand(macro_call.into());
let span_map = ctx.sema.db.expansion_span_map(macro_call);
// Don't call `prettify_macro_expansion()` outside the actual assist action; it does some heavy rowan tree manipulation,
// which can be very costly for big macros when it is done *even without the assist being invoked*.
- let expanded = prettify_macro_expansion(ctx.db(), expanded, &span_map, target_crate_id);
- builder.replace(text_range, expanded.to_string())
+ let expanded = prettify_macro_expansion(ctx.db(), expanded, span_map, target_crate_id);
+ let expanded = ast::edit::indent(&expanded, unexpanded.indent_level());
+ editor.replace(unexpanded.syntax(), expanded);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
@@ -207,15 +210,15 @@ macro_rules! num {
inline_macro,
r#"
macro_rules! foo {
- () => {foo!()}
+ ($t:tt) => {foo!(1)}
}
-fn f() { let result = foo$0!(); }
+fn f() { let result = foo$0!(0); }
"#,
r#"
macro_rules! foo {
- () => {foo!()}
+ ($t:tt) => {foo!(1)}
}
-fn f() { let result = foo!(); }
+fn f() { let result = foo!(1); }
"#,
);
}
@@ -258,7 +261,7 @@ macro_rules! whitespace {
if true {}
};
}
-fn f() { if true{}; }
+fn f() { if true {}; }
"#,
)
}
@@ -297,12 +300,12 @@ macro_rules! foo {
}
fn main() {
cfg_if!{
- if #[cfg(test)]{
- 1;
- }else {
- 1;
- }
-};
+ if #[cfg(test)]{
+ 1;
+ }else {
+ 1;
+ }
+ };
}
"#,
);
diff --git a/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs
index 6d8750afdc..e4a1314f5b 100644
--- a/crates/ide-assists/src/handlers/inline_type_alias.rs
+++ b/crates/ide-assists/src/handlers/inline_type_alias.rs
@@ -5,8 +5,7 @@
use hir::{HasSource, PathResolution};
use ide_db::FxHashMap;
use ide_db::{
- defs::Definition, imports::insert_use::ast_to_remove_for_path_in_use_stmt,
- search::FileReference,
+ defs::Definition, imports::insert_use::remove_use_tree_if_simple, search::FileReference,
};
use itertools::Itertools;
use syntax::ast::syntax_factory::SyntaxFactory;
@@ -46,7 +45,7 @@ use super::inline_call::split_refs_and_uses;
// let _: i32 = 3;
// }
// ```
-pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let name = ctx.find_node_at_offset::<ast::Name>()?;
let ast_alias = name.syntax().parent().and_then(ast::TypeAlias::cast)?;
@@ -72,14 +71,12 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>)
let source = ctx.sema.parse(file_id);
let editor = builder.make_editor(source.syntax());
- let (path_types, path_type_uses) =
- split_refs_and_uses(builder, refs, |path_type| {
- path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast)
- });
+ let (path_types, path_type_uses) = split_refs_and_uses(refs, |path_type| {
+ path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast)
+ });
path_type_uses
.iter()
- .flat_map(ast_to_remove_for_path_in_use_stmt)
- .for_each(|x| editor.delete(x.syntax()));
+ .for_each(|use_tree| remove_use_tree_if_simple(use_tree, &editor));
for (target, replacement) in path_types.into_iter().filter_map(|path_type| {
let replacement =
@@ -128,7 +125,7 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>)
// let a: Vec<u32>;
// }
// ```
-pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let alias_instance = ctx.find_node_at_offset::<ast::PathType>()?;
let concrete_type;
let replacement;
@@ -415,7 +412,7 @@ fn create_replacement(
editor.finish().new_root().clone()
}
-fn get_type_alias(ctx: &AssistContext<'_>, path: &ast::PathType) -> Option<ast::TypeAlias> {
+fn get_type_alias(ctx: &AssistContext<'_, '_>, path: &ast::PathType) -> Option<ast::TypeAlias> {
let resolved_path = ctx.sema.resolve_path(&path.path()?)?;
// We need the generics in the correct order to be able to map any provided
@@ -1094,7 +1091,6 @@ fn f() -> Vec<&str> {
}
//- /foo.rs
-
fn foo() {
let _: Vec<i8> = Vec::new();
}
@@ -1123,7 +1119,6 @@ mod foo;
//- /foo.rs
-
fn foo() {
let _: i32 = 0;
}
diff --git a/crates/ide-assists/src/handlers/into_to_qualified_from.rs b/crates/ide-assists/src/handlers/into_to_qualified_from.rs
index 47b273535a..9f8b08f4f8 100644
--- a/crates/ide-assists/src/handlers/into_to_qualified_from.rs
+++ b/crates/ide-assists/src/handlers/into_to_qualified_from.rs
@@ -36,7 +36,7 @@ use crate::assist_context::{AssistContext, Assists};
// let b: B = B::from(a);
// }
// ```
-pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
let nameref = method_call.name_ref()?;
let receiver = method_call.receiver()?;
diff --git a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
index 2cbeae1d19..986989952f 100644
--- a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
@@ -34,7 +34,10 @@ static ASSIST_LABEL: &str = "Introduce named lifetime";
// }
// }
// ```
-pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn introduce_named_lifetime(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
// FIXME: How can we handle renaming any one of multiple anonymous lifetimes?
// FIXME: should also add support for the case fun(f: &Foo) -> &$0Foo
let lifetime =
diff --git a/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs b/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs
index 95f223420b..427fbbeaa0 100644
--- a/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs
+++ b/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs
@@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
pub(crate) fn introduce_named_type_parameter(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
let param = impl_trait_type.syntax().ancestors().find_map(ast::Param::cast)?;
@@ -48,7 +48,8 @@ pub(crate) fn introduce_named_type_parameter(
)
.for_impl_trait_as_generic(&impl_trait_type);
- let type_param = make.type_param(make.name(&type_param_name), Some(type_bound_list));
+ let type_bound_list = non_default_bounds(&type_bound_list).then_some(type_bound_list);
+ let type_param = make.type_param(make.name(&type_param_name), type_bound_list);
let new_ty = make.ty(&type_param_name);
editor.replace(impl_trait_type.syntax(), new_ty.syntax());
@@ -63,6 +64,10 @@ pub(crate) fn introduce_named_type_parameter(
)
}
+fn non_default_bounds(bounds: &ast::TypeBoundList) -> bool {
+ bounds.bounds().collect_array().is_none_or(|[bound]| bound.syntax().text() != "Sized")
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -169,6 +174,24 @@ fn foo<
}
#[test]
+ fn replace_impl_default_bounds() {
+ check_assist(
+ introduce_named_type_parameter,
+ r#"fn foo(bar: $0impl Sized) {}"#,
+ r#"fn foo<$0S>(bar: S) {}"#,
+ );
+ }
+
+ #[test]
+ fn replace_impl_question_bounds() {
+ check_assist(
+ introduce_named_type_parameter,
+ r#"fn foo(bar: &$0impl ?Sized) {}"#,
+ r#"fn foo<$0S: ?Sized>(bar: &S) {}"#,
+ );
+ }
+
+ #[test]
fn replace_impl_with_mut() {
check_assist(
introduce_named_type_parameter,
diff --git a/crates/ide-assists/src/handlers/invert_if.rs b/crates/ide-assists/src/handlers/invert_if.rs
index c8cb7bb60f..9dda4bbb96 100644
--- a/crates/ide-assists/src/handlers/invert_if.rs
+++ b/crates/ide-assists/src/handlers/invert_if.rs
@@ -26,7 +26,7 @@ use crate::{
// if y { B } else { A }
// }
// ```
-pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let if_keyword = ctx
.find_token_syntax_at_offset(T![if])
.or_else(|| ctx.find_token_syntax_at_offset(T![else]))?;
diff --git a/crates/ide-assists/src/handlers/merge_imports.rs b/crates/ide-assists/src/handlers/merge_imports.rs
index 1dd0833fad..dc40a6a640 100644
--- a/crates/ide-assists/src/handlers/merge_imports.rs
+++ b/crates/ide-assists/src/handlers/merge_imports.rs
@@ -27,7 +27,7 @@ use Edit::*;
// ```
// use std::{fmt::Formatter, io};
// ```
-pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let (target, edits) = if ctx.has_empty_selection() {
// Merge a neighbor
cov_mark::hit!(merge_with_use_item_neighbors);
diff --git a/crates/ide-assists/src/handlers/merge_match_arms.rs b/crates/ide-assists/src/handlers/merge_match_arms.rs
index 6e84af5f91..f41769150c 100644
--- a/crates/ide-assists/src/handlers/merge_match_arms.rs
+++ b/crates/ide-assists/src/handlers/merge_match_arms.rs
@@ -33,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists, TextRange};
// }
// }
// ```
-pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let current_arm = ctx.find_node_at_trimmed_offset::<ast::MatchArm>()?;
// Don't try to handle arms with guards for now - can add support for this later
if current_arm.guard().is_some() {
@@ -107,7 +107,7 @@ fn contains_placeholder(a: &ast::MatchArm) -> bool {
fn are_same_types(
current_arm_types: &FxHashMap<String, Option<Type<'_>>>,
arm: &ast::MatchArm,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> bool {
let arm_types = get_arm_types(ctx, arm);
for (other_arm_type_name, other_arm_type) in arm_types {
@@ -122,14 +122,14 @@ fn are_same_types(
}
fn get_arm_types<'db>(
- context: &AssistContext<'db>,
+ context: &AssistContext<'_, 'db>,
arm: &ast::MatchArm,
) -> FxHashMap<String, Option<Type<'db>>> {
let mut mapping: FxHashMap<String, Option<Type<'db>>> = FxHashMap::default();
fn recurse<'db>(
map: &mut FxHashMap<String, Option<Type<'db>>>,
- ctx: &AssistContext<'db>,
+ ctx: &AssistContext<'_, 'db>,
pat: &Option<ast::Pat>,
) {
if let Some(local_pat) = pat {
diff --git a/crates/ide-assists/src/handlers/merge_nested_if.rs b/crates/ide-assists/src/handlers/merge_nested_if.rs
index e491c043e1..bb3725a133 100644
--- a/crates/ide-assists/src/handlers/merge_nested_if.rs
+++ b/crates/ide-assists/src/handlers/merge_nested_if.rs
@@ -23,7 +23,7 @@ use crate::{
// if x == 3 && y == 4 { 1 }
// }
// ```
-pub(crate) fn merge_nested_if(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn merge_nested_if(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let if_keyword = ctx.find_token_syntax_at_offset(T![if])?;
let expr = ast::IfExpr::cast(if_keyword.parent()?)?;
let if_range = if_keyword.text_range();
diff --git a/crates/ide-assists/src/handlers/move_bounds.rs b/crates/ide-assists/src/handlers/move_bounds.rs
index e044068ff7..3efc847141 100644
--- a/crates/ide-assists/src/handlers/move_bounds.rs
+++ b/crates/ide-assists/src/handlers/move_bounds.rs
@@ -24,7 +24,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
pub(crate) fn move_bounds_to_where_clause(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let type_param_list = ctx.find_node_at_offset::<ast::GenericParamList>()?;
diff --git a/crates/ide-assists/src/handlers/move_const_to_impl.rs b/crates/ide-assists/src/handlers/move_const_to_impl.rs
index 86bdf3f8b4..4071f16a9f 100644
--- a/crates/ide-assists/src/handlers/move_const_to_impl.rs
+++ b/crates/ide-assists/src/handlers/move_const_to_impl.rs
@@ -42,7 +42,7 @@ use crate::assist_context::{AssistContext, Assists};
// }
// }
// ```
-pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let db = ctx.db();
let const_: ast::Const = ctx.find_node_at_offset()?;
// Don't show the assist when the cursor is at the const's body.
diff --git a/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/crates/ide-assists/src/handlers/move_from_mod_rs.rs
index a36d3136a1..21a8c53e8c 100644
--- a/crates/ide-assists/src/handlers/move_from_mod_rs.rs
+++ b/crates/ide-assists/src/handlers/move_from_mod_rs.rs
@@ -20,7 +20,7 @@ use crate::{
// ```
// fn t() {}
// ```
-pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let source_file = ctx.find_node_at_offset::<ast::SourceFile>()?;
let module = ctx.sema.file_to_module_def(ctx.vfs_file_id())?;
// Enable this assist if the user select all "meaningful" content in the source file
diff --git a/crates/ide-assists/src/handlers/move_guard.rs b/crates/ide-assists/src/handlers/move_guard.rs
index 7309cc6d06..7cd0d01238 100644
--- a/crates/ide-assists/src/handlers/move_guard.rs
+++ b/crates/ide-assists/src/handlers/move_guard.rs
@@ -38,7 +38,7 @@ use crate::{AssistContext, AssistId, Assists};
// }
// }
// ```
-pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let match_arm = ctx.find_node_at_offset::<MatchArm>()?;
let guard = match_arm.guard()?;
if ctx.offset() > guard.syntax().text_range().end() {
@@ -122,7 +122,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>)
// ```
pub(crate) fn move_arm_cond_to_match_guard(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?;
let match_pat = match_arm.pat()?;
diff --git a/crates/ide-assists/src/handlers/move_module_to_file.rs b/crates/ide-assists/src/handlers/move_module_to_file.rs
index 503003bc6b..3fec102275 100644
--- a/crates/ide-assists/src/handlers/move_module_to_file.rs
+++ b/crates/ide-assists/src/handlers/move_module_to_file.rs
@@ -24,7 +24,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
// mod foo;
// ```
-pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let module_ast = ctx.find_node_at_offset::<ast::Module>()?;
let module_items = module_ast.item_list()?;
diff --git a/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/crates/ide-assists/src/handlers/move_to_mod_rs.rs
index 5e95b264fc..7407bd1a0c 100644
--- a/crates/ide-assists/src/handlers/move_to_mod_rs.rs
+++ b/crates/ide-assists/src/handlers/move_to_mod_rs.rs
@@ -20,7 +20,7 @@ use crate::{
// ```
// fn t() {}
// ```
-pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let source_file = ctx.find_node_at_offset::<ast::SourceFile>()?;
let module = ctx.sema.file_to_module_def(ctx.vfs_file_id())?;
// Enable this assist if the user select all "meaningful" content in the source file
diff --git a/crates/ide-assists/src/handlers/normalize_import.rs b/crates/ide-assists/src/handlers/normalize_import.rs
index 36da1d1788..f97a3e583f 100644
--- a/crates/ide-assists/src/handlers/normalize_import.rs
+++ b/crates/ide-assists/src/handlers/normalize_import.rs
@@ -17,7 +17,7 @@ use crate::{
// ```
// use std::{fmt::Formatter, io};
// ```
-pub(crate) fn normalize_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn normalize_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let use_item = if ctx.has_empty_selection() {
ctx.find_node_at_offset()?
} else {
diff --git a/crates/ide-assists/src/handlers/number_representation.rs b/crates/ide-assists/src/handlers/number_representation.rs
index fac81aefe0..a50f443d5d 100644
--- a/crates/ide-assists/src/handlers/number_representation.rs
+++ b/crates/ide-assists/src/handlers/number_representation.rs
@@ -15,7 +15,10 @@ const MIN_NUMBER_OF_DIGITS_TO_FORMAT: usize = 5;
// ```
// const _: i32 = 1_012_345;
// ```
-pub(crate) fn reformat_number_literal(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn reformat_number_literal(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let literal = ctx.find_node_at_offset::<ast::Literal>()?;
let literal = match literal.kind() {
ast::LiteralKind::IntNumber(it) => it,
diff --git a/crates/ide-assists/src/handlers/promote_local_to_const.rs b/crates/ide-assists/src/handlers/promote_local_to_const.rs
index ed61d32eb6..23c88e65d6 100644
--- a/crates/ide-assists/src/handlers/promote_local_to_const.rs
+++ b/crates/ide-assists/src/handlers/promote_local_to_const.rs
@@ -39,7 +39,7 @@ use crate::{
// }
// }
// ```
-pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let pat = ctx.find_node_at_offset::<ast::IdentPat>()?;
let name = pat.name()?;
if !pat.is_simple_ident() {
diff --git a/crates/ide-assists/src/handlers/pull_assignment_up.rs b/crates/ide-assists/src/handlers/pull_assignment_up.rs
index 082052c9d4..4024d51ffc 100644
--- a/crates/ide-assists/src/handlers/pull_assignment_up.rs
+++ b/crates/ide-assists/src/handlers/pull_assignment_up.rs
@@ -33,7 +33,7 @@ use crate::{
// };
// }
// ```
-pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
let op_kind = assign_expr.op_kind()?;
@@ -115,13 +115,13 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) ->
)
}
-struct AssignmentsCollector<'a> {
- sema: &'a hir::Semantics<'a, ide_db::RootDatabase>,
+struct AssignmentsCollector<'a, 'db> {
+ sema: &'a hir::Semantics<'db, ide_db::RootDatabase>,
common_lhs: ast::Expr,
assignments: Vec<(ast::BinExpr, ast::Expr)>,
}
-impl AssignmentsCollector<'_> {
+impl AssignmentsCollector<'_, '_> {
fn collect_match(&mut self, match_expr: &ast::MatchExpr) -> Option<()> {
for arm in match_expr.match_arm_list()?.arms() {
match arm.expr()? {
diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs
index d7885d5065..edf0a855c4 100644
--- a/crates/ide-assists/src/handlers/qualify_method_call.rs
+++ b/crates/ide-assists/src/handlers/qualify_method_call.rs
@@ -32,7 +32,7 @@ use crate::{
// Foo::foo(&foo);
// }
// ```
-pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let name: ast::NameRef = ctx.find_node_at_offset()?;
let call = name.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
diff --git a/crates/ide-assists/src/handlers/qualify_path.rs b/crates/ide-assists/src/handlers/qualify_path.rs
index e3dd77360c..cb48554083 100644
--- a/crates/ide-assists/src/handlers/qualify_path.rs
+++ b/crates/ide-assists/src/handlers/qualify_path.rs
@@ -4,7 +4,7 @@ use std::iter;
use hir::AsAssocItem;
use ide_db::RootDatabase;
use ide_db::{
- helpers::mod_path_to_ast,
+ helpers::mod_path_to_ast_with_factory,
imports::import_assets::{ImportCandidate, LocatedImport},
};
use syntax::Edition;
@@ -34,7 +34,7 @@ use crate::{
// }
// # pub mod std { pub mod collections { pub struct HashMap { } } }
// ```
-pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let (import_assets, syntax_under_caret, expected) = find_importable_node(ctx)?;
let cfg = ctx.config.import_path_config();
@@ -129,7 +129,7 @@ impl QualifyCandidate<'_> {
item: hir::ItemInNs,
edition: Edition,
) {
- let import = mod_path_to_ast(import, edition);
+ let import = mod_path_to_ast_with_factory(editor.make(), import, edition);
match self {
QualifyCandidate::QualifierStart(segment, generics) => {
let generics = generics.as_ref().map_or_else(String::new, ToString::to_string);
diff --git a/crates/ide-assists/src/handlers/raw_string.rs b/crates/ide-assists/src/handlers/raw_string.rs
index 8234a0374e..0a74515739 100644
--- a/crates/ide-assists/src/handlers/raw_string.rs
+++ b/crates/ide-assists/src/handlers/raw_string.rs
@@ -24,7 +24,7 @@ use crate::{
// r#"Hello, World!"#;
// }
// ```
-pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let token = ctx.find_token_at_offset::<ast::AnyString>()?;
if token.is_raw() {
return None;
@@ -60,7 +60,7 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
// "Hello, \"World!\"";
// }
// ```
-pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let token = ctx.find_token_at_offset::<ast::AnyString>()?;
if !token.is_raw() {
return None;
@@ -97,7 +97,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
// r##"Hello, World!"##;
// }
// ```
-pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let token = ctx.find_token_at_offset::<ast::AnyString>()?;
if !token.is_raw() {
return None;
@@ -128,7 +128,7 @@ pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()>
// r"Hello, World!";
// }
// ```
-pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let token = ctx.find_token_at_offset::<ast::AnyString>()?;
if !token.is_raw() {
return None;
@@ -160,7 +160,7 @@ fn replace_literal(
token: &impl AstToken,
new: &str,
builder: &mut SourceChangeBuilder,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) {
let old_token = token.syntax();
let parent = old_token.parent().expect("no parent token");
diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs
index 778533be5a..91977c2b7f 100644
--- a/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists};
// let x = 42 * (4 + 2);
// }
// ```
-pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone());
let make = editor.make();
let macro_calls = if ctx.has_empty_selection() {
diff --git a/crates/ide-assists/src/handlers/remove_else_branches.rs b/crates/ide-assists/src/handlers/remove_else_branches.rs
index 0c03856417..a40d691e4c 100644
--- a/crates/ide-assists/src/handlers/remove_else_branches.rs
+++ b/crates/ide-assists/src/handlers/remove_else_branches.rs
@@ -35,7 +35,7 @@ use crate::{AssistContext, AssistId, Assists};
// let _x = 2;
// }
// ```
-pub(crate) fn remove_else_branches(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn remove_else_branches(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let else_token = ctx.find_token_syntax_at_offset(T![else])?;
let else_branches = ctx
.find_node_at_range::<ast::IfExpr>()
diff --git a/crates/ide-assists/src/handlers/remove_mut.rs b/crates/ide-assists/src/handlers/remove_mut.rs
index 2a6024339f..db379809c5 100644
--- a/crates/ide-assists/src/handlers/remove_mut.rs
+++ b/crates/ide-assists/src/handlers/remove_mut.rs
@@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists};
// fn feed(&self, amount: u32) {}
// }
// ```
-pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let mut_token = ctx.find_token_syntax_at_offset(T![mut])?;
let target = mut_token.text_range();
diff --git a/crates/ide-assists/src/handlers/remove_parentheses.rs b/crates/ide-assists/src/handlers/remove_parentheses.rs
index af249c97b9..d6606d181e 100644
--- a/crates/ide-assists/src/handlers/remove_parentheses.rs
+++ b/crates/ide-assists/src/handlers/remove_parentheses.rs
@@ -17,7 +17,7 @@ use crate::{AssistContext, AssistId, Assists};
// _ = 2 + 2;
// }
// ```
-pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let parens = ctx.find_node_at_offset::<ast::ParenExpr>()?;
let cursor_in_range =
diff --git a/crates/ide-assists/src/handlers/remove_underscore.rs b/crates/ide-assists/src/handlers/remove_underscore.rs
index 1de1c15cf7..efe5c945be 100644
--- a/crates/ide-assists/src/handlers/remove_underscore.rs
+++ b/crates/ide-assists/src/handlers/remove_underscore.rs
@@ -24,7 +24,7 @@ use crate::{AssistContext, Assists};
// foo = 2;
// }
// ```
-pub(crate) fn remove_underscore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn remove_underscore(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let (text, text_range, def) = if let Some(name_ref) = ctx.find_node_at_offset::<ast::Name>() {
let text = name_ref.text();
if !text.starts_with('_') {
diff --git a/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs
index c38bdfdccf..2958acc478 100644
--- a/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -33,7 +33,7 @@ use crate::{AssistContext, AssistId, Assists};
// mod foo {
// }
// ```
-pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
// First, grab the uses that intersect with the current selection.
let selected_el = match ctx.covering_element() {
syntax::NodeOrToken::Node(n) => n,
@@ -111,19 +111,24 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
is_path_per_ns_unused_in_scope(ctx, &u, scope, &res).then_some(u)
}
})
- .peekable();
+ .collect::<Vec<_>>();
- // Peek so we terminate early if an unused use is found. Only do the rest of the work if the user selects the assist.
- if unused.peek().is_some() {
+ // Terminate early unless an unused use is found. Only do the rest of the work if the user selects the assist.
+ if !unused.is_empty() {
acc.add(
AssistId::quick_fix("remove_unused_imports"),
"Remove all unused imports",
selected_el.text_range(),
|builder| {
- let unused: Vec<ast::UseTree> = unused.map(|x| builder.make_mut(x)).collect();
- for node in unused {
- node.remove_recursive();
+ let editor = builder.make_editor(&selected_el);
+ unused.sort_by_key(|use_tree| use_tree.syntax().text_range().start());
+ for node in &unused {
+ editor.delete(node.syntax());
}
+ for node in unused.iter().cloned() {
+ node.remove_recursive(&editor);
+ }
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
} else {
@@ -132,7 +137,7 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
}
fn is_path_per_ns_unused_in_scope(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
u: &ast::UseTree,
scope: &mut Vec<SearchScope>,
path: &PathResolutionPerNs,
@@ -151,7 +156,7 @@ fn is_path_per_ns_unused_in_scope(
}
fn is_path_unused_in_scope(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
u: &ast::UseTree,
scope: &mut Vec<SearchScope>,
path: &[Option<PathResolution>],
@@ -167,7 +172,7 @@ fn is_path_unused_in_scope(
}
fn is_trait_unused_in_scope(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
u: &ast::UseTree,
scope: &mut Vec<SearchScope>,
t: &hir::Trait,
@@ -178,7 +183,7 @@ fn is_trait_unused_in_scope(
}
fn used_once_in_scope(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
def: Definition,
rename: Option<Rename>,
scopes: &Vec<SearchScope>,
diff --git a/crates/ide-assists/src/handlers/remove_unused_param.rs b/crates/ide-assists/src/handlers/remove_unused_param.rs
index b91d678c93..d7b0b5ea01 100644
--- a/crates/ide-assists/src/handlers/remove_unused_param.rs
+++ b/crates/ide-assists/src/handlers/remove_unused_param.rs
@@ -31,7 +31,7 @@ use crate::{
// frobnicate();
// }
// ```
-pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let param: ast::Param = ctx.find_node_at_offset()?;
let ident_pat = match param.pat()? {
ast::Pat::IdentPat(it) => it,
@@ -94,7 +94,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) ->
}
fn process_usages(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
builder: &mut SourceChangeBuilder,
editioned_file_id: EditionedFileId,
references: Vec<FileReference>,
diff --git a/crates/ide-assists/src/handlers/reorder_fields.rs b/crates/ide-assists/src/handlers/reorder_fields.rs
index facbab8019..9c9224b997 100644
--- a/crates/ide-assists/src/handlers/reorder_fields.rs
+++ b/crates/ide-assists/src/handlers/reorder_fields.rs
@@ -19,7 +19,7 @@ use crate::{AssistContext, AssistId, Assists};
// struct Foo {foo: i32, bar: i32};
// const test: Foo = Foo {foo: 1, bar: 0}
// ```
-pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let path = ctx.find_node_at_offset::<ast::Path>()?;
let record =
path.syntax().parent().and_then(<Either<ast::RecordExpr, ast::RecordPat>>::cast)?;
@@ -97,7 +97,7 @@ fn replace<T: AstNode + PartialEq>(
fn compute_fields_ranks(
path: &ast::Path,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<FxHashMap<String, usize>> {
let strukt = match ctx.sema.resolve_path(path) {
Some(hir::PathResolution::Def(hir::ModuleDef::Adt(hir::Adt::Struct(it)))) => it,
diff --git a/crates/ide-assists/src/handlers/reorder_impl_items.rs b/crates/ide-assists/src/handlers/reorder_impl_items.rs
index df5281895a..658947abe1 100644
--- a/crates/ide-assists/src/handlers/reorder_impl_items.rs
+++ b/crates/ide-assists/src/handlers/reorder_impl_items.rs
@@ -42,7 +42,7 @@ use crate::{AssistContext, AssistId, Assists};
// fn c() {}
// }
// ```
-pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+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()?;
@@ -113,7 +113,7 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) ->
fn compute_item_ranks(
path: &ast::Path,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<FxHashMap<String, usize>> {
let td = trait_definition(path, &ctx.sema)?;
diff --git a/crates/ide-assists/src/handlers/replace_arith_op.rs b/crates/ide-assists/src/handlers/replace_arith_op.rs
index 5ad5efac05..9fadf1333f 100644
--- a/crates/ide-assists/src/handlers/replace_arith_op.rs
+++ b/crates/ide-assists/src/handlers/replace_arith_op.rs
@@ -4,7 +4,10 @@ use syntax::{
ast::{self, ArithOp, BinaryOp},
};
-use crate::assist_context::{AssistContext, Assists};
+use crate::{
+ assist_context::{AssistContext, Assists},
+ utils::wrap_paren,
+};
// Assist: replace_arith_with_checked
//
@@ -21,7 +24,10 @@ use crate::assist_context::{AssistContext, Assists};
// let x = 1.checked_add(2);
// }
// ```
-pub(crate) fn replace_arith_with_checked(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn replace_arith_with_checked(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
replace_arith(acc, ctx, ArithKind::Checked)
}
@@ -42,7 +48,7 @@ pub(crate) fn replace_arith_with_checked(acc: &mut Assists, ctx: &AssistContext<
// ```
pub(crate) fn replace_arith_with_saturating(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
replace_arith(acc, ctx, ArithKind::Saturating)
}
@@ -64,13 +70,13 @@ pub(crate) fn replace_arith_with_saturating(
// ```
pub(crate) fn replace_arith_with_wrapping(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
replace_arith(acc, ctx, ArithKind::Wrapping)
}
-fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> Option<()> {
- let (lhs, op, rhs) = parse_binary_op(ctx)?;
+fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_, '_>, kind: ArithKind) -> Option<()> {
+ let (lhs, op, is_assign, rhs) = parse_binary_op(ctx)?;
let op_expr = lhs.syntax().parent()?;
if !is_primitive_int(ctx, &lhs) || !is_primitive_int(ctx, &rhs) {
@@ -87,18 +93,20 @@ fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) ->
let make = editor.make();
let method_name = kind.method_name(op);
- let needs_parentheses =
- lhs.precedence().needs_parentheses_in(ast::prec::ExprPrecedence::Postfix);
- let receiver = if needs_parentheses { make.expr_paren(lhs).into() } else { lhs };
- let arith_expr =
- make.expr_method_call(receiver, make.name_ref(&method_name), make.arg_list([rhs]));
+ let receiver = wrap_paren(lhs.clone(), make, ast::prec::ExprPrecedence::Postfix);
+ let mut arith_expr = make
+ .expr_method_call(receiver, make.name_ref(&method_name), make.arg_list([rhs]))
+ .into();
+ if is_assign {
+ arith_expr = make.expr_assignment(lhs, arith_expr).into();
+ }
editor.replace(op_expr, arith_expr.syntax());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
-fn is_primitive_int(ctx: &AssistContext<'_>, expr: &ast::Expr) -> bool {
+fn is_primitive_int(ctx: &AssistContext<'_, '_>, expr: &ast::Expr) -> bool {
match ctx.sema.type_of_expr(expr) {
Some(ty) => ty.adjusted().is_int_or_uint(),
_ => false,
@@ -106,24 +114,25 @@ fn is_primitive_int(ctx: &AssistContext<'_>, expr: &ast::Expr) -> bool {
}
/// Extract the operands of an arithmetic expression (e.g. `1 + 2` or `1.checked_add(2)`)
-fn parse_binary_op(ctx: &AssistContext<'_>) -> Option<(ast::Expr, ArithOp, ast::Expr)> {
+fn parse_binary_op(ctx: &AssistContext<'_, '_>) -> Option<(ast::Expr, ArithOp, bool, ast::Expr)> {
if !ctx.has_empty_selection() {
return None;
}
let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
- let op = match expr.op_kind() {
- Some(BinaryOp::ArithOp(ArithOp::Add)) => ArithOp::Add,
- Some(BinaryOp::ArithOp(ArithOp::Sub)) => ArithOp::Sub,
- Some(BinaryOp::ArithOp(ArithOp::Mul)) => ArithOp::Mul,
- Some(BinaryOp::ArithOp(ArithOp::Div)) => ArithOp::Div,
+ let (op, is_assign) = match expr.op_kind()? {
+ BinaryOp::ArithOp(arith_op) => (arith_op, false),
+ BinaryOp::Assignment { op: Some(op) } => (op, true),
_ => return None,
};
+ if !matches!(op, ArithOp::Add | ArithOp::Sub | ArithOp::Mul | ArithOp::Div) {
+ return None;
+ }
let lhs = expr.lhs()?;
let rhs = expr.rhs()?;
- Some((lhs, op, rhs))
+ Some((lhs, op, is_assign, rhs))
}
pub(crate) enum ArithKind {
@@ -250,6 +259,25 @@ fn main() {
}
#[test]
+ fn replace_arith_with_wrapping_add_assign() {
+ check_assist(
+ replace_arith_with_wrapping,
+ r#"
+fn main() {
+ let mut x = 1;
+ x $0+= 2;
+}
+"#,
+ r#"
+fn main() {
+ let mut x = 1;
+ x = x.wrapping_add(2);
+}
+"#,
+ )
+ }
+
+ #[test]
fn replace_arith_not_applicable_with_non_empty_selection() {
check_assist_not_applicable(
replace_arith_with_checked,
diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 751cd42f6e..4e85b30b58 100644
--- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -1,10 +1,13 @@
use hir::{InFile, ModuleDef};
-use ide_db::{helpers::mod_path_to_ast, imports::import_assets::NameToImport, items_locator};
+use ide_db::{
+ helpers::mod_path_to_ast_with_factory, imports::import_assets::NameToImport, items_locator,
+};
use itertools::Itertools;
use syntax::{
+ Edition,
SyntaxKind::WHITESPACE,
T,
- ast::{self, AstNode, HasName},
+ ast::{self, AstNode, HasName, syntax_factory::SyntaxFactory},
syntax_editor::{Position, SyntaxEditor},
};
@@ -41,7 +44,7 @@ use crate::{
// ```
pub(crate) fn replace_derive_with_manual_impl(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let attr = ctx.find_node_at_offset_with_descend::<ast::Attr>()?;
let path = attr.path()?;
@@ -87,13 +90,12 @@ pub(crate) fn replace_derive_with_manual_impl(
.flat_map(|trait_| {
current_module
.find_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), cfg)
- .as_ref()
- .map(|path| mod_path_to_ast(path, current_edition))
- .zip(Some(trait_))
+ .map(|path| (path, trait_))
});
- let mut no_traits_found = true;
- for (replace_trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
+ let found_traits = found_traits.collect::<Vec<_>>();
+ let no_traits_found = found_traits.is_empty();
+ for (replace_trait_mod_path, trait_) in found_traits {
add_assist(
acc,
ctx,
@@ -101,35 +103,58 @@ pub(crate) fn replace_derive_with_manual_impl(
&current_derives,
&args,
&path,
- &replace_trait_path,
+ Some(replace_trait_mod_path),
Some(trait_),
&adt,
+ current_edition,
)?;
}
if no_traits_found {
- add_assist(acc, ctx, &attr, &current_derives, &args, &path, &path, None, &adt)?;
+ add_assist(
+ acc,
+ ctx,
+ &attr,
+ &current_derives,
+ &args,
+ &path,
+ None,
+ None,
+ &adt,
+ current_edition,
+ )?;
}
Some(())
}
fn add_assist(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
attr: &ast::Attr,
old_derives: &[ast::Path],
old_tree: &ast::TokenTree,
old_trait_path: &ast::Path,
- replace_trait_path: &ast::Path,
+ replace_trait_mod_path: Option<hir::ModPath>,
trait_: Option<hir::Trait>,
adt: &ast::Adt,
+ current_edition: Edition,
) -> Option<()> {
let target = attr.syntax().text_range();
let annotated_name = adt.name()?;
- let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`");
+ let label_trait_path = match replace_trait_mod_path.as_ref() {
+ Some(path) => {
+ mod_path_to_ast_with_factory(&SyntaxFactory::without_mappings(), path, current_edition)
+ }
+ None => old_trait_path.clone(),
+ };
+ let label = format!("Convert to manual `impl {label_trait_path} for {annotated_name}`");
acc.add(AssistId::refactor("replace_derive_with_manual_impl"), label, target, |builder| {
let editor = builder.make_editor(attr.syntax());
let make = editor.make();
+ let replace_trait_path = match replace_trait_mod_path.as_ref() {
+ Some(path) => mod_path_to_ast_with_factory(make, path, current_edition),
+ None => old_trait_path.clone(),
+ };
let insert_after = Position::after(adt.syntax());
let impl_is_unsafe = trait_.map(|s| s.is_unsafe(ctx.db())).unwrap_or(false);
let impl_def = impl_def_from_trait(
@@ -139,7 +164,7 @@ fn add_assist(
adt,
&annotated_name,
trait_,
- replace_trait_path,
+ &replace_trait_path,
impl_is_unsafe,
);
update_attribute(&editor, old_derives, old_tree, old_trait_path, attr);
diff --git a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
index 0badad7d0c..aa3f917f12 100644
--- a/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ide-assists/src/handlers/replace_if_let_with_match.rs
@@ -1,3 +1,5 @@
+use hir::db::ExpandDatabase;
+use itertools::Itertools;
use std::iter::successors;
use ide_db::{RootDatabase, defs::NameClass, ty_filter::TryEnum};
@@ -45,7 +47,10 @@ use crate::{
// }
// }
// ```
-pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn replace_if_let_with_match(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
let available_range = TextRange::new(
if_expr.syntax().text_range().start(),
@@ -66,8 +71,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
}
});
let scrutinee_to_be_expr = if_expr.condition()?;
- let scrutinee_to_be_expr = match let_and_guard(&scrutinee_to_be_expr) {
- (Some(let_expr), _) => let_expr.expr()?,
+ let scrutinee_to_be_expr = match let_and_guard(&scrutinee_to_be_expr, ctx)? {
+ (Some((_, expr)), _) => expr,
(None, cond) => cond?,
};
@@ -75,11 +80,9 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
let mut cond_bodies = Vec::new();
for if_expr in if_exprs {
let cond = if_expr.condition()?;
- let (cond, guard) = match let_and_guard(&cond) {
+ let (cond, guard) = match let_and_guard(&cond, ctx)? {
(None, guard) => (None, Some(guard?)),
- (Some(let_), guard) => {
- let pat = let_.pat()?;
- let expr = let_.expr()?;
+ (Some((pat, expr)), guard) => {
if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() {
// Only if all condition expressions are equal we can merge them into a match
return None;
@@ -120,6 +123,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
// Dedent from original position, then indent for match arm
let body = body.dedent(indent);
let body = unwrap_trivial_block(body);
+ let pat = pretty_pat_inside_macro(pat, &ctx.sema);
match (pat, guard.map(|it| make.match_guard(it))) {
(Some(pat), guard) => make.match_arm(pat, guard, body),
(None, _) if !pat_seen => {
@@ -159,7 +163,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
}
fn make_else_arm(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
make: &SyntaxFactory,
else_expr: Option<ast::Expr>,
conditionals: &[(Option<ast::Pat>, Option<ast::Expr>, ast::BlockExpr)],
@@ -222,7 +226,10 @@ fn make_else_arm(
// }
// }
// ```
-pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn replace_match_with_if_let(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let match_expr: ast::MatchExpr = ctx.find_node_at_offset()?;
let match_arm_list = match_expr.match_arm_list()?;
let available_range = TextRange::new(
@@ -394,13 +401,18 @@ fn is_sad_pat(sema: &hir::Semantics<'_, RootDatabase>, pat: &ast::Pat) -> bool {
.is_some_and(|it| does_pat_match_variant(pat, &it.sad_pattern()))
}
-fn let_and_guard(cond: &ast::Expr) -> (Option<ast::LetExpr>, Option<ast::Expr>) {
+fn let_and_guard(
+ cond: &ast::Expr,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<(Option<(ast::Pat, ast::Expr)>, Option<ast::Expr>)> {
if let ast::Expr::ParenExpr(expr) = cond
&& let Some(sub_expr) = expr.expr()
{
- let_and_guard(&sub_expr)
+ let_and_guard(&sub_expr, ctx)?
} else if let ast::Expr::LetExpr(let_expr) = cond {
- (Some(let_expr.clone()), None)
+ (Some((let_expr.pat()?, let_expr.expr()?)), None)
+ } else if let Some((pat, expr, guard)) = parse_matches_macro(cond, ctx) {
+ (Some((pat, expr)), guard)
} else if let ast::Expr::BinExpr(bin_expr) = cond
&& let Some(ast::Expr::LetExpr(let_expr)) = and_bin_expr_left(bin_expr).lhs()
{
@@ -418,10 +430,11 @@ fn let_and_guard(cond: &ast::Expr) -> (Option<ast::LetExpr>, Option<ast::Expr>)
}
let new_expr = editor.finish().new_root().clone();
- (Some(let_expr), ast::Expr::cast(new_expr))
+ (Some((let_expr.pat()?, let_expr.expr()?)), ast::Expr::cast(new_expr))
} else {
(None, Some(cond.clone()))
}
+ .into()
}
fn match_scrutinee_needs_paren(expr: &ast::Expr) -> bool {
@@ -445,6 +458,66 @@ fn and_bin_expr_left(expr: &ast::BinExpr) -> ast::BinExpr {
}
}
+fn parse_matches_macro(
+ expr: &ast::Expr,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<(ast::Pat, ast::Expr, Option<ast::Expr>)> {
+ let ast::Expr::MacroExpr(macro_expr) = expr else { return None };
+ let macro_call = macro_expr.macro_call()?;
+ let tt = macro_call.token_tree()?;
+ let r_delim = syntax::NodeOrToken::Token(tt.right_delimiter_token()?);
+
+ if macro_call.path()?.segment()?.name_ref()?.text() != "matches" {
+ return None;
+ }
+
+ let parse = |src: String| syntax::hacks::parse_expr_from_str(&src, ctx.edition());
+ let input = tt.syntax().children_with_tokens().skip(1).take_while(|it| *it != r_delim);
+ // Only supports single top-level comma case
+ let [comma] = input.clone().filter(|it| it.kind() == T![,]).collect_array()?;
+ let input_rest = input.clone().skip_while(|it| *it != comma).skip(1);
+ let if_kwd = input_rest.clone().find(|it| it.kind() == T![if]);
+ let input_expr = input.clone().take_while(|it| *it != comma).join("");
+ let input_pat = input_rest.clone().take_while(|it| Some(it) != if_kwd.as_ref());
+ let input_guard =
+ if_kwd.as_ref().map(|if_kwd| { input_rest }.skip_while(|it| it != if_kwd).skip(1).join(""));
+
+ let pat_token = match { input_pat }.find(|it| !it.kind().is_trivia())? {
+ syntax::NodeOrToken::Node(node) => node.first_token()?,
+ syntax::NodeOrToken::Token(t) => t,
+ };
+ // XXX: Use descend pat for sema analysis
+ let descend_pat = ctx.sema.descend_into_macros(pat_token).into_iter().find_map(|token| {
+ token
+ .parent_ancestors()
+ .take_while(|it| !matches!(it.kind(), SyntaxKind::MATCH_ARM | SyntaxKind::ITEM_LIST))
+ .filter_map(ast::Pat::cast)
+ .last()
+ })?;
+
+ Some((descend_pat, parse(input_expr)?, input_guard.and_then(parse)))
+}
+
+fn pretty_pat_inside_macro(
+ pat: Option<ast::Pat>,
+ sema: &hir::Semantics<'_, RootDatabase>,
+) -> Option<ast::Pat> {
+ let pretty = |pat| {
+ let db = sema.db;
+ let scope = sema.scope(&pat)?;
+ let file_id = scope.file_id().macro_file()?;
+ // Don't call `prettify_macro_expansion()` outside the actual assist action; see inline_macro assist
+ let pretty_node = hir::prettify_macro_expansion(
+ db,
+ pat,
+ db.expansion_span_map(file_id),
+ scope.module().krate(db).into(),
+ );
+ ast::Pat::cast(pretty_node)
+ };
+ pat.map(|pat| pretty(pat.syntax().clone()).unwrap_or(pat))
+}
+
#[cfg(test)]
mod tests {
use super::*;
@@ -1816,6 +1889,53 @@ fn foo(x: Result<i32, ()>) {
}
#[test]
+ fn test_if_matches_with_match() {
+ check_assist(
+ replace_if_let_with_match,
+ r#"
+//- minicore: result, matches
+fn foo(x: Result<i32, ()>) {
+ $0if matches!(x, Ok(a @ 1..2)) {
+ ()
+ } else {
+ ()
+ }
+}
+"#,
+ r#"
+fn foo(x: Result<i32, ()>) {
+ match x {
+ Ok([email protected]) => (),
+ _ => (),
+ }
+}
+"#,
+ );
+
+ check_assist(
+ replace_if_let_with_match,
+ r#"
+//- minicore: result, matches
+fn foo(x: Result<i32, ()>) {
+ $0if matches!(x, Ok(ref a)) {
+ ()
+ } else {
+ ()
+ }
+}
+"#,
+ r#"
+fn foo(x: Result<i32, ()>) {
+ match x {
+ Ok(ref a) => (),
+ Err(_) => (),
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn test_replace_match_with_if_let_unwraps_simple_expressions() {
check_assist(
replace_match_with_if_let,
diff --git a/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
index 802d5f72b9..f50614a66f 100644
--- a/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
+++ b/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
@@ -29,7 +29,7 @@ use crate::{
// ```
pub(crate) fn replace_is_method_with_if_let_method(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let has_cond = ctx.find_node_at_offset::<Either<ast::IfExpr, ast::WhileExpr>>()?;
diff --git a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
index 85e72130e0..29688d9b97 100644
--- a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
@@ -34,7 +34,10 @@ use crate::{AssistContext, AssistId, Assists};
//
// fn compute() -> Option<i32> { None }
// ```
-pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn replace_let_with_if_let(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let let_kw = ctx.find_token_syntax_at_offset(T![let])?;
let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?;
let init = let_stmt.initializer()?;
diff --git a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
index 6e4dd8cb73..7aa9a82109 100644
--- a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
+++ b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
@@ -25,7 +25,10 @@ use crate::{AssistContext, Assists, utils::wrap_paren_in_call};
// a.unwrap_or_else(|| 2);
// }
// ```
-pub(crate) fn replace_with_lazy_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn replace_with_lazy_method(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
let scope = ctx.sema.scope(call.syntax())?;
@@ -113,7 +116,10 @@ fn into_closure(param: &Expr, name_lazy: &str) -> Expr {
// a.unwrap_or(2);
// }
// ```
-pub(crate) fn replace_with_eager_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn replace_with_eager_method(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
let scope = ctx.sema.scope(call.syntax())?;
diff --git a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
index 17ef7727ec..979f832978 100644
--- a/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
+++ b/crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs
@@ -26,7 +26,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
pub(crate) fn replace_named_generic_with_impl(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
// finds `<P: AsRef<Path>>`
let type_param = ctx.find_node_at_offset::<ast::TypeParam>()?;
@@ -34,7 +34,7 @@ pub(crate) fn replace_named_generic_with_impl(
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 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();
@@ -89,6 +89,8 @@ pub(crate) fn replace_named_generic_with_impl(
}
}
+ let type_bound_list = type_bound_list
+ .unwrap_or_else(|| make.type_bound_list([make.type_bound_text("Sized")]).unwrap());
let new_bounds = make.impl_trait_type(type_bound_list);
for path_type in path_types_to_replace.iter().rev() {
editor.replace(path_type.syntax(), new_bounds.syntax());
@@ -313,6 +315,15 @@ mod tests {
}
#[test]
+ fn replace_generic_without_bounds() {
+ check_assist(
+ replace_named_generic_with_impl,
+ r#"fn foo<T$0>(input: T) {}"#,
+ r#"fn foo(input: impl Sized) {}"#,
+ );
+ }
+
+ #[test]
fn replace_generic_with_multiple_trait_bounds() {
check_assist(
replace_named_generic_with_impl,
diff --git a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs
index eebe93f005..0bd1ec12d0 100644
--- a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs
@@ -1,11 +1,11 @@
use hir::{AsAssocItem, ModuleDef, PathResolution};
use ide_db::{
- helpers::mod_path_to_ast,
- imports::insert_use::{ImportScope, insert_use},
+ helpers::mod_path_to_ast_with_factory,
+ imports::insert_use::{ImportScope, insert_use_with_editor},
};
use syntax::{
AstNode, Edition, SyntaxNode,
- ast::{self, HasGenericArgs, make},
+ ast::{self, HasGenericArgs},
match_ast,
syntax_editor::SyntaxEditor,
};
@@ -29,7 +29,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
pub(crate) fn replace_qualified_name_with_use(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let original_path: ast::Path = ctx.find_node_at_offset()?;
// We don't want to mess with use statements
@@ -75,7 +75,7 @@ pub(crate) fn replace_qualified_name_with_use(
let scope_node = scope.as_syntax_node();
let editor = builder.make_editor(scope_node);
shorten_paths(&editor, scope_node, &original_path);
- builder.add_file_edits(ctx.vfs_file_id(), editor);
+ let make = editor.make();
let path = drop_generic_args(&original_path);
let edition = ctx
.sema
@@ -83,18 +83,19 @@ pub(crate) fn replace_qualified_name_with_use(
.map(|semantics_scope| semantics_scope.krate().edition(ctx.db()))
.unwrap_or(Edition::CURRENT);
// stick the found import in front of the to be replaced path
- let path =
- match path_to_qualifier.and_then(|it| mod_path_to_ast(&it, edition).qualifier()) {
- Some(qualifier) => make::path_concat(qualifier, path),
- None => path,
- };
- let scope = builder.make_import_scope_mut(scope);
- insert_use(&scope, path, &ctx.config.insert_use);
+ let path = match path_to_qualifier
+ .and_then(|it| mod_path_to_ast_with_factory(make, &it, edition).qualifier())
+ {
+ Some(qualifier) => make.path_concat(qualifier, path),
+ None => path,
+ };
+ insert_use_with_editor(&scope, path, &ctx.config.insert_use, &editor);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
-fn target_path(ctx: &AssistContext<'_>, mut original_path: ast::Path) -> Option<ast::Path> {
+fn target_path(ctx: &AssistContext<'_, '_>, mut original_path: ast::Path) -> Option<ast::Path> {
let on_first = original_path.qualifier().is_none();
if on_first {
diff --git a/crates/ide-assists/src/handlers/replace_string_with_char.rs b/crates/ide-assists/src/handlers/replace_string_with_char.rs
index fb5b234d55..df22c472a4 100644
--- a/crates/ide-assists/src/handlers/replace_string_with_char.rs
+++ b/crates/ide-assists/src/handlers/replace_string_with_char.rs
@@ -22,7 +22,10 @@ use crate::{AssistContext, AssistId, Assists, utils::string_suffix};
// find('{');
// }
// ```
-pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn replace_string_with_char(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let token = ctx.find_token_syntax_at_offset(STRING).and_then(ast::String::cast)?;
let value = token.value().ok()?;
let target = token.syntax().text_range();
@@ -64,7 +67,10 @@ pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext<'_
// find("{");
// }
// ```
-pub(crate) fn replace_char_with_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn replace_char_with_string(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let token = ctx.find_token_syntax_at_offset(CHAR)?;
let target = token.text_range();
diff --git a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
index a692259410..5e336523b4 100644
--- a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
+++ b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs
@@ -28,7 +28,7 @@ use crate::{
// ```
pub(crate) fn replace_turbofish_with_explicit_type(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;
diff --git a/crates/ide-assists/src/handlers/sort_items.rs b/crates/ide-assists/src/handlers/sort_items.rs
index 911fa9d14b..49b3cfb908 100644
--- a/crates/ide-assists/src/handlers/sort_items.rs
+++ b/crates/ide-assists/src/handlers/sort_items.rs
@@ -81,7 +81,7 @@ use crate::{AssistContext, AssistId, Assists, utils::get_methods};
// Cat { name: String, weight: f64 },
// }
// ```
-pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn sort_items(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
if ctx.has_empty_selection() {
cov_mark::hit!(not_applicable_if_no_selection);
return None;
@@ -150,7 +150,7 @@ fn add_sort_field_list_assist(acc: &mut Assists, field_list: Option<ast::FieldLi
fn add_sort_methods_assist(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
item_list: ast::AssocItemList,
) -> Option<()> {
let selection = ctx.selection_trimmed();
diff --git a/crates/ide-assists/src/handlers/split_import.rs b/crates/ide-assists/src/handlers/split_import.rs
index 1729a0667c..ec5ed44b56 100644
--- a/crates/ide-assists/src/handlers/split_import.rs
+++ b/crates/ide-assists/src/handlers/split_import.rs
@@ -13,7 +13,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
// use std::{collections::HashMap};
// ```
-pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let colon_colon = ctx.find_token_syntax_at_offset(T![::])?;
let path = ast::Path::cast(colon_colon.parent()?)?.qualifier()?;
@@ -30,9 +30,9 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
let target = colon_colon.text_range();
acc.add(AssistId::refactor_rewrite("split_import"), "Split import", target, |edit| {
- let use_tree = edit.make_mut(use_tree.clone());
- let path = edit.make_mut(path);
- use_tree.split_prefix(&path);
+ let editor = edit.make_editor(use_tree.syntax());
+ use_tree.split_prefix_with_editor(&editor, &path);
+ edit.add_file_edits(ctx.vfs_file_id(), editor);
})
}
diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs
index 849dfa49dd..5642f10a6d 100644
--- a/crates/ide-assists/src/handlers/term_search.rs
+++ b/crates/ide-assists/src/handlers/term_search.rs
@@ -10,7 +10,7 @@ use syntax::{AstNode, ast};
use crate::assist_context::{AssistContext, Assists};
-pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?;
let syntax = unexpanded.syntax();
let goal_range = syntax.text_range();
diff --git a/crates/ide-assists/src/handlers/toggle_async_sugar.rs b/crates/ide-assists/src/handlers/toggle_async_sugar.rs
index fa6ccb9a5f..99be89ece1 100644
--- a/crates/ide-assists/src/handlers/toggle_async_sugar.rs
+++ b/crates/ide-assists/src/handlers/toggle_async_sugar.rs
@@ -27,7 +27,7 @@ use crate::{AssistContext, Assists};
// ```
pub(crate) fn sugar_impl_future_into_async(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let ret_type: ast::RetType = ctx.find_node_at_offset()?;
let function = ret_type.syntax().parent().and_then(ast::Fn::cast)?;
@@ -117,7 +117,7 @@ pub(crate) fn sugar_impl_future_into_async(
// ```
pub(crate) fn desugar_async_into_impl_future(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
) -> Option<()> {
let async_token = ctx.find_token_syntax_at_offset(SyntaxKind::ASYNC_KW)?;
let function = async_token.parent().and_then(ast::Fn::cast)?;
diff --git a/crates/ide-assists/src/handlers/toggle_ignore.rs b/crates/ide-assists/src/handlers/toggle_ignore.rs
index a088fb178d..35c304b387 100644
--- a/crates/ide-assists/src/handlers/toggle_ignore.rs
+++ b/crates/ide-assists/src/handlers/toggle_ignore.rs
@@ -23,7 +23,7 @@ use crate::{AssistContext, AssistId, Assists, utils::test_related_attribute_syn}
// assert_eq!(2 + 2, 5);
// }
// ```
-pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn toggle_ignore(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let attr: ast::Attr = ctx.find_node_at_offset()?;
let func = attr.syntax().parent().and_then(ast::Fn::cast)?;
let attr = test_related_attribute_syn(&func)?;
diff --git a/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
index 4d375080f5..e1ac9ea135 100644
--- a/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
+++ b/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
@@ -26,7 +26,7 @@ use crate::{AssistContext, Assists};
//
// sth!{ }
// ```
-pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
#[derive(Debug)]
enum MacroDelims {
LPar,
@@ -45,7 +45,8 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>)
let ltoken = token_tree.left_delimiter_token()?;
let rtoken = token_tree.right_delimiter_token()?;
- if !is_macro_call(&token_tree)? {
+ if is_macro_call(&token_tree) != Some(true) {
+ cov_mark::hit!(toggle_macro_delimiter_is_not_macro_call);
return None;
}
@@ -111,7 +112,6 @@ fn is_macro_call(token_tree: &ast::TokenTree) -> Option<bool> {
return Some(true);
}
- let token_tree = ast::TokenTree::cast(parent)?;
let prev = previous_non_trivia_token(token_tree.syntax().clone())?;
let prev_prev = previous_non_trivia_token(prev.clone())?;
Some(prev.kind() == T![!] && prev_prev.kind() == SyntaxKind::IDENT)
@@ -374,6 +374,7 @@ mod abc {
#[test]
fn test_unrelated_par() {
+ cov_mark::check!(toggle_macro_delimiter_is_not_macro_call);
check_assist_not_applicable(
toggle_macro_delimiter,
r#"
@@ -383,8 +384,7 @@ macro_rules! prt {
}};
}
-prt!(($03 + 5));
-
+prt!((3 + 5$0));
"#,
)
}
diff --git a/crates/ide-assists/src/handlers/unmerge_imports.rs b/crates/ide-assists/src/handlers/unmerge_imports.rs
index ab6317ad44..ec5c0929b4 100644
--- a/crates/ide-assists/src/handlers/unmerge_imports.rs
+++ b/crates/ide-assists/src/handlers/unmerge_imports.rs
@@ -21,7 +21,7 @@ use crate::{
// use std::fmt::{Debug};
// use std::fmt::Display;
// ```
-pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone());
let make = editor.make();
let tree = ctx.find_node_at_offset::<ast::UseTree>()?;
diff --git a/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
index 65300ccefd..555d3d2ee9 100644
--- a/crates/ide-assists/src/handlers/unmerge_match_arm.rs
+++ b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
@@ -30,7 +30,7 @@ use crate::{AssistContext, AssistId, Assists};
// }
// }
// ```
-pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let pipe_token = ctx.find_token_syntax_at_offset(T![|])?;
let or_pat = ast::OrPat::cast(pipe_token.parent()?)?;
if or_pat.leading_pipe().is_some_and(|it| it == pipe_token) {
diff --git a/crates/ide-assists/src/handlers/unnecessary_async.rs b/crates/ide-assists/src/handlers/unnecessary_async.rs
index b9385775b4..5542180362 100644
--- a/crates/ide-assists/src/handlers/unnecessary_async.rs
+++ b/crates/ide-assists/src/handlers/unnecessary_async.rs
@@ -28,7 +28,7 @@ use crate::{AssistContext, Assists};
// pub fn foo() {}
// pub async fn bar() { foo() }
// ```
-pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let function: ast::Fn = ctx.find_node_at_offset()?;
// Do nothing if the cursor isn't on the async token.
@@ -91,7 +91,7 @@ pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
}
fn find_all_references(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
def: &Definition,
) -> impl Iterator<Item = (EditionedFileId, FileReference)> {
def.usages(&ctx.sema).all().into_iter().flat_map(|(file_id, references)| {
@@ -101,7 +101,7 @@ fn find_all_references(
/// Finds the await expression for the given `NameRef`.
/// If no await expression is found, returns None.
-fn find_await_expression(ctx: &AssistContext<'_>, nameref: &NameRef) -> Option<ast::AwaitExpr> {
+fn find_await_expression(ctx: &AssistContext<'_, '_>, nameref: &NameRef) -> Option<ast::AwaitExpr> {
// From the nameref, walk up the tree to the await expression.
let await_expr = if let Some(path) = full_path_of_name_ref(nameref) {
// Function calls.
diff --git a/crates/ide-assists/src/handlers/unqualify_method_call.rs b/crates/ide-assists/src/handlers/unqualify_method_call.rs
index 045a272952..242ebc4063 100644
--- a/crates/ide-assists/src/handlers/unqualify_method_call.rs
+++ b/crates/ide-assists/src/handlers/unqualify_method_call.rs
@@ -22,7 +22,7 @@ use crate::{AssistContext, AssistId, Assists};
// }
// # mod std { pub mod ops { pub trait Add { fn add(self, _: Self) {} } impl Add for i32 {} } }
// ```
-pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let call = ctx.find_node_at_offset::<ast::CallExpr>()?;
let ast::Expr::PathExpr(path_expr) = call.expr()? else { return None };
let path = path_expr.path()?;
@@ -77,7 +77,7 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>)
fn add_import(
qualifier: ast::Path,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
editor: &syntax::syntax_editor::SyntaxEditor,
) {
if let Some(path_segment) = qualifier.segment() {
diff --git a/crates/ide-assists/src/handlers/unwrap_block.rs b/crates/ide-assists/src/handlers/unwrap_branch.rs
index 77941bcfb2..a582af4e2c 100644
--- a/crates/ide-assists/src/handlers/unwrap_block.rs
+++ b/crates/ide-assists/src/handlers/unwrap_branch.rs
@@ -1,8 +1,10 @@
+use either::Either;
use syntax::{
AstNode, SyntaxElement, SyntaxKind, SyntaxNode, T,
ast::{
self,
edit::{AstNodeEdit, IndentLevel},
+ syntax_factory::SyntaxFactory,
},
match_ast,
syntax_editor::{Element, Position, SyntaxEditor},
@@ -10,7 +12,7 @@ use syntax::{
use crate::{AssistContext, AssistId, Assists};
-// Assist: unwrap_block
+// Assist: unwrap_branch
//
// This assist removes if...else, for, while and loop control statements to just keep the body.
//
@@ -27,15 +29,16 @@ use crate::{AssistContext, AssistId, Assists};
// println!("foo");
// }
// ```
-pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
- let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?;
- let target = block.syntax().text_range();
- let mut container = block.syntax().clone();
+pub(crate) fn unwrap_branch(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
+ let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone());
+ let place = unwrap_branch_place(ctx)?;
+ let target = place.syntax().text_range();
+ let block = wrap_block_raw(&place, editor.make());
+ let mut container = place.syntax().clone();
let mut replacement = block.clone();
let mut prefer_container = None;
- let from_indent = block.indent_level();
+ let from_indent = place.indent_level();
let into_indent = loop {
let parent = container.parent()?;
container = match_ast! {
@@ -67,21 +70,84 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
_ => return None,
}
};
+ if ast::MatchArm::cast(container.parent()?).is_some() {
+ replacement = editor.make().tail_only_block_expr(replacement.into());
+ prefer_container = Some(container.clone());
+ break IndentLevel::from_node(&container);
+ }
};
+ let is_branch =
+ !block.is_standalone() || place.syntax().parent().and_then(ast::MatchArm::cast).is_some();
+ let label = if is_branch { "Unwrap branch" } else { "Unwrap block" };
let replacement = replacement.stmt_list()?;
- acc.add(AssistId::refactor_rewrite("unwrap_block"), "Unwrap block", target, |builder| {
- let editor = builder.make_editor(block.syntax());
+ acc.add(AssistId::refactor_rewrite("unwrap_branch"), label, target, |builder| {
let replacement = replacement.dedent(from_indent).indent(into_indent);
+ let mut replacement = extract_statements(replacement);
let container = prefer_container.unwrap_or(container);
- editor.replace_with_many(&container, extract_statements(replacement));
+ if ast::ExprStmt::can_cast(container.kind())
+ && block.tail_expr().is_some_and(|it| !it.is_block_like())
+ {
+ replacement.push(editor.make().token(T![;]).into());
+ }
+
+ editor.replace_with_many(&container, replacement);
delete_else_before(container, &editor);
builder.add_file_edits(ctx.vfs_file_id(), editor);
})
}
+// Assist: unwrap_block
+//
+// This assist removes braces and unwrap single expressions block.
+//
+// ```
+// fn foo() {
+// match () {
+// _ => {$0
+// bar()
+// }
+// }
+// }
+// ```
+// ->
+// ```
+// fn foo() {
+// match () {
+// _ => bar(),
+// }
+// }
+// ```
+pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
+ let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
+ let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?;
+ let target = block.syntax().text_range();
+ let tail_expr = block.tail_expr()?;
+ let stmt_list = block.stmt_list()?;
+ let container = Either::<ast::MatchArm, ast::ClosureExpr>::cast(block.syntax().parent()?)?;
+
+ if stmt_list.statements().next().is_some() {
+ return None;
+ }
+
+ acc.add(AssistId::refactor_rewrite("unwrap_block"), "Unwrap block", target, |builder| {
+ let editor = builder.make_editor(block.syntax());
+ let replacement = stmt_list.dedent(tail_expr.indent_level()).indent(block.indent_level());
+ let mut replacement = extract_statements(replacement);
+
+ if container.left().is_some_and(|it| it.comma_token().is_none())
+ && !tail_expr.is_block_like()
+ {
+ replacement.push(editor.make().token(T![,]).into());
+ }
+
+ editor.replace_with_many(block.syntax(), replacement);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
+ })
+}
+
fn delete_else_before(container: SyntaxNode, editor: &SyntaxEditor) {
let make = editor.make();
let Some(else_token) = container
@@ -121,6 +187,18 @@ fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExp
try_wrap_assign().unwrap_or(replacement)
}
+fn unwrap_branch_place(ctx: &AssistContext<'_, '_>) -> Option<ast::Expr> {
+ if let Some(l_curly_token) = ctx.find_token_syntax_at_offset(T!['{']) {
+ let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?;
+ Some(block.into())
+ } else if let Some(fat_arrow_token) = ctx.find_token_syntax_at_offset(T![=>]) {
+ let match_arm = fat_arrow_token.parent().and_then(ast::MatchArm::cast)?;
+ match_arm.expr()
+ } else {
+ None
+ }
+}
+
fn extract_statements(stmt_list: ast::StmtList) -> Vec<SyntaxElement> {
let mut elements = stmt_list
.syntax()
@@ -132,16 +210,27 @@ fn extract_statements(stmt_list: ast::StmtList) -> Vec<SyntaxElement> {
elements
}
+fn wrap_block_raw(expr: &ast::Expr, make: &SyntaxFactory) -> ast::BlockExpr {
+ if let ast::Expr::BlockExpr(block) = expr {
+ block.clone()
+ } else {
+ make.tail_only_block_expr(expr.indent(1.into()))
+ }
+}
+
#[cfg(test)]
mod tests {
- use crate::tests::{check_assist, check_assist_not_applicable};
+ use crate::tests::{
+ check_assist, check_assist_by_label, check_assist_not_applicable,
+ check_assist_not_applicable_by_label, check_assist_with_label,
+ };
use super::*;
#[test]
fn unwrap_tail_expr_block() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
$0{
@@ -160,7 +249,7 @@ fn main() {
#[test]
fn unwrap_stmt_expr_block() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
$0{
@@ -176,9 +265,8 @@ fn main() {
}
"#,
);
- // Pedantically, we should add an `;` here...
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
$0{
@@ -189,7 +277,7 @@ fn main() {
"#,
r#"
fn main() {
- 92
+ 92;
()
}
"#,
@@ -199,7 +287,7 @@ fn main() {
#[test]
fn simple_if() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
bar();
@@ -228,7 +316,7 @@ fn main() {
#[test]
fn simple_if_else() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
bar();
@@ -260,7 +348,7 @@ fn main() {
#[test]
fn simple_if_else_if() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
// bar();
@@ -294,7 +382,7 @@ fn main() {
#[test]
fn simple_if_else_if_nested() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
// bar();
@@ -330,7 +418,7 @@ fn main() {
#[test]
fn simple_if_else_if_nested_else() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
// bar();
@@ -370,7 +458,7 @@ fn main() {
#[test]
fn simple_if_else_if_nested_middle() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
// bar();
@@ -408,7 +496,7 @@ fn main() {
#[test]
fn simple_if_bad_cursor_position() {
check_assist_not_applicable(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
bar();$0
@@ -428,7 +516,7 @@ fn main() {
#[test]
fn simple_for() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
for i in 0..5 {$0
@@ -461,7 +549,7 @@ fn main() {
#[test]
fn simple_if_in_for() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
for i in 0..5 {
@@ -490,9 +578,91 @@ fn main() {
}
#[test]
+ fn simple_if_in_match_arm() {
+ check_assist(
+ unwrap_branch,
+ r#"
+fn main() {
+ match 1 {
+ 1 => if true {$0
+ foo();
+ }
+ _ => (),
+ }
+}
+"#,
+ r#"
+fn main() {
+ match 1 {
+ 1 => {
+ foo();
+ }
+ _ => (),
+ }
+}
+"#,
+ );
+
+ check_assist(
+ unwrap_branch,
+ r#"
+fn main() {
+ match 1 {
+ 1 => if true {
+ foo();
+ } else {$0
+ bar();
+ }
+ _ => (),
+ }
+}
+"#,
+ r#"
+fn main() {
+ match 1 {
+ 1 => {
+ bar();
+ }
+ _ => (),
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn simple_match_in_match_arm() {
+ check_assist(
+ unwrap_branch,
+ r#"
+fn main() {
+ match 1 {
+ 1 => match () {
+ _ => {$0
+ foo();
+ }
+ }
+ _ => (),
+ }
+}
+"#,
+ r#"
+fn main() {
+ match 1 {
+ 1 => {
+ foo();
+ }
+ _ => (),
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn simple_loop() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
loop {$0
@@ -525,7 +695,7 @@ fn main() {
#[test]
fn simple_while() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
while true {$0
@@ -558,7 +728,7 @@ fn main() {
#[test]
fn simple_let_else() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
let Some(2) = None else {$0
@@ -573,7 +743,7 @@ fn main() {
"#,
);
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
let Some(2) = None else {$0
@@ -592,7 +762,7 @@ fn main() {
#[test]
fn unwrap_match_arm() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
match rel_path {
@@ -616,7 +786,7 @@ fn main() {
#[test]
fn unwrap_match_arm_in_let() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
let value = match rel_path {
@@ -638,9 +808,33 @@ fn main() {
}
#[test]
+ fn unwrap_match_arm_without_block() {
+ check_assist(
+ unwrap_branch,
+ r#"
+fn main() {
+ match rel_path {
+ Ok(rel_path) $0=> Foo {
+ rel_path,
+ },
+ Err(_) => None,
+ }
+}
+"#,
+ r#"
+fn main() {
+ Foo {
+ rel_path,
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn simple_if_in_while_bad_cursor_position() {
check_assist_not_applicable(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
while true {
@@ -661,7 +855,7 @@ fn main() {
#[test]
fn simple_single_line() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
{$0 0 }
@@ -678,7 +872,7 @@ fn main() {
#[test]
fn simple_nested_block() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
$0{
@@ -701,7 +895,7 @@ fn main() {
#[test]
fn nested_single_line() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
{$0 { println!("foo"); } }
@@ -715,7 +909,7 @@ fn main() {
);
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
{$0 { 0 } }
@@ -732,7 +926,7 @@ fn main() {
#[test]
fn simple_if_single_line() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
if true {$0 /* foo */ foo() } else { bar() /* bar */}
@@ -749,7 +943,7 @@ fn main() {
#[test]
fn if_single_statement() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
if true {$0
@@ -768,7 +962,7 @@ fn main() {
#[test]
fn multiple_statements() {
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() -> i32 {
if 2 > 1 {$0
@@ -792,7 +986,7 @@ fn main() -> i32 {
fn unwrap_block_in_let_initializers() {
// https://github.com/rust-lang/rust-analyzer/issues/13679
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
let x = {$0
@@ -807,7 +1001,7 @@ fn main() {
"#,
);
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() -> i32 {
let _ = {$01; 2};
@@ -820,7 +1014,7 @@ fn main() -> i32 {
"#,
);
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() -> i32 {
let mut a = {$01; 2};
@@ -833,7 +1027,7 @@ fn main() -> i32 {
"#,
);
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() -> i32 {
let mut a = {$0
@@ -857,7 +1051,7 @@ fn main() -> i32 {
fn unwrap_if_in_let_initializers() {
// https://github.com/rust-lang/rust-analyzer/issues/13679
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
let a = 1;
@@ -881,7 +1075,7 @@ fn main() {
fn unwrap_block_with_modifiers() {
// https://github.com/rust-lang/rust-analyzer/issues/17964
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
unsafe $0{
@@ -896,7 +1090,7 @@ fn main() {
"#,
);
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
async move $0{
@@ -911,7 +1105,7 @@ fn main() {
"#,
);
check_assist(
- unwrap_block,
+ unwrap_branch,
r#"
fn main() {
try $0{
@@ -926,4 +1120,177 @@ fn main() {
"#,
);
}
+
+ #[test]
+ fn unwrap_block_labels() {
+ check_assist_with_label(
+ unwrap_branch,
+ r#"
+fn main() {
+ $0{
+ bar;
+ }
+}
+"#,
+ "Unwrap block",
+ );
+ check_assist_with_label(
+ unwrap_branch,
+ r#"
+fn main() {
+ let x = $0{
+ bar()
+ };
+}
+"#,
+ "Unwrap block",
+ );
+ check_assist_with_label(
+ unwrap_branch,
+ r#"
+fn main() {
+ let x = if true $0{
+ bar()
+ };
+}
+"#,
+ "Unwrap branch",
+ );
+ check_assist_with_label(
+ unwrap_branch,
+ r#"
+fn main() {
+ let x = match () {
+ () => $0{
+ bar(),
+ }
+ };
+}
+"#,
+ "Unwrap branch",
+ );
+ check_assist_with_label(
+ unwrap_branch,
+ r#"
+fn main() {
+ match () {
+ () => $0{
+ bar(),
+ }
+ }
+}
+"#,
+ "Unwrap branch",
+ );
+ }
+
+ #[test]
+ fn unwrap_block_in_branch() {
+ check_assist_by_label(
+ unwrap_block,
+ r#"
+fn main() {
+ match rel_path {
+ Ok(rel_path) => {$0
+ if true {
+ foo()
+ }
+ }
+ Err(_) => None,
+ }
+}
+"#,
+ r#"
+fn main() {
+ match rel_path {
+ Ok(rel_path) => if true {
+ foo()
+ }
+ Err(_) => None,
+ }
+}
+"#,
+ "Unwrap block",
+ );
+
+ check_assist_by_label(
+ unwrap_block,
+ r#"
+fn main() {
+ match rel_path {
+ Ok(rel_path) => {$0
+ 1 + 2
+ }
+ Err(_) => None,
+ }
+}
+"#,
+ r#"
+fn main() {
+ match rel_path {
+ Ok(rel_path) => 1 + 2,
+ Err(_) => None,
+ }
+}
+"#,
+ "Unwrap block",
+ );
+ }
+
+ #[test]
+ fn unwrap_block_in_branch_non_standalone() {
+ check_assist_not_applicable_by_label(
+ unwrap_block,
+ r#"
+fn main() {
+ match rel_path {
+ Ok(rel_path) => {
+ if true {$0
+ foo()
+ }
+ }
+ Err(_) => None,
+ }
+}
+"#,
+ "Unwrap block",
+ );
+ }
+
+ #[test]
+ fn unwrap_block_in_branch_non_tail_expr_only() {
+ check_assist_not_applicable_by_label(
+ unwrap_block,
+ r#"
+fn main() {
+ match rel_path {
+ Ok(rel_path) => {$0
+ x;
+ y
+ }
+ Err(_) => None,
+ }
+}
+"#,
+ "Unwrap block",
+ );
+ }
+
+ #[test]
+ fn unwrap_block_in_closure() {
+ check_assist_by_label(
+ unwrap_block,
+ r#"
+fn main() {
+ let f = || {$0 foo() };
+}
+"#,
+ r#"
+fn main() {
+ let f = || foo();
+}
+"#,
+ "Unwrap block",
+ );
+ }
}
diff --git a/crates/ide-assists/src/handlers/unwrap_return_type.rs b/crates/ide-assists/src/handlers/unwrap_return_type.rs
index 1fe9ea4eb8..608c68ea85 100644
--- a/crates/ide-assists/src/handlers/unwrap_return_type.rs
+++ b/crates/ide-assists/src/handlers/unwrap_return_type.rs
@@ -37,7 +37,7 @@ use crate::{AssistContext, AssistId, Assists};
// fn foo() -> i32 { 42i32 }
// ```
-pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
let parent = ret_type.syntax().parent()?;
let body_expr = match_ast! {
diff --git a/crates/ide-assists/src/handlers/unwrap_tuple.rs b/crates/ide-assists/src/handlers/unwrap_tuple.rs
index e03274bbb3..7d0205afe3 100644
--- a/crates/ide-assists/src/handlers/unwrap_tuple.rs
+++ b/crates/ide-assists/src/handlers/unwrap_tuple.rs
@@ -25,7 +25,7 @@ use crate::{AssistContext, AssistId, Assists};
// let bar = "Bar";
// }
// ```
-pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let let_kw = ctx.find_token_syntax_at_offset(T![let])?;
let let_stmt = let_kw.parent().and_then(Either::<ast::LetStmt, ast::LetExpr>::cast)?;
let mut indent_level = let_stmt.indent_level();
diff --git a/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs b/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs
index 935ae18905..0c8e40bca1 100644
--- a/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs
+++ b/crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs
@@ -21,7 +21,10 @@ use crate::{AssistContext, Assists};
// todo!()
// }
// ```
-pub(crate) fn unwrap_type_to_generic_arg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn unwrap_type_to_generic_arg(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+) -> Option<()> {
let path_type = ctx.find_node_at_offset::<ast::PathType>()?;
let path = path_type.path()?;
let segment = path.segment()?;
diff --git a/crates/ide-assists/src/handlers/wrap_return_type.rs b/crates/ide-assists/src/handlers/wrap_return_type.rs
index ddc0af31c3..f9c103aab8 100644
--- a/crates/ide-assists/src/handlers/wrap_return_type.rs
+++ b/crates/ide-assists/src/handlers/wrap_return_type.rs
@@ -40,7 +40,7 @@ use crate::{AssistContext, AssistId, Assists};
// fn foo() -> Result<i32, ${0:_}> { Ok(42i32) }
// ```
-pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let ret_type = ctx.find_node_at_offset::<ast::RetType>()?;
let parent = ret_type.syntax().parent()?;
let body_expr = match_ast! {
@@ -212,7 +212,7 @@ impl WrapperKind {
// Try to find an wrapper type alias in the current scope (shadowing the default).
fn wrapper_alias<'db>(
- ctx: &AssistContext<'db>,
+ ctx: &AssistContext<'_, 'db>,
make: &SyntaxFactory,
core_wrapper: hir::Enum,
ast_ret_type: &ast::Type,
diff --git a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
index 635fab857d..94bd29049d 100644
--- a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
+++ b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
@@ -106,7 +106,7 @@ fn attempt_get_derive(attr: ast::Attr, ident: SyntaxToken) -> WrapUnwrapOption {
attempt_attr().unwrap_or_else(|| WrapUnwrapOption::WrapAttr(vec![attr]))
}
}
-pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
let option = if ctx.has_empty_selection() {
let ident = ctx.find_token_syntax_at_offset(T![ident]);
let attr = ctx.find_node_at_offset::<ast::Attr>();
@@ -161,7 +161,7 @@ pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -
fn wrap_derive(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
attr: ast::Attr,
derive_element: TextRange,
) -> Option<()> {
@@ -232,7 +232,11 @@ fn wrap_derive(
Some(())
}
-fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec<ast::Attr>) -> Option<()> {
+fn wrap_cfg_attrs(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_, '_>,
+ attrs: Vec<ast::Attr>,
+) -> Option<()> {
let (first_attr, last_attr) = (attrs.first()?, attrs.last()?);
let range = first_attr.syntax().text_range().cover(last_attr.syntax().text_range());
let handle_source_change = |edit: &mut SourceChangeBuilder| {
@@ -268,7 +272,7 @@ fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec<ast::At
fn unwrap_cfg_attr(
acc: &mut Assists,
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
meta: ast::CfgAttrMeta,
) -> Option<()> {
let top_attr = ast::Meta::from(meta.clone()).parent_attr()?;
diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs
index 80f05caf4e..eabcb8093e 100644
--- a/crates/ide-assists/src/lib.rs
+++ b/crates/ide-assists/src/lib.rs
@@ -102,7 +102,7 @@ pub fn assists(
mod handlers {
use crate::{AssistContext, Assists};
- pub(crate) type Handler = fn(&mut Assists, &AssistContext<'_>) -> Option<()>;
+ pub(crate) type Handler = fn(&mut Assists, &AssistContext<'_, '_>) -> Option<()>;
mod add_braces;
mod add_explicit_dot_deref;
@@ -232,7 +232,7 @@ mod handlers {
mod unmerge_match_arm;
mod unnecessary_async;
mod unqualify_method_call;
- mod unwrap_block;
+ mod unwrap_branch;
mod unwrap_return_type;
mod unwrap_tuple;
mod unwrap_type_to_generic_arg;
@@ -380,7 +380,8 @@ mod handlers {
unmerge_imports::unmerge_imports,
unnecessary_async::unnecessary_async,
unqualify_method_call::unqualify_method_call,
- unwrap_block::unwrap_block,
+ unwrap_branch::unwrap_block,
+ unwrap_branch::unwrap_branch,
unwrap_return_type::unwrap_return_type,
unwrap_tuple::unwrap_tuple,
unwrap_type_to_generic_arg::unwrap_type_to_generic_arg,
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 048f3d7ce8..d3ee35aa86 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -3736,6 +3736,29 @@ fn doctest_unwrap_block() {
"unwrap_block",
r#####"
fn foo() {
+ match () {
+ _ => {$0
+ bar()
+ }
+ }
+}
+"#####,
+ r#####"
+fn foo() {
+ match () {
+ _ => bar(),
+ }
+}
+"#####,
+ )
+}
+
+#[test]
+fn doctest_unwrap_branch() {
+ check_doc_test(
+ "unwrap_branch",
+ r#####"
+fn foo() {
if true {$0
println!("foo");
}
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index bf1062d207..096f6678a5 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -21,8 +21,7 @@ use syntax::{
SyntaxNode, SyntaxToken, T, TextRange, TextSize, WalkEvent,
ast::{
self, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
- edit::{AstNodeEdit, IndentLevel},
- edit_in_place::AttrsOwnerEdit,
+ edit::{AstNodeEdit, AttrsOwnerEdit, IndentLevel},
make,
prec::ExprPrecedence,
syntax_factory::SyntaxFactory,
@@ -183,7 +182,11 @@ pub fn filter_assoc_items(
(default_methods, def.body()),
(DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
),
- _ => default_methods == DefaultMethods::No,
+ ast::AssocItem::TypeAlias(def) => matches!(
+ (default_methods, def.ty()),
+ (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None)
+ ),
+ ast::AssocItem::MacroCall(_) => unreachable!(),
})
.collect();
@@ -222,7 +225,7 @@ pub fn add_trait_assoc_items_to_impl(
let item_prettified = prettify_macro_expansion(
sema.db,
original_item.syntax().clone(),
- &span_map,
+ span_map,
target_scope.krate().into(),
);
if let Some(formatted) = ast::AssocItem::cast(item_prettified) {
@@ -242,8 +245,9 @@ pub fn add_trait_assoc_items_to_impl(
PathTransform::trait_impl(target_scope, &source_scope, trait_, impl_.clone());
cloned_item = ast::AssocItem::cast(transform.apply(cloned_item.syntax())).unwrap();
}
- cloned_item.remove_attrs_and_docs();
- cloned_item
+ let (editor, cloned_item) = SyntaxEditor::with_ast_node(&cloned_item);
+ cloned_item.remove_attrs_and_docs(&editor);
+ ast::AssocItem::cast(editor.finish().new_root().clone()).unwrap()
})
.filter_map(|item| match item {
ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
@@ -382,18 +386,21 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
pat_head == var_head
}
-pub(crate) fn does_pat_variant_nested_or_literal(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool {
+pub(crate) fn does_pat_variant_nested_or_literal(
+ ctx: &AssistContext<'_, '_>,
+ pat: &ast::Pat,
+) -> bool {
check_pat_variant_nested_or_literal_with_depth(ctx, pat, 0)
}
-fn check_pat_variant_from_enum(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool {
+fn check_pat_variant_from_enum(ctx: &AssistContext<'_, '_>, pat: &ast::Pat) -> bool {
ctx.sema.type_of_pat(pat).is_none_or(|ty: hir::TypeInfo<'_>| {
ty.adjusted().as_adt().is_some_and(|adt| matches!(adt, hir::Adt::Enum(_)))
})
}
fn check_pat_variant_nested_or_literal_with_depth(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
pat: &ast::Pat,
depth_after_refutable: usize,
) -> bool {
@@ -409,6 +416,7 @@ fn check_pat_variant_nested_or_literal_with_depth(
| ast::Pat::MacroPat(_)
| ast::Pat::PathPat(_)
| ast::Pat::BoxPat(_)
+ | ast::Pat::DerefPat(_)
| ast::Pat::ConstBlockPat(_) => true,
ast::Pat::IdentPat(ident_pat) => ident_pat.pat().is_some_and(|pat| {
@@ -480,7 +488,7 @@ pub(crate) fn expr_fill_default(config: &AssistConfig) -> ast::Expr {
/// - `Some(None)`: no impl exists.
/// - `Some(Some(_))`: an impl exists, with no matching function names.
pub(crate) fn find_struct_impl(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
adt: &ast::Adt,
names: &[String],
) -> Option<Option<ast::Impl>> {
@@ -536,15 +544,11 @@ pub(crate) fn generate_impl_with_item(
adt: &ast::Adt,
body: Option<ast::AssocItemList>,
) -> ast::Impl {
- generate_impl_inner_with_factory(make, false, adt, None, true, body)
+ generate_impl_inner(make, false, adt, None, true, body)
}
-pub(crate) fn generate_impl_with_factory(make: &SyntaxFactory, adt: &ast::Adt) -> ast::Impl {
- generate_impl_inner_with_factory(make, false, adt, None, true, None)
-}
-
-pub(crate) fn generate_impl(adt: &ast::Adt) -> ast::Impl {
- generate_impl_inner(false, adt, None, true, None)
+pub(crate) fn generate_impl(make: &SyntaxFactory, adt: &ast::Adt) -> ast::Impl {
+ generate_impl_inner(make, false, adt, None, true, None)
}
/// Generates the corresponding `impl <trait> for Type {}` including type
@@ -557,7 +561,7 @@ pub(crate) fn generate_trait_impl(
adt: &ast::Adt,
trait_: ast::Type,
) -> ast::Impl {
- generate_impl_inner_with_factory(make, is_unsafe, adt, Some(trait_), true, None)
+ generate_impl_inner(make, is_unsafe, adt, Some(trait_), true, None)
}
/// Generates the corresponding `impl <trait> for Type {}` including type
@@ -569,7 +573,7 @@ pub(crate) fn generate_trait_impl_intransitive(
adt: &ast::Adt,
trait_: ast::Type,
) -> ast::Impl {
- generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, None)
+ generate_impl_inner(make, false, adt, Some(trait_), false, None)
}
pub(crate) fn generate_trait_impl_intransitive_with_item(
@@ -578,7 +582,7 @@ pub(crate) fn generate_trait_impl_intransitive_with_item(
trait_: ast::Type,
body: ast::AssocItemList,
) -> ast::Impl {
- generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, Some(body))
+ generate_impl_inner(make, false, adt, Some(trait_), false, Some(body))
}
pub(crate) fn generate_trait_impl_with_item(
@@ -588,79 +592,10 @@ pub(crate) fn generate_trait_impl_with_item(
trait_: ast::Type,
body: ast::AssocItemList,
) -> ast::Impl {
- generate_impl_inner_with_factory(make, is_unsafe, adt, Some(trait_), true, Some(body))
+ generate_impl_inner(make, is_unsafe, adt, Some(trait_), true, Some(body))
}
fn generate_impl_inner(
- is_unsafe: bool,
- adt: &ast::Adt,
- trait_: Option<ast::Type>,
- trait_is_transitive: bool,
- body: Option<ast::AssocItemList>,
-) -> ast::Impl {
- // Ensure lifetime params are before type & const params
- let generic_params = adt.generic_param_list().map(|generic_params| {
- let lifetime_params =
- generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
- let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
- let param = match param {
- ast::TypeOrConstParam::Type(param) => {
- // remove defaults since they can't be specified in impls
- let mut bounds =
- param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect());
- if let Some(trait_) = &trait_ {
- // Add the current trait to `bounds` if the trait is transitive,
- // meaning `impl<T> Trait for U<T>` requires `T: Trait`.
- if trait_is_transitive {
- bounds.push(make::type_bound(trait_.clone()));
- }
- };
- // `{ty_param}: {bounds}`
- let param = make::type_param(param.name()?, make::type_bound_list(bounds));
- ast::GenericParam::TypeParam(param)
- }
- ast::TypeOrConstParam::Const(param) => {
- // remove defaults since they can't be specified in impls
- let param = make::const_param(param.name()?, param.ty()?);
- ast::GenericParam::ConstParam(param)
- }
- };
- Some(param)
- });
-
- make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
- });
- let generic_args =
- generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update());
- let adt_assoc_bounds = trait_
- .as_ref()
- .zip(generic_params.as_ref())
- .and_then(|(trait_, params)| generic_param_associated_bounds(adt, trait_, params));
-
- let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text()));
-
- let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
- match trait_ {
- Some(trait_) => make::impl_trait(
- cfg_attrs,
- is_unsafe,
- None,
- None,
- generic_params,
- generic_args,
- false,
- trait_,
- ty,
- adt_assoc_bounds,
- adt.where_clause(),
- body,
- ),
- None => make::impl_(cfg_attrs, generic_params, generic_args, ty, adt.where_clause(), body),
- }
- .clone_for_update()
-}
-
-fn generate_impl_inner_with_factory(
make: &SyntaxFactory,
is_unsafe: bool,
adt: &ast::Adt,
@@ -700,12 +635,11 @@ fn generate_impl_inner_with_factory(
make.generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
});
- let generic_args =
- generic_params.as_ref().map(|params| params.to_generic_args().clone_for_update());
- let adt_assoc_bounds =
- trait_.as_ref().zip(generic_params.as_ref()).and_then(|(trait_, params)| {
- generic_param_associated_bounds_with_factory(make, adt, trait_, params)
- });
+ let generic_args = generic_params.as_ref().map(|params| params.to_generic_args(make));
+ let adt_assoc_bounds = trait_
+ .as_ref()
+ .zip(generic_params.as_ref())
+ .and_then(|(trait_, params)| generic_param_associated_bounds(make, adt, trait_, params));
let ty: ast::Type = make.ty_path(make.ident_path(&adt.name().unwrap().text())).into();
@@ -730,51 +664,6 @@ fn generate_impl_inner_with_factory(
}
fn generic_param_associated_bounds(
- adt: &ast::Adt,
- trait_: &ast::Type,
- generic_params: &ast::GenericParamList,
-) -> Option<ast::WhereClause> {
- let in_type_params = |name: &ast::NameRef| {
- generic_params
- .generic_params()
- .filter_map(|param| match param {
- ast::GenericParam::TypeParam(type_param) => type_param.name(),
- _ => None,
- })
- .any(|param| param.text() == name.text())
- };
- let adt_body = match adt {
- ast::Adt::Enum(e) => e.variant_list().map(|it| it.syntax().clone()),
- ast::Adt::Struct(s) => s.field_list().map(|it| it.syntax().clone()),
- ast::Adt::Union(u) => u.record_field_list().map(|it| it.syntax().clone()),
- };
- let mut trait_where_clause = adt_body
- .into_iter()
- .flat_map(|it| it.descendants())
- .filter_map(ast::Path::cast)
- .filter_map(|path| {
- let qualifier = path.qualifier()?.as_single_segment()?;
- let qualifier = qualifier
- .name_ref()
- .or_else(|| match qualifier.type_anchor()?.ty()? {
- ast::Type::PathType(path_type) => path_type.path()?.as_single_name_ref(),
- _ => None,
- })
- .filter(in_type_params)?;
- Some((qualifier, path.segment()?.name_ref()?))
- })
- .map(|(qualifier, assoc_name)| {
- let segments = [qualifier, assoc_name].map(make::path_segment);
- let path = make::path_from_segments(segments, false);
- let bounds = Some(make::type_bound(trait_.clone()));
- make::where_pred(either::Either::Right(make::ty_path(path)), bounds)
- })
- .unique_by(|it| it.syntax().to_string())
- .peekable();
- trait_where_clause.peek().is_some().then(|| make::where_clause(trait_where_clause))
-}
-
-fn generic_param_associated_bounds_with_factory(
make: &SyntaxFactory,
adt: &ast::Adt,
trait_: &ast::Type,
@@ -1147,7 +1036,7 @@ pub(crate) fn add_group_separators(s: &str, group_size: usize) -> String {
/// Replaces the record expression, handling field shorthands including inside macros.
pub(crate) fn replace_record_field_expr(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
edit: &mut SourceChangeBuilder,
record_field: ast::RecordExprField,
initializer: ast::Expr,
diff --git a/crates/ide-assists/src/utils/ref_field_expr.rs b/crates/ide-assists/src/utils/ref_field_expr.rs
index fc9bf210e4..d2b02b3373 100644
--- a/crates/ide-assists/src/utils/ref_field_expr.rs
+++ b/crates/ide-assists/src/utils/ref_field_expr.rs
@@ -13,7 +13,7 @@ use crate::AssistContext;
/// Decides whether the new path expression needs to be dereferenced and/or wrapped in parens.
/// Returns the relevant parent expression to replace and the [RefData].
pub(crate) fn determine_ref_and_parens(
- ctx: &AssistContext<'_>,
+ ctx: &AssistContext<'_, '_>,
field_expr: &FieldExpr,
) -> (ast::Expr, RefData) {
let s = field_expr.syntax();
@@ -62,8 +62,8 @@ pub(crate) fn determine_ref_and_parens(
// other combinations (`&value` -> `value`, `&&value` -> `&value`, `&value` -> `&&value`) might or might not be able to auto-ref/deref,
// but there might be trait implementations an added `&` might resolve to
// -> ONLY handle auto-ref from `value` to `&value`
- fn is_auto_ref(ctx: &AssistContext<'_>, call_expr: &MethodCallExpr) -> bool {
- fn impl_(ctx: &AssistContext<'_>, call_expr: &MethodCallExpr) -> Option<bool> {
+ fn is_auto_ref(ctx: &AssistContext<'_, '_>, call_expr: &MethodCallExpr) -> bool {
+ fn impl_(ctx: &AssistContext<'_, '_>, call_expr: &MethodCallExpr) -> Option<bool> {
let rec = call_expr.receiver()?;
let rec_ty = ctx.sema.type_of_expr(&rec)?.original();
// input must be actual value
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 2ed582598b..f3190bbbc8 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -34,7 +34,8 @@ use crate::{
CompletionContext, CompletionItem, CompletionItemKind,
context::{
DotAccess, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind,
- PathCompletionCtx, PathKind, PatternContext, TypeAscriptionTarget, TypeLocation, Visible,
+ PathCompletionCtx, PathKind, PatternContext, Qualified, TypeAscriptionTarget, TypeLocation,
+ Visible,
},
item::Builder,
render::{
@@ -86,7 +87,7 @@ impl Completions {
}
}
- pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_>, keyword: &'static str) {
+ pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_, '_>, keyword: &'static str) {
let item = CompletionItem::new(
CompletionItemKind::Keyword,
ctx.source_range(),
@@ -96,7 +97,7 @@ impl Completions {
item.add_to(self, ctx.db);
}
- pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_>) {
+ pub(crate) fn add_nameref_keywords_with_colon(&mut self, ctx: &CompletionContext<'_, '_>) {
["self::", "crate::"].into_iter().for_each(|kw| self.add_keyword(ctx, kw));
if ctx.depth_from_crate_root > 0 {
@@ -106,7 +107,7 @@ impl Completions {
pub(crate) fn add_nameref_keywords_with_type_like(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
) {
let mut add_keyword = |kw| {
@@ -119,7 +120,7 @@ impl Completions {
}
}
- pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext<'_>) {
+ pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext<'_, '_>) {
["self", "crate"].into_iter().for_each(|kw| self.add_keyword(ctx, kw));
if ctx.depth_from_crate_root > 0 {
@@ -129,7 +130,7 @@ impl Completions {
pub(crate) fn add_type_keywords(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
) {
let mut add_keyword = |kw, snippet| {
@@ -144,7 +145,7 @@ impl Completions {
pub(crate) fn add_super_keyword(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
super_chain_len: Option<usize>,
) {
if let Some(len) = super_chain_len
@@ -157,7 +158,7 @@ impl Completions {
pub(crate) fn add_keyword_snippet_expr(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
incomplete_let: bool,
kw: &str,
snippet: &str,
@@ -184,7 +185,7 @@ impl Completions {
pub(crate) fn add_keyword_snippet(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
kw: &str,
snippet: &str,
) {
@@ -200,7 +201,7 @@ impl Completions {
pub(crate) fn add_expr(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
expr: &hir::term_search::Expr<'_>,
) {
if let Some(item) = render_expr(ctx, expr) {
@@ -210,7 +211,7 @@ impl Completions {
pub(crate) fn add_crate_roots(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
) {
ctx.process_all_names(&mut |name, res, doc_aliases| match res {
@@ -223,7 +224,7 @@ impl Completions {
pub(crate) fn add_path_resolution(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
local_name: hir::Name,
resolution: hir::ScopeDef,
@@ -245,7 +246,7 @@ impl Completions {
pub(crate) fn add_pattern_resolution(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
pattern_ctx: &PatternContext,
local_name: hir::Name,
resolution: hir::ScopeDef,
@@ -266,7 +267,7 @@ impl Completions {
pub(crate) fn add_enum_variants(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
e: hir::Enum,
) {
@@ -277,7 +278,7 @@ impl Completions {
pub(crate) fn add_module(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
module: hir::Module,
local_name: hir::Name,
@@ -294,7 +295,7 @@ impl Completions {
pub(crate) fn add_macro(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
mac: hir::Macro,
local_name: hir::Name,
@@ -315,7 +316,7 @@ impl Completions {
pub(crate) fn add_function(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
func: hir::Function,
local_name: Option<hir::Name>,
@@ -337,7 +338,7 @@ impl Completions {
pub(crate) fn add_method(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
dot_access: &DotAccess<'_>,
func: hir::Function,
receiver: Option<SmolStr>,
@@ -361,7 +362,7 @@ impl Completions {
pub(crate) fn add_method_with_import(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
dot_access: &DotAccess<'_>,
func: hir::Function,
import: LocatedImport,
@@ -385,7 +386,7 @@ impl Completions {
.add_to(self, ctx.db);
}
- pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
+ pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_, '_>, konst: hir::Const) {
let is_private_editable = match ctx.is_visible(&konst) {
Visible::Yes => false,
Visible::Editable => true,
@@ -399,7 +400,7 @@ impl Completions {
pub(crate) fn add_type_alias(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
type_alias: hir::TypeAlias,
) {
let is_private_editable = match ctx.is_visible(&type_alias) {
@@ -415,7 +416,7 @@ impl Completions {
pub(crate) fn add_type_alias_with_eq(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
type_alias: hir::TypeAlias,
) {
if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
@@ -426,7 +427,7 @@ impl Completions {
pub(crate) fn add_qualified_enum_variant(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
variant: hir::EnumVariant,
path: hir::ModPath,
@@ -443,7 +444,7 @@ impl Completions {
pub(crate) fn add_enum_variant(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
variant: hir::EnumVariant,
local_name: Option<hir::Name>,
@@ -466,7 +467,7 @@ impl Completions {
pub(crate) fn add_field(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
dot_access: &DotAccess<'_>,
receiver: Option<SmolStr>,
field: hir::Field,
@@ -490,7 +491,7 @@ impl Completions {
pub(crate) fn add_struct_literal(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
strukt: hir::Struct,
path: Option<hir::ModPath>,
@@ -514,7 +515,7 @@ impl Completions {
pub(crate) fn add_union_literal(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
un: hir::Union,
path: Option<hir::ModPath>,
local_name: Option<hir::Name>,
@@ -535,7 +536,7 @@ impl Completions {
pub(crate) fn add_tuple_field(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
receiver: Option<SmolStr>,
field: usize,
ty: &hir::Type<'_>,
@@ -546,7 +547,7 @@ impl Completions {
self.add(item);
}
- pub(crate) fn add_lifetime(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) {
+ pub(crate) fn add_lifetime(&mut self, ctx: &CompletionContext<'_, '_>, name: hir::Name) {
CompletionItem::new(
SymbolKind::LifetimeParam,
ctx.source_range(),
@@ -556,7 +557,7 @@ impl Completions {
.add_to(self, ctx.db)
}
- pub(crate) fn add_label(&mut self, ctx: &CompletionContext<'_>, name: hir::Name) {
+ pub(crate) fn add_label(&mut self, ctx: &CompletionContext<'_, '_>, name: hir::Name) {
CompletionItem::new(
SymbolKind::Label,
ctx.source_range(),
@@ -568,7 +569,7 @@ impl Completions {
pub(crate) fn add_variant_pat(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
pattern_ctx: &PatternContext,
path_ctx: Option<&PathCompletionCtx<'_>>,
variant: hir::EnumVariant,
@@ -589,7 +590,7 @@ impl Completions {
pub(crate) fn add_qualified_variant_pat(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
pattern_ctx: &PatternContext,
variant: hir::EnumVariant,
path: hir::ModPath,
@@ -610,7 +611,7 @@ impl Completions {
pub(crate) fn add_struct_pat(
&mut self,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
pattern_ctx: &PatternContext,
strukt: hir::Struct,
local_name: Option<hir::Name>,
@@ -628,7 +629,7 @@ impl Completions {
));
}
- pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) {
+ pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_, '_>, name: &str) {
let item = CompletionItem::new(
CompletionItemKind::Binding,
ctx.source_range(),
@@ -643,10 +644,10 @@ impl Completions {
/// Skips variants that are visible with single segment paths.
fn enum_variants_with_paths(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
enum_: hir::Enum,
impl_: Option<&ast::Impl>,
- cb: impl Fn(&mut Completions, &CompletionContext<'_>, hir::EnumVariant, hir::ModPath),
+ cb: impl Fn(&mut Completions, &CompletionContext<'_, '_>, hir::EnumVariant, hir::ModPath),
) {
let mut process_variant = |variant: EnumVariant| {
let self_path = hir::ModPath::from_segments(
@@ -682,7 +683,7 @@ fn enum_variants_with_paths(
pub(super) fn complete_name(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
NameContext { name, kind }: &NameContext,
) {
match kind {
@@ -723,10 +724,10 @@ pub(super) fn complete_name(
}
}
-pub(super) fn complete_name_ref(
+pub(super) fn complete_name_ref<'db>(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
- NameRefContext { nameref, kind }: &NameRefContext<'_>,
+ ctx: &CompletionContext<'_, 'db>,
+ NameRefContext { nameref, kind }: &NameRefContext<'db>,
) {
match kind {
NameRefKind::Path(path_ctx) => {
@@ -752,6 +753,7 @@ pub(super) fn complete_name_ref(
if let TypeAscriptionTarget::RetType { item: Some(item), .. } =
ascription
&& path_ctx.required_thin_arrow().is_some()
+ && matches!(path_ctx.qualified, Qualified::No)
{
keyword::complete_for_and_where(acc, ctx, &item.clone().into());
}
@@ -811,7 +813,7 @@ pub(super) fn complete_name_ref(
fn complete_patterns(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
pattern_ctx: &PatternContext,
) {
flyimport::import_on_the_fly_pat(acc, ctx, pattern_ctx);
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs
index da1e664f96..df513dd8fd 100644
--- a/crates/ide-completion/src/completions/attribute.rs
+++ b/crates/ide-completion/src/completions/attribute.rs
@@ -4,13 +4,7 @@
use std::sync::LazyLock;
-use ide_db::{
- FxHashMap, SymbolKind,
- generated::lints::{
- CLIPPY_LINT_GROUPS, CLIPPY_LINTS, DEFAULT_LINTS, FEATURES, Lint, RUSTDOC_LINTS,
- },
- syntax_helpers::node_ext::parse_tt_as_comma_sep_paths,
-};
+use ide_db::{FxHashMap, SymbolKind, syntax_helpers::node_ext::parse_tt_as_comma_sep_paths};
use itertools::Itertools;
use syntax::{
AstNode, Edition, SyntaxKind, T,
@@ -26,6 +20,7 @@ use crate::{
mod cfg;
mod derive;
mod diagnostic;
+mod feature;
mod lint;
mod macro_use;
mod repr;
@@ -36,8 +31,8 @@ pub(crate) use self::derive::complete_derive_path;
/// Complete inputs to known builtin attributes as well as derive attributes
pub(crate) fn complete_known_attribute_input(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
- &colon_prefix: &bool,
+ ctx: &CompletionContext<'_, '_>,
+ colon_prefix: bool,
fake_attribute_under_caret: &ast::TokenTreeMeta,
extern_crate: Option<&ast::ExternCrate>,
) -> Option<()> {
@@ -49,35 +44,25 @@ pub(crate) fn complete_known_attribute_input(
let tt = attribute.token_tree()?;
match segments.as_slice() {
- ["repr"] => repr::complete_repr(acc, ctx, tt),
- ["feature"] => lint::complete_lint(
+ ["repr"] => repr::complete_repr(acc, ctx, &parse_comma_sep_expr(tt)?),
+ ["feature"] => {
+ feature::complete_feature(acc, ctx, &parse_tt_as_comma_sep_paths(tt, ctx.edition)?)
+ }
+ ["allow" | "expect" | "deny" | "forbid" | "warn"] => lint::complete_lint(
acc,
ctx,
colon_prefix,
&parse_tt_as_comma_sep_paths(tt, ctx.edition)?,
- FEATURES,
),
- ["allow"] | ["expect"] | ["deny"] | ["forbid"] | ["warn"] => {
- let existing_lints = parse_tt_as_comma_sep_paths(tt, ctx.edition)?;
-
- let lints: Vec<Lint> = CLIPPY_LINT_GROUPS
- .iter()
- .map(|g| &g.lint)
- .chain(DEFAULT_LINTS)
- .chain(CLIPPY_LINTS)
- .chain(RUSTDOC_LINTS)
- .cloned()
- .collect();
-
- lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints);
- }
["macro_use"] => macro_use::complete_macro_use(
acc,
ctx,
extern_crate,
&parse_tt_as_comma_sep_paths(tt, ctx.edition)?,
),
- ["diagnostic", "on_unimplemented"] => diagnostic::complete_on_unimplemented(acc, ctx, tt),
+ ["diagnostic", "on_unimplemented"] => {
+ diagnostic::complete_on_unimplemented(acc, ctx, &parse_comma_sep_expr(tt)?)
+ }
_ => (),
}
Some(())
@@ -85,7 +70,7 @@ pub(crate) fn complete_known_attribute_input(
pub(crate) fn complete_attribute_path(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
&AttrCtx { kind, annotated_item_kind, ref derive_helpers }: &AttrCtx,
) {
@@ -190,7 +175,7 @@ pub(crate) fn complete_attribute_path(
match attributes {
Some(applicable) => applicable
.iter()
- .flat_map(|name| ATTRIBUTES.binary_search_by(|attr| attr.key().cmp(name)).ok())
+ .flat_map(|name| ATTRIBUTES.binary_search_by_key(name, |attr| attr.key()).ok())
.flat_map(|idx| ATTRIBUTES.get(idx))
.for_each(add_completion),
None if is_inner => ATTRIBUTES.iter().for_each(add_completion),
diff --git a/crates/ide-completion/src/completions/attribute/cfg.rs b/crates/ide-completion/src/completions/attribute/cfg.rs
index 0d36fb7a40..1672e8e793 100644
--- a/crates/ide-completion/src/completions/attribute/cfg.rs
+++ b/crates/ide-completion/src/completions/attribute/cfg.rs
@@ -6,12 +6,12 @@ use syntax::{AstToken, Direction, NodeOrToken, SmolStr, SyntaxKind, algo, ast::I
use crate::{CompletionItem, completions::Completions, context::CompletionContext};
-pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
+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, ctx.edition);
completion.insert_text(format!(r#""{item}""#));
- acc.add(completion.build(ctx.db));
+ completion.add_to(acc, ctx.db);
};
// FIXME: Move this into context/analysis.rs
@@ -49,8 +49,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
ctx.edition,
);
item.insert_text(insert_text);
-
- acc.add(item.build(ctx.db));
+ item.add_to(acc, ctx.db);
}),
},
None => ctx
@@ -82,7 +81,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
{
item.insert_snippet(cap, snippet);
}
- acc.add(item.build(ctx.db));
+ item.add_to(acc, ctx.db);
}),
}
}
diff --git a/crates/ide-completion/src/completions/attribute/derive.rs b/crates/ide-completion/src/completions/attribute/derive.rs
index 57ea609a40..9e7dabbd01 100644
--- a/crates/ide-completion/src/completions/attribute/derive.rs
+++ b/crates/ide-completion/src/completions/attribute/derive.rs
@@ -12,7 +12,7 @@ use crate::{
pub(crate) fn complete_derive_path(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
existing_derives: &ExistingDerives,
) {
diff --git a/crates/ide-completion/src/completions/attribute/diagnostic.rs b/crates/ide-completion/src/completions/attribute/diagnostic.rs
index 8adc974239..1a2e5652e1 100644
--- a/crates/ide-completion/src/completions/attribute/diagnostic.rs
+++ b/crates/ide-completion/src/completions/attribute/diagnostic.rs
@@ -9,47 +9,45 @@ use super::AttrCompletion;
pub(super) fn complete_on_unimplemented(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
- input: ast::TokenTree,
+ ctx: &CompletionContext<'_, '_>,
+ existing_keys: &[ast::Expr],
) {
- if let Some(existing_keys) = super::parse_comma_sep_expr(input) {
- for attr in ATTRIBUTE_ARGS {
- let already_annotated = existing_keys
- .iter()
- .filter_map(|expr| match expr {
- ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
- ast::Expr::BinExpr(bin)
- if bin.op_kind() == Some(ast::BinaryOp::Assignment { op: None }) =>
- {
- match bin.lhs()? {
- ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
- _ => None,
- }
+ for attr in ATTRIBUTE_ARGS {
+ let already_annotated = existing_keys
+ .iter()
+ .filter_map(|expr| match expr {
+ ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
+ ast::Expr::BinExpr(bin)
+ if bin.op_kind() == Some(ast::BinaryOp::Assignment { op: None }) =>
+ {
+ match bin.lhs()? {
+ ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
+ _ => None,
}
- _ => None,
- })
- .any(|it| {
- let text = it.text();
- attr.key() == text && text != "note"
- });
- if already_annotated {
- continue;
- }
+ }
+ _ => None,
+ })
+ .any(|it| {
+ let text = it.text();
+ attr.key() == text && text != "note"
+ });
+ if already_annotated {
+ continue;
+ }
- let mut item = CompletionItem::new(
- SymbolKind::BuiltinAttr,
- ctx.source_range(),
- attr.label,
- ctx.edition,
- );
- if let Some(lookup) = attr.lookup {
- item.lookup_by(lookup);
- }
- if let Some((snippet, cap)) = attr.snippet.zip(ctx.config.snippet_cap) {
- item.insert_snippet(cap, snippet);
- }
- item.add_to(acc, ctx.db);
+ let mut item = CompletionItem::new(
+ SymbolKind::BuiltinAttr,
+ ctx.source_range(),
+ attr.label,
+ ctx.edition,
+ );
+ if let Some(lookup) = attr.lookup {
+ item.lookup_by(lookup);
+ }
+ if let Some((snippet, cap)) = attr.snippet.zip(ctx.config.snippet_cap) {
+ item.insert_snippet(cap, snippet);
}
+ item.add_to(acc, ctx.db);
}
}
diff --git a/crates/ide-completion/src/completions/attribute/feature.rs b/crates/ide-completion/src/completions/attribute/feature.rs
new file mode 100644
index 0000000000..1bcf05faa8
--- /dev/null
+++ b/crates/ide-completion/src/completions/attribute/feature.rs
@@ -0,0 +1,30 @@
+//! Completion for features
+use ide_db::{
+ SymbolKind,
+ documentation::Documentation,
+ generated::lints::{FEATURES, Lint},
+};
+use syntax::ast;
+
+use crate::{Completions, context::CompletionContext, item::CompletionItem};
+
+pub(super) fn complete_feature(
+ acc: &mut Completions,
+ ctx: &CompletionContext<'_, '_>,
+ existing_features: &[ast::Path],
+) {
+ for &Lint { label, description, .. } in FEATURES {
+ let feature_already_annotated = existing_features
+ .iter()
+ .filter_map(|p| p.as_single_name_ref())
+ .any(|n| n.text() == label);
+ if feature_already_annotated {
+ continue;
+ }
+
+ let mut item =
+ CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label, ctx.edition);
+ item.documentation(Documentation::new_borrowed(description));
+ item.add_to(acc, ctx.db)
+ }
+}
diff --git a/crates/ide-completion/src/completions/attribute/lint.rs b/crates/ide-completion/src/completions/attribute/lint.rs
index df577b8ed0..2f957b5a55 100644
--- a/crates/ide-completion/src/completions/attribute/lint.rs
+++ b/crates/ide-completion/src/completions/attribute/lint.rs
@@ -1,29 +1,29 @@
//! Completion for lints
-use ide_db::{SymbolKind, documentation::Documentation, generated::lints::Lint};
+use ide_db::{
+ SymbolKind,
+ documentation::Documentation,
+ generated::lints::{CLIPPY_LINT_GROUPS, CLIPPY_LINTS, DEFAULT_LINTS, Lint, RUSTDOC_LINTS},
+};
use syntax::ast;
use crate::{Completions, context::CompletionContext, item::CompletionItem};
pub(super) fn complete_lint(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
is_qualified: bool,
existing_lints: &[ast::Path],
- lints_completions: &[Lint],
) {
- for &Lint { label, description, .. } in lints_completions {
- let (qual, name) = {
- // FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead?
- let mut parts = label.split("::");
- let ns_or_label = match parts.next() {
- Some(it) => it,
- None => continue,
- };
- let label = parts.next();
- match label {
- Some(label) => (Some(ns_or_label), label),
- None => (None, ns_or_label),
- }
+ let lints = (CLIPPY_LINT_GROUPS.iter().map(|g| &g.lint))
+ .chain(DEFAULT_LINTS)
+ .chain(CLIPPY_LINTS)
+ .chain(RUSTDOC_LINTS);
+
+ for &Lint { label, description, .. } in lints {
+ // FIXME: change `Lint`'s label to not store a path in it but split the prefix off instead?
+ let (qual, name) = match label.split_once("::") {
+ Some((qual, name)) => (Some(qual), name),
+ None => (None, label),
};
if qual.is_none() && is_qualified {
// qualified completion requested, but this lint is unqualified
@@ -56,7 +56,7 @@ pub(super) fn complete_lint(
};
let mut item =
CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label, ctx.edition);
- item.documentation(Documentation::new_owned(description.to_owned()));
+ item.documentation(Documentation::new_borrowed(description));
item.add_to(acc, ctx.db)
}
}
diff --git a/crates/ide-completion/src/completions/attribute/macro_use.rs b/crates/ide-completion/src/completions/attribute/macro_use.rs
index 136315c61f..618cf6fd01 100644
--- a/crates/ide-completion/src/completions/attribute/macro_use.rs
+++ b/crates/ide-completion/src/completions/attribute/macro_use.rs
@@ -7,7 +7,7 @@ use crate::{Completions, context::CompletionContext, item::CompletionItem};
pub(super) fn complete_macro_use(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
extern_crate: Option<&ast::ExternCrate>,
existing_imports: &[ast::Path],
) {
@@ -20,11 +20,11 @@ pub(super) fn complete_macro_use(
let mac_name = mac.name(ctx.db);
let mac_name = mac_name.as_str();
- let existing_import = existing_imports
+ let already_imported = existing_imports
.iter()
.filter_map(|p| p.as_single_name_ref())
- .find(|n| n.text() == mac_name);
- if existing_import.is_some() {
+ .any(|n| n.text() == mac_name);
+ if already_imported {
continue;
}
diff --git a/crates/ide-completion/src/completions/attribute/repr.rs b/crates/ide-completion/src/completions/attribute/repr.rs
index cb7ccf7373..63cddb365e 100644
--- a/crates/ide-completion/src/completions/attribute/repr.rs
+++ b/crates/ide-completion/src/completions/attribute/repr.rs
@@ -7,43 +7,37 @@ use crate::{Completions, context::CompletionContext, item::CompletionItem};
pub(super) fn complete_repr(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
- input: ast::TokenTree,
+ ctx: &CompletionContext<'_, '_>,
+ existing_reprs: &[ast::Expr],
) {
- if let Some(existing_reprs) = super::parse_comma_sep_expr(input) {
- for &ReprCompletion { label, snippet, lookup, collides } in REPR_COMPLETIONS {
- let repr_already_annotated = existing_reprs
- .iter()
- .filter_map(|expr| match expr {
+ for &ReprCompletion { label, snippet, lookup, collides } in REPR_COMPLETIONS {
+ let repr_already_annotated = existing_reprs
+ .iter()
+ .filter_map(|expr| match expr {
+ ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
+ ast::Expr::CallExpr(call) => match call.expr()? {
ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
- ast::Expr::CallExpr(call) => match call.expr()? {
- ast::Expr::PathExpr(path) => path.path()?.as_single_name_ref(),
- _ => None,
- },
_ => None,
- })
- .any(|it| {
- let text = it.text();
- lookup.unwrap_or(label) == text || collides.contains(&text.as_str())
- });
- if repr_already_annotated {
- continue;
- }
+ },
+ _ => None,
+ })
+ .any(|it| {
+ let text = it.text();
+ lookup.unwrap_or(label) == text || collides.contains(&text.as_str())
+ });
+ if repr_already_annotated {
+ continue;
+ }
- let mut item = CompletionItem::new(
- SymbolKind::BuiltinAttr,
- ctx.source_range(),
- label,
- ctx.edition,
- );
- if let Some(lookup) = lookup {
- item.lookup_by(lookup);
- }
- if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) {
- item.insert_snippet(cap, snippet);
- }
- item.add_to(acc, ctx.db);
+ let mut item =
+ CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), label, ctx.edition);
+ if let Some(lookup) = lookup {
+ item.lookup_by(lookup);
+ }
+ if let Some((snippet, cap)) = snippet.zip(ctx.config.snippet_cap) {
+ item.insert_snippet(cap, snippet);
}
+ item.add_to(acc, ctx.db);
}
}
diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs
index 18c1992afa..2cc2200df9 100644
--- a/crates/ide-completion/src/completions/dot.rs
+++ b/crates/ide-completion/src/completions/dot.rs
@@ -18,7 +18,7 @@ use crate::{
/// Complete dot accesses, i.e. fields or methods.
pub(crate) fn complete_dot(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
dot_access: &DotAccess<'_>,
) {
let receiver_ty = match dot_access {
@@ -126,7 +126,7 @@ pub(crate) fn complete_dot(
pub(crate) fn complete_undotted_self(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
expr_ctx: &PathExprCtx<'_>,
) {
@@ -198,7 +198,7 @@ pub(crate) fn complete_undotted_self(
fn complete_fields(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
receiver: &hir::Type<'_>,
mut named_field: impl FnMut(&mut Completions, hir::Field, hir::Type<'_>),
mut tuple_index: impl FnMut(&mut Completions, usize, hir::Type<'_>),
@@ -227,13 +227,13 @@ fn complete_fields(
}
fn complete_methods(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
receiver: &hir::Type<'_>,
traits_in_scope: &FxHashSet<hir::TraitId>,
f: impl FnMut(hir::Function),
) {
- struct Callback<'a, F> {
- ctx: &'a CompletionContext<'a>,
+ struct Callback<'a, 'db, F> {
+ ctx: &'a CompletionContext<'a, 'db>,
f: F,
// We deliberately deduplicate by function ID and not name, because while inherent methods cannot be
// duplicated, trait methods can. And it is still useful to show all of them (even when there
@@ -241,7 +241,7 @@ fn complete_methods(
seen_methods: FxHashSet<Function>,
}
- impl<F> MethodCandidateCallback for Callback<'_, F>
+ impl<F> MethodCandidateCallback for Callback<'_, '_, F>
where
F: FnMut(hir::Function),
{
@@ -1093,7 +1093,7 @@ impl Foo { fn foo(&mut self) { let _: fn(&mut Self) = |this| { $0 } } }"#,
me this.foo() fn(&mut self)
lc self &mut Foo
lc this &mut Foo
- md core
+ md core::
sp Self Foo
st Foo Foo
tt Fn
@@ -1117,7 +1117,7 @@ impl Foo { fn foo(&self) { let _: fn(&Self) = |foo| { $0 } } }"#,
me self.foo() fn(&self)
lc foo &Foo
lc self &Foo
- md core
+ md core::
sp Self Foo
st Foo Foo
tt Fn
@@ -1137,7 +1137,7 @@ impl Foo { fn foo(&self) { let _: fn(&Self) = || { $0 } } }"#,
fd self.field i32
me self.foo() fn(&self)
lc self &Foo
- md core
+ md core::
sp Self Foo
st Foo Foo
tt Fn
@@ -1159,7 +1159,7 @@ impl Foo { fn foo(&self) { let _: fn(&Self, &Self) = |foo, other| { $0 } } }"#,
lc foo &Foo
lc other &Foo
lc self &Foo
- md core
+ md core::
sp Self Foo
st Foo Foo
tt Fn
diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs
index 885d1a3075..30662877d6 100644
--- a/crates/ide-completion/src/completions/env_vars.rs
+++ b/crates/ide-completion/src/completions/env_vars.rs
@@ -47,7 +47,7 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[
pub(crate) fn complete_cargo_env_vars(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
original: &ast::String,
expanded: &ast::String,
) -> Option<()> {
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index c15c67173e..a2a4cbac21 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -12,14 +12,14 @@ use crate::{
context::{PathCompletionCtx, PathExprCtx, Qualified},
};
-struct PathCallback<'a, F> {
- ctx: &'a CompletionContext<'a>,
+struct PathCallback<'a, 'db, F> {
+ ctx: &'a CompletionContext<'a, 'db>,
acc: &'a mut Completions,
add_assoc_item: F,
seen: FxHashSet<hir::AssocItem>,
}
-impl<F> PathCandidateCallback for PathCallback<'_, F>
+impl<F> PathCandidateCallback for PathCallback<'_, '_, F>
where
F: FnMut(&mut Completions, hir::AssocItem),
{
@@ -46,7 +46,7 @@ where
pub(crate) fn complete_expr_path(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
expr_ctx: &PathExprCtx<'_>,
) {
@@ -458,7 +458,7 @@ pub(crate) fn complete_expr_path(
pub(crate) fn complete_expr(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
) {
let _p = tracing::info_span!("complete_expr").entered();
diff --git a/crates/ide-completion/src/completions/extern_abi.rs b/crates/ide-completion/src/completions/extern_abi.rs
index 570d1a0a2d..b00b2e1907 100644
--- a/crates/ide-completion/src/completions/extern_abi.rs
+++ b/crates/ide-completion/src/completions/extern_abi.rs
@@ -43,7 +43,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().is_some_and(|it| ast::Abi::can_cast(it.kind())) {
diff --git a/crates/ide-completion/src/completions/extern_crate.rs b/crates/ide-completion/src/completions/extern_crate.rs
index 91202e8b32..819db549d4 100644
--- a/crates/ide-completion/src/completions/extern_crate.rs
+++ b/crates/ide-completion/src/completions/extern_crate.rs
@@ -8,7 +8,7 @@ use crate::{CompletionItem, CompletionItemKind, context::CompletionContext};
use super::Completions;
-pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_>) {
+pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_, '_>) {
let imported_extern_crates: Vec<Name> = ctx.scope.extern_crate_decls().collect();
for (name, module) in ctx.scope.extern_crates() {
diff --git a/crates/ide-completion/src/completions/field.rs b/crates/ide-completion/src/completions/field.rs
index 26afa9c8ad..b4f7e1839d 100644
--- a/crates/ide-completion/src/completions/field.rs
+++ b/crates/ide-completion/src/completions/field.rs
@@ -7,7 +7,7 @@ use crate::{
pub(crate) fn complete_field_list_tuple_variant(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
) {
if ctx.qualifier_ctx.vis_node.is_some() {
@@ -28,7 +28,7 @@ pub(crate) fn complete_field_list_tuple_variant(
pub(crate) fn complete_field_list_record_variant(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
) {
if ctx.qualifier_ctx.vis_node.is_none() {
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 2cf87baf33..b350647b9a 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -108,10 +108,10 @@ use crate::{
// The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag.
// Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding
// capability enabled.
-pub(crate) fn import_on_the_fly_path(
+pub(crate) fn import_on_the_fly_path<'db>(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
- path_ctx: &PathCompletionCtx<'_>,
+ ctx: &CompletionContext<'_, 'db>,
+ path_ctx: &PathCompletionCtx<'db>,
) -> Option<()> {
if !ctx.config.enable_imports_on_the_fly {
return None;
@@ -155,7 +155,7 @@ pub(crate) fn import_on_the_fly_path(
pub(crate) fn import_on_the_fly_pat(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
pattern_ctx: &PatternContext,
) -> Option<()> {
if !ctx.config.enable_imports_on_the_fly {
@@ -178,10 +178,10 @@ pub(crate) fn import_on_the_fly_pat(
)
}
-pub(crate) fn import_on_the_fly_dot(
+pub(crate) fn import_on_the_fly_dot<'db>(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
- dot_access: &DotAccess<'_>,
+ ctx: &CompletionContext<'_, 'db>,
+ dot_access: &DotAccess<'db>,
) -> Option<()> {
if !ctx.config.enable_imports_on_the_fly {
return None;
@@ -206,11 +206,11 @@ pub(crate) fn import_on_the_fly_dot(
)
}
-fn import_on_the_fly(
+fn import_on_the_fly<'db>(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
- path_ctx @ PathCompletionCtx { kind, .. }: &PathCompletionCtx<'_>,
- import_assets: ImportAssets<'_>,
+ ctx: &CompletionContext<'_, 'db>,
+ path_ctx @ PathCompletionCtx { kind, .. }: &PathCompletionCtx<'db>,
+ import_assets: ImportAssets<'db>,
position: SyntaxNode,
potential_import_name: String,
) -> Option<()> {
@@ -292,11 +292,11 @@ fn import_on_the_fly(
Some(())
}
-fn import_on_the_fly_pat_(
+fn import_on_the_fly_pat_<'db>(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, 'db>,
pattern_ctx: &PatternContext,
- import_assets: ImportAssets<'_>,
+ import_assets: ImportAssets<'db>,
position: SyntaxNode,
potential_import_name: String,
) -> Option<()> {
@@ -338,11 +338,11 @@ fn import_on_the_fly_pat_(
Some(())
}
-fn import_on_the_fly_method(
+fn import_on_the_fly_method<'db>(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
- dot_access: &DotAccess<'_>,
- import_assets: ImportAssets<'_>,
+ ctx: &CompletionContext<'_, 'db>,
+ dot_access: &DotAccess<'db>,
+ import_assets: ImportAssets<'db>,
position: SyntaxNode,
potential_import_name: String,
) -> Option<()> {
@@ -378,7 +378,7 @@ fn import_on_the_fly_method(
Some(())
}
-fn filter_excluded_flyimport(ctx: &CompletionContext<'_>, import: &LocatedImport) -> bool {
+fn filter_excluded_flyimport(ctx: &CompletionContext<'_, '_>, import: &LocatedImport) -> bool {
let def = import.item_to_import.into_module_def();
let is_exclude_flyimport = ctx.exclude_flyimport.get(&def).copied();
@@ -400,14 +400,14 @@ fn filter_excluded_flyimport(ctx: &CompletionContext<'_>, import: &LocatedImport
true
}
-fn import_name(ctx: &CompletionContext<'_>) -> String {
+fn import_name(ctx: &CompletionContext<'_, '_>) -> String {
let token_kind = ctx.token.kind();
if token_kind.is_any_identifier() { ctx.token.to_string() } else { String::new() }
}
fn import_assets_for_path<'db>(
- ctx: &CompletionContext<'db>,
+ ctx: &CompletionContext<'_, 'db>,
path: Option<&ast::Path>,
potential_import_name: &str,
qualifier: Option<ast::Path>,
diff --git a/crates/ide-completion/src/completions/fn_param.rs b/crates/ide-completion/src/completions/fn_param.rs
index bd0b69215c..f1e8e5f39e 100644
--- a/crates/ide-completion/src/completions/fn_param.rs
+++ b/crates/ide-completion/src/completions/fn_param.rs
@@ -22,7 +22,7 @@ use crate::{
/// Also complete parameters for closure or local functions from the surrounding defined locals.
pub(crate) fn complete_fn_param(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
pattern_ctx: &PatternContext,
) -> Option<()> {
let (ParamContext { param_list, kind, param, .. }, impl_or_trait) = match pattern_ctx {
@@ -78,7 +78,7 @@ pub(crate) fn complete_fn_param(
}
fn fill_fn_params(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
function: &ast::Fn,
param_list: &ast::ParamList,
current_param: &ast::Param,
@@ -139,7 +139,7 @@ fn fill_fn_params(
}
fn params_from_stmt_list_scope(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
stmt_list: ast::StmtList,
mut cb: impl FnMut(hir::Name, String),
) {
@@ -196,7 +196,7 @@ fn should_add_self_completions(
}
}
-fn comma_wrapper(ctx: &CompletionContext<'_>) -> Option<(impl Fn(&str) -> SmolStr, TextRange)> {
+fn comma_wrapper(ctx: &CompletionContext<'_, '_>) -> Option<(impl Fn(&str) -> SmolStr, TextRange)> {
let param =
ctx.original_token.parent_ancestors().find(|node| node.kind() == SyntaxKind::PARAM)?;
diff --git a/crates/ide-completion/src/completions/format_string.rs b/crates/ide-completion/src/completions/format_string.rs
index eaacd8d1a8..1e52402185 100644
--- a/crates/ide-completion/src/completions/format_string.rs
+++ b/crates/ide-completion/src/completions/format_string.rs
@@ -10,7 +10,7 @@ use crate::{CompletionItem, CompletionItemKind, Completions, context::Completion
/// Complete identifiers in format strings.
pub(crate) fn format_string(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
original: &ast::String,
expanded: &ast::String,
) {
diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs
index 39048e4400..1b26cab263 100644
--- a/crates/ide-completion/src/completions/item_list.rs
+++ b/crates/ide-completion/src/completions/item_list.rs
@@ -9,7 +9,7 @@ pub(crate) mod trait_impl;
pub(crate) fn complete_item_list_in_expr(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
expr_ctx: &PathExprCtx<'_>,
) {
@@ -24,7 +24,7 @@ pub(crate) fn complete_item_list_in_expr(
pub(crate) fn complete_item_list(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
kind: &ItemListKind,
) {
@@ -72,7 +72,11 @@ pub(crate) fn complete_item_list(
}
}
-fn add_keywords(acc: &mut Completions, ctx: &CompletionContext<'_>, kind: Option<&ItemListKind>) {
+fn add_keywords(
+ acc: &mut Completions,
+ ctx: &CompletionContext<'_, '_>,
+ kind: Option<&ItemListKind>,
+) {
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
let in_item_list = matches!(kind, Some(ItemListKind::SourceFile | ItemListKind::Module) | None);
diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 4072f05a41..c165a32082 100644
--- a/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -38,10 +38,14 @@ use ide_db::{
syntax_helpers::prettify_macro_expansion, traits::get_missing_assoc_items,
};
use syntax::ast::HasGenericParams;
+use syntax::syntax_editor::{Position, SyntaxEditor};
use syntax::{
AstNode, SmolStr, SyntaxElement, SyntaxKind, T, TextRange, ToSmolStr,
- ast::{self, HasGenericArgs, HasTypeBounds, edit_in_place::AttrsOwnerEdit, make},
- format_smolstr, ted,
+ ast::{
+ self, HasGenericArgs, HasTypeBounds,
+ edit::{AstNodeEdit, AttrsOwnerEdit},
+ },
+ format_smolstr,
};
use crate::{
@@ -59,7 +63,7 @@ enum ImplCompletionKind {
pub(crate) fn complete_trait_impl_const(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
name: &Option<ast::Name>,
) -> Option<()> {
complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::Const)
@@ -67,7 +71,7 @@ pub(crate) fn complete_trait_impl_const(
pub(crate) fn complete_trait_impl_type_alias(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
name: &Option<ast::Name>,
) -> Option<()> {
complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::TypeAlias)
@@ -75,7 +79,7 @@ pub(crate) fn complete_trait_impl_type_alias(
pub(crate) fn complete_trait_impl_fn(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
name: &Option<ast::Name>,
) -> Option<()> {
complete_trait_impl_name(acc, ctx, name, ImplCompletionKind::Fn)
@@ -83,7 +87,7 @@ pub(crate) fn complete_trait_impl_fn(
fn complete_trait_impl_name(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
name: &Option<ast::Name>,
kind: ImplCompletionKind,
) -> Option<()> {
@@ -122,7 +126,7 @@ fn complete_trait_impl_name(
pub(crate) fn complete_trait_impl_item_by_name(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
name_ref: &Option<ast::NameRef>,
impl_: &Option<ast::Impl>,
@@ -149,7 +153,7 @@ pub(crate) fn complete_trait_impl_item_by_name(
fn complete_trait_impl(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
kind: ImplCompletionKind,
replacement_range: TextRange,
impl_def: &ast::Impl,
@@ -178,7 +182,7 @@ fn complete_trait_impl(
fn add_function_impl(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
replacement_range: TextRange,
func: hir::Function,
impl_def: hir::Impl,
@@ -198,7 +202,7 @@ fn add_function_impl(
fn add_function_impl_(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
replacement_range: TextRange,
func: hir::Function,
impl_def: hir::Impl,
@@ -233,13 +237,14 @@ fn add_function_impl_(
get_transformed_fn(ctx, source.value, impl_def, async_sugaring)
{
let function_decl = function_declaration(ctx, &transformed_fn, source.file_id.macro_file());
+ let ws = if function_decl.contains('\n') { "\n" } else { " " };
match ctx.config.snippet_cap {
Some(cap) => {
- let snippet = format!("{function_decl} {{\n $0\n}}");
+ let snippet = format!("{function_decl}{ws}{{\n $0\n}}");
item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet));
}
None => {
- let header = format!("{function_decl} {{");
+ let header = format!("{function_decl}{ws}{{");
item.text_edit(TextEdit::replace(replacement_range, header));
}
};
@@ -257,70 +262,65 @@ enum AsyncSugaring {
/// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc.
fn get_transformed_assoc_item(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
assoc_item: ast::AssocItem,
impl_def: hir::Impl,
) -> Option<ast::AssocItem> {
let trait_ = impl_def.trait_(ctx.db)?;
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,
- source_scope,
- trait_,
- ctx.sema.source(impl_def)?.value,
- );
+ let impl_source = ctx.sema.source(impl_def)?;
+ let target_scope = &ctx.sema.scope(impl_source.syntax().value)?;
+ let transform =
+ PathTransform::trait_impl(target_scope, source_scope, trait_, impl_source.value);
- let assoc_item = assoc_item.clone_for_update();
// FIXME: Paths in nested macros are not handled well. See
// `macro_generated_assoc_item2` test.
let assoc_item = ast::AssocItem::cast(transform.apply(assoc_item.syntax()))?;
- assoc_item.remove_attrs_and_docs();
- Some(assoc_item)
+ let (editor, assoc_item) = SyntaxEditor::with_ast_node(&assoc_item);
+ assoc_item.remove_attrs_and_docs(&editor);
+ ast::AssocItem::cast(editor.finish().new_root().clone())
}
/// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc.
fn get_transformed_fn(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
fn_: ast::Fn,
impl_def: hir::Impl,
async_: AsyncSugaring,
) -> Option<ast::Fn> {
let trait_ = impl_def.trait_(ctx.db)?;
let source_scope = &ctx.sema.scope(fn_.syntax())?;
- let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?;
- let transform = PathTransform::trait_impl(
- target_scope,
- source_scope,
- trait_,
- ctx.sema.source(impl_def)?.value,
- );
+ let impl_source = ctx.sema.source(impl_def)?;
+ let target_scope = &ctx.sema.scope(impl_source.syntax().value)?;
+ let transform =
+ PathTransform::trait_impl(target_scope, source_scope, trait_, impl_source.value);
- let fn_ = fn_.clone_for_update();
+ let fn_ = fn_.reset_indent();
// FIXME: Paths in nested macros are not handled well. See
// `macro_generated_assoc_item2` test.
let fn_ = ast::Fn::cast(transform.apply(fn_.syntax()))?;
- fn_.remove_attrs_and_docs();
+ let (editor, fn_) = SyntaxEditor::with_ast_node(&fn_);
+ let factory = editor.make();
+ fn_.remove_attrs_and_docs(&editor);
match async_ {
AsyncSugaring::Desugar => {
match fn_.ret_type() {
Some(ret_ty) => {
let ty = ret_ty.ty()?;
- ted::replace(
+ editor.replace(
ty.syntax(),
- make::ty(&format!("impl Future<Output = {ty}>"))
- .syntax()
- .clone_for_update(),
+ factory.ty(&format!("impl Future<Output = {ty}>")).syntax(),
+ );
+ }
+ None => {
+ let ret_type = factory.ret_type(factory.ty("impl Future<Output = ()>"));
+ editor.insert_with_whitespace(
+ Position::after(fn_.param_list()?.syntax()),
+ ret_type.syntax(),
);
}
- None => ted::append_child(
- fn_.param_list()?.syntax(),
- make::ret_type(make::ty("impl Future<Output = ()>"))
- .syntax()
- .clone_for_update(),
- ),
}
- fn_.async_token().unwrap().detach();
+ editor.delete(fn_.async_token()?);
}
AsyncSugaring::Resugar => {
let ty = fn_.ret_type()?.ty()?;
@@ -347,23 +347,26 @@ fn get_transformed_fn(
if let ast::Type::TupleType(ty) = &output
&& ty.fields().next().is_none()
{
- ted::remove(fn_.ret_type()?.syntax());
+ editor.delete(fn_.ret_type()?.syntax());
} else {
- ted::replace(ty.syntax(), output.syntax());
+ editor.replace(ty.syntax(), output.syntax());
}
}
_ => (),
}
- ted::prepend_child(fn_.syntax(), make::token(T![async]));
+ editor.insert_with_whitespace(
+ Position::first_child_of(fn_.syntax()),
+ factory.token(T![async]),
+ );
}
AsyncSugaring::Async | AsyncSugaring::Plain => (),
}
- Some(fn_)
+ ast::Fn::cast(editor.finish().new_root().clone())
}
fn add_type_alias_impl(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
replacement_range: TextRange,
type_alias: hir::TypeAlias,
impl_def: hir::Impl,
@@ -444,7 +447,7 @@ fn add_type_alias_impl(
fn add_const_impl(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
replacement_range: TextRange,
const_: hir::Const,
impl_def: hir::Impl,
@@ -486,13 +489,13 @@ fn add_const_impl(
}
fn make_const_compl_syntax(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
const_: &ast::Const,
macro_file: Option<MacroCallId>,
) -> SmolStr {
let const_ = if let Some(macro_file) = macro_file {
let span_map = ctx.db.expansion_span_map(macro_file);
- prettify_macro_expansion(ctx.db, const_.syntax().clone(), &span_map, ctx.krate.into())
+ prettify_macro_expansion(ctx.db, const_.syntax().clone(), span_map, ctx.krate.into())
} else {
const_.syntax().clone()
};
@@ -514,13 +517,13 @@ fn make_const_compl_syntax(
}
fn function_declaration(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
node: &ast::Fn,
macro_file: Option<MacroCallId>,
) -> String {
let node = if let Some(macro_file) = macro_file {
let span_map = ctx.db.expansion_span_map(macro_file);
- prettify_macro_expansion(ctx.db, node.syntax().clone(), &span_map, ctx.krate.into())
+ prettify_macro_expansion(ctx.db, node.syntax().clone(), span_map, ctx.krate.into())
} else {
node.syntax().clone()
};
@@ -1256,7 +1259,7 @@ trait SomeTrait<T> {}
trait Foo<T> {
fn function()
- where Self: SomeTrait<T>;
+ where Self: SomeTrait<T>;
}
struct Bar;
@@ -1269,13 +1272,14 @@ trait SomeTrait<T> {}
trait Foo<T> {
fn function()
- where Self: SomeTrait<T>;
+ where Self: SomeTrait<T>;
}
struct Bar;
impl Foo<u32> for Bar {
fn function()
- where Self: SomeTrait<u32> {
+where Self: SomeTrait<u32>
+{
$0
}
}
@@ -1356,7 +1360,7 @@ noop! {
struct Test;
impl Foo for Test {
- fn foo(&mut self,bar:i64,baz: &mut u32) -> Result<(),u32> {
+ fn foo(&mut self,bar: i64,baz: &mut u32) -> Result<(),u32> {
$0
}
}
@@ -1740,7 +1744,7 @@ impl Trait for () {
me fn bar(..)
me fn baz(..)
me fn foo(..)
- md proc_macros
+ md proc_macros::
kw crate::
kw self::
"#]],
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs
index fbb3cde968..f7dd1589ae 100644
--- a/crates/ide-completion/src/completions/keyword.rs
+++ b/crates/ide-completion/src/completions/keyword.rs
@@ -6,7 +6,7 @@ use crate::{CompletionContext, Completions};
pub(crate) fn complete_for_and_where(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
keyword_item: &ast::Item,
) {
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
diff --git a/crates/ide-completion/src/completions/lifetime.rs b/crates/ide-completion/src/completions/lifetime.rs
index 8902cd09ce..6291b42a03 100644
--- a/crates/ide-completion/src/completions/lifetime.rs
+++ b/crates/ide-completion/src/completions/lifetime.rs
@@ -17,7 +17,7 @@ use crate::{
/// Completes lifetimes.
pub(crate) fn complete_lifetime(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
lifetime_ctx: &LifetimeContext,
) {
let &LifetimeContext { kind: LifetimeKind::Lifetime { in_lifetime_param_bound, def }, .. } =
@@ -44,7 +44,7 @@ pub(crate) fn complete_lifetime(
/// Completes labels.
pub(crate) fn complete_label(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
lifetime_ctx: &LifetimeContext,
) {
if !matches!(lifetime_ctx, LifetimeContext { kind: LifetimeKind::LabelRef, .. }) {
diff --git a/crates/ide-completion/src/completions/macro_def.rs b/crates/ide-completion/src/completions/macro_def.rs
index 2c8e7a2e62..884b4dd966 100644
--- a/crates/ide-completion/src/completions/macro_def.rs
+++ b/crates/ide-completion/src/completions/macro_def.rs
@@ -4,7 +4,7 @@ use ide_db::SymbolKind;
use crate::{CompletionItem, Completions, context::CompletionContext};
-pub(crate) fn complete_macro_segment(acc: &mut Completions, ctx: &CompletionContext<'_>) {
+pub(crate) fn complete_macro_segment(acc: &mut Completions, ctx: &CompletionContext<'_, '_>) {
for &label in MACRO_SEGMENTS {
let item =
CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), label, ctx.edition);
diff --git a/crates/ide-completion/src/completions/mod_.rs b/crates/ide-completion/src/completions/mod_.rs
index 3333300045..a8c6c82aac 100644
--- a/crates/ide-completion/src/completions/mod_.rs
+++ b/crates/ide-completion/src/completions/mod_.rs
@@ -14,7 +14,7 @@ use crate::{CompletionItem, Completions, context::CompletionContext};
/// Complete mod declaration, i.e. `mod $0;`
pub(crate) fn complete_mod(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
mod_under_caret: &ast::Module,
) -> Option<()> {
if mod_under_caret.item_list().is_some() {
diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs
index e7597bf95c..7b887fb7d7 100644
--- a/crates/ide-completion/src/completions/pattern.rs
+++ b/crates/ide-completion/src/completions/pattern.rs
@@ -12,7 +12,7 @@ use crate::{
/// Completes constants and paths in unqualified patterns.
pub(crate) fn complete_pattern(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
pattern_ctx: &PatternContext,
) {
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
@@ -128,7 +128,7 @@ pub(crate) fn complete_pattern(
pub(crate) fn complete_pattern_path(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
) {
match qualified {
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs
index 82baf885dd..0cb39dd108 100644
--- a/crates/ide-completion/src/completions/postfix.rs
+++ b/crates/ide-completion/src/completions/postfix.rs
@@ -12,7 +12,7 @@ use ide_db::{
text_edit::TextEdit,
ty_filter::TryEnum,
};
-use itertools::Itertools;
+use itertools::{Either, Itertools};
use stdx::never;
use syntax::{
SmolStr,
@@ -31,7 +31,7 @@ use crate::{
pub(crate) fn complete_postfix(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
dot_access: &DotAccess<'_>,
) {
if !ctx.config.enable_postfix_completions {
@@ -84,15 +84,15 @@ pub(crate) fn complete_postfix(
let mut item = postfix_snippet(
"drop",
"fn drop(&mut self)",
- &format!("{path}($0{receiver_text})", path = path.display(ctx.db, ctx.edition)),
+ format!("{path}($0{receiver_text})", path = path.display(ctx.db, ctx.edition)),
);
item.set_documentation(drop_fn.docs(ctx.db));
item.add_to(acc, ctx.db);
}
- 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);
- postfix_snippet("deref", "*expr", &format!("*{receiver_text}")).add_to(acc, ctx.db);
+ 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);
+ postfix_snippet("deref", "*expr", format!("*{receiver_text}")).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
@@ -110,15 +110,47 @@ pub(crate) fn complete_postfix(
add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text);
}
- postfix_snippet("box", "Box::new(expr)", &format!("Box::new({receiver_text})"))
+ 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})"))
+ 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(expected_ty) = ctx.expected_type.as_ref()
+ && let Some(adt) = expected_ty.as_adt()
+ {
+ let is_valid_new = expected_ty
+ .iterate_assoc_items(ctx.db, |item| {
+ if let hir::AssocItem::Function(func) = item
+ && func.name(ctx.db) == hir::sym::new
+ && !func.has_self_param(ctx.db)
+ {
+ let params = func.params_without_self(ctx.db);
+ if params.len() == 1 {
+ return Some(());
+ }
+ }
+ None
+ })
+ .is_some();
+
+ let adt = hir::ModuleDef::from(adt);
+ if is_valid_new && let Some(path) = ctx.module.find_path(ctx.db, adt, cfg) {
+ let ty_name = path.display(ctx.db, ctx.display_target.edition).to_smolstr();
+
+ postfix_snippet(
+ "new",
+ &format_smolstr!("{}::new(expr)", ty_name),
+ format!("{}::new({}$0)", ty_name, receiver_text),
+ )
+ .add_to(acc, ctx.db);
+ }
+ }
+
let try_enum = TryEnum::from_ty(&ctx.sema, receiver_ty);
let is_in_cond = is_in_condition(&dot_receiver_including_refs);
+ let is_in_value = is_in_value(&dot_receiver_including_refs);
if let Some(parent) = dot_receiver_including_refs.syntax().parent() {
let placeholder = suggest_receiver_name(dot_receiver, "0", &ctx.sema);
match &try_enum {
@@ -127,13 +159,13 @@ pub(crate) fn complete_postfix(
postfix_snippet(
"let",
"let Ok(_)",
- &format!("let Ok({placeholder}) = {receiver_text}"),
+ format!("let Ok({placeholder}) = {receiver_text}"),
)
.add_to(acc, ctx.db);
postfix_snippet(
"letm",
"let Ok(mut _)",
- &format!("let Ok(mut {placeholder}) = {receiver_text}"),
+ format!("let Ok(mut {placeholder}) = {receiver_text}"),
)
.add_to(acc, ctx.db);
}
@@ -141,38 +173,38 @@ pub(crate) fn complete_postfix(
postfix_snippet(
"let",
"let Some(_)",
- &format!("let Some({placeholder}) = {receiver_text}"),
+ format!("let Some({placeholder}) = {receiver_text}"),
)
.add_to(acc, ctx.db);
postfix_snippet(
"letm",
"let Some(mut _)",
- &format!("let Some(mut {placeholder}) = {receiver_text}"),
+ format!("let Some(mut {placeholder}) = {receiver_text}"),
)
.add_to(acc, ctx.db);
}
},
_ if is_in_cond => {
- postfix_snippet("let", "let", &format!("let $1 = {receiver_text}"))
+ postfix_snippet("let", "let", format!("let $1 = {receiver_text}"))
.add_to(acc, ctx.db);
}
_ if matches!(parent.kind(), STMT_LIST | EXPR_STMT) => {
- postfix_snippet("let", "let", &format!("let $0 = {receiver_text}{semi}"))
+ postfix_snippet("let", "let", format!("let $0 = {receiver_text}{semi}"))
.add_to(acc, ctx.db);
- postfix_snippet("letm", "let mut", &format!("let mut $0 = {receiver_text}{semi}"))
+ postfix_snippet("letm", "let mut", format!("let mut $0 = {receiver_text}{semi}"))
.add_to(acc, ctx.db);
}
_ if matches!(parent.kind(), MATCH_ARM | CLOSURE_EXPR) => {
postfix_snippet(
"let",
"let",
- &format!("{{\n let $1 = {receiver_text};\n $0\n}}"),
+ format!("{{\n let $1 = {receiver_text};\n $0\n}}"),
)
.add_to(acc, ctx.db);
postfix_snippet(
"letm",
"let mut",
- &format!("{{\n let mut $1 = {receiver_text};\n $0\n}}"),
+ format!("{{\n let mut $1 = {receiver_text};\n $0\n}}"),
)
.add_to(acc, ctx.db);
}
@@ -187,7 +219,7 @@ pub(crate) fn complete_postfix(
postfix_snippet(
"match",
"match expr {}",
- &format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"),
+ format!("match {receiver_text} {{\n Ok(${{1:_}}) => {{$2}},\n Err(${{3:_}}) => {{$0}},\n}}"),
)
.add_to(acc, ctx.db);
}
@@ -195,7 +227,7 @@ pub(crate) fn complete_postfix(
postfix_snippet(
"match",
"match expr {}",
- &format!(
+ format!(
"match {receiver_text} {{\n Some(${{1:_}}) => {{$2}},\n None => {{$0}},\n}}"
),
)
@@ -206,35 +238,35 @@ pub(crate) fn complete_postfix(
postfix_snippet(
"match",
"match expr {}",
- &format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"),
+ format!("match {receiver_text} {{\n ${{1:_}} => {{$0}},\n}}"),
)
.add_to(acc, ctx.db);
}
}
if let Some(try_enum) = &try_enum {
let placeholder = suggest_receiver_name(dot_receiver, "1", &ctx.sema);
+ let if_then_snip =
+ if is_in_value { "{\n $2\n} else {\n $0\n}" } else { "{\n $0\n}" };
match try_enum {
TryEnum::Result => {
postfix_snippet(
"ifl",
"if let Ok {}",
- &format!("if let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"),
+ format!("if let Ok({placeholder}) = {receiver_text} {if_then_snip}"),
)
.add_to(acc, ctx.db);
postfix_snippet(
"lete",
"let Ok else {}",
- &format!(
- "let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0"
- ),
+ format!("let Ok({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0"),
)
.add_to(acc, ctx.db);
postfix_snippet(
"while",
"while let Ok {}",
- &format!("while let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"),
+ format!("while let Ok({placeholder}) = {receiver_text} {{\n $0\n}}"),
)
.add_to(acc, ctx.db);
}
@@ -242,14 +274,14 @@ pub(crate) fn complete_postfix(
postfix_snippet(
"ifl",
"if let Some {}",
- &format!("if let Some({placeholder}) = {receiver_text} {{\n $0\n}}"),
+ format!("if let Some({placeholder}) = {receiver_text} {if_then_snip}"),
)
.add_to(acc, ctx.db);
postfix_snippet(
"lete",
"let Some else {}",
- &format!(
+ format!(
"let Some({placeholder}) = {receiver_text} else {{\n $2\n}};\n$0"
),
)
@@ -258,18 +290,20 @@ pub(crate) fn complete_postfix(
postfix_snippet(
"while",
"while let Some {}",
- &format!("while let Some({placeholder}) = {receiver_text} {{\n $0\n}}"),
+ format!("while let Some({placeholder}) = {receiver_text} {{\n $0\n}}"),
)
.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}}"))
+ let if_then_snip =
+ if is_in_value { "{\n $1\n} else {\n $0\n}" } else { "{\n $0\n}" };
+ postfix_snippet("if", "if expr {}", format!("if {receiver_text} {if_then_snip}"))
.add_to(acc, ctx.db);
postfix_snippet(
"while",
"while expr {}",
- &format!("while {receiver_text} {{\n $0\n}}"),
+ format!("while {receiver_text} {{\n $0\n}}"),
)
.add_to(acc, ctx.db);
} else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator()
@@ -278,14 +312,14 @@ pub(crate) fn complete_postfix(
postfix_snippet(
"for",
"for ele in expr {}",
- &format!("for ele in {receiver_text} {{\n $0\n}}"),
+ format!("for ele in {receiver_text} {{\n $0\n}}"),
)
.add_to(acc, ctx.db);
}
}
if receiver_ty.is_bool() || receiver_ty.is_unknown() {
- postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db);
+ postfix_snippet("not", "!expr", format!("!{receiver_text}")).add_to(acc, ctx.db);
}
let block_should_be_wrapped = if let ast::Expr::BlockExpr(block) = dot_receiver {
@@ -300,11 +334,11 @@ pub(crate) fn complete_postfix(
let (open_paren, close_paren) = if is_in_cond { ("(", ")") } else { ("", "") };
let unsafe_completion_string =
format!("{open_paren}unsafe {open_brace}{receiver_text}{close_brace}{close_paren}");
- postfix_snippet("unsafe", "unsafe {}", &unsafe_completion_string).add_to(acc, ctx.db);
+ postfix_snippet("unsafe", "unsafe {}", unsafe_completion_string).add_to(acc, ctx.db);
let const_completion_string =
format!("{open_paren}const {open_brace}{receiver_text}{close_brace}{close_paren}");
- postfix_snippet("const", "const {}", &const_completion_string).add_to(acc, ctx.db);
+ postfix_snippet("const", "const {}", const_completion_string).add_to(acc, ctx.db);
}
if let ast::Expr::Literal(literal) = dot_receiver.clone()
@@ -313,11 +347,11 @@ pub(crate) fn complete_postfix(
add_format_like_completions(acc, ctx, dot_receiver, cap, &literal_text, semi);
}
- postfix_snippet("return", "return expr", &format!("return {receiver_text}{semi}"))
+ postfix_snippet("return", "return expr", format!("return {receiver_text}{semi}"))
.add_to(acc, ctx.db);
if let Some(BreakableKind::Block | BreakableKind::Loop) = expr_ctx.in_breakable {
- postfix_snippet("break", "break expr", &format!("break {receiver_text}{semi}"))
+ postfix_snippet("break", "break expr", format!("break {receiver_text}{semi}"))
.add_to(acc, ctx.db);
}
}
@@ -364,7 +398,7 @@ fn get_receiver_text(
}
let file_text = sema.db.file_text(range.file_id.file_id(sema.db));
let text = file_text.text(sema.db);
- let indent_spaces = indent_of_tail_line(&text[TextRange::up_to(range.range.start())]);
+ let indent_spaces = indent_of_tail_line(&text[TextRange::up_to(range.range.end())]);
let mut text = stdx::dedent_by(indent_spaces, &text[range.range]);
// The receiver texts should be interpreted as-is, as they are expected to be
@@ -434,6 +468,11 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
.syntax()
.children_with_tokens()
.filter(|it| Some(it) != last_child_or_token.as_ref())
+ .flat_map(|it| {
+ let has_ws = it.next_sibling_or_token().is_some_and(|it| it.kind().is_trivia());
+ let need_ws = !has_ws && it.kind().is_any_identifier();
+ itertools::chain([Either::Left(it)], need_ws.then_some(Either::Right(" ")))
+ })
.format("")
.to_smolstr()
.as_str(),
@@ -445,10 +484,10 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
}
fn build_postfix_snippet_builder<'ctx>(
- ctx: &'ctx CompletionContext<'_>,
+ ctx: &'ctx CompletionContext<'_, '_>,
cap: SnippetCap,
receiver: &'ctx ast::Expr,
-) -> Option<impl Fn(&str, &str, &str) -> Builder + 'ctx> {
+) -> Option<impl Fn(&str, &str, String) -> Builder + 'ctx> {
let receiver_range = ctx.sema.original_range_opt(receiver.syntax())?.range;
if ctx.source_range().end() < receiver_range.start() {
// This shouldn't happen, yet it does. I assume this might be due to an incorrect token
@@ -461,12 +500,12 @@ fn build_postfix_snippet_builder<'ctx>(
// Wrapping impl Fn in an option ruins lifetime inference for the parameters in a way that
// can't be annotated for the closure, hence fix it by constructing it without the Option first
fn build<'ctx>(
- ctx: &'ctx CompletionContext<'_>,
+ ctx: &'ctx CompletionContext<'_, '_>,
cap: SnippetCap,
delete_range: TextRange,
- ) -> impl Fn(&str, &str, &str) -> Builder + 'ctx {
+ ) -> impl Fn(&str, &str, String) -> Builder + 'ctx {
move |label, detail, snippet| {
- let edit = TextEdit::replace(delete_range, snippet.to_owned());
+ let edit = TextEdit::replace(delete_range, snippet);
let mut item = CompletionItem::new(
CompletionItemKind::Snippet,
ctx.source_range(),
@@ -491,8 +530,8 @@ fn build_postfix_snippet_builder<'ctx>(
fn add_custom_postfix_completions(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
- postfix_snippet: impl Fn(&str, &str, &str) -> Builder,
+ ctx: &CompletionContext<'_, '_>,
+ postfix_snippet: impl Fn(&str, &str, String) -> Builder,
receiver_text: &str,
) -> Option<()> {
ImportScope::find_insert_use_container(&ctx.token.parent()?, &ctx.sema)?;
@@ -503,9 +542,10 @@ fn add_custom_postfix_completions(
None => return,
};
let body = snippet.postfix_snippet(receiver_text);
+ let document = Documentation::new_owned(format!("```rust\n{body}\n```"));
let mut builder =
- postfix_snippet(trigger, snippet.description.as_deref().unwrap_or_default(), &body);
- builder.documentation(Documentation::new_owned(format!("```rust\n{body}\n```")));
+ postfix_snippet(trigger, snippet.description.as_deref().unwrap_or_default(), body);
+ builder.documentation(document);
for import in imports.into_iter() {
builder.add_import(import);
}
@@ -533,6 +573,22 @@ pub(crate) fn is_in_condition(it: &ast::Expr) -> bool {
.unwrap_or(false)
}
+pub(crate) fn is_in_value(it: &ast::Expr) -> bool {
+ let Some(node) = it.syntax().parent() else { return false };
+ let kind = node.kind();
+ ast::LetStmt::can_cast(kind)
+ || ast::ArgList::can_cast(kind)
+ || ast::ArrayExpr::can_cast(kind)
+ || ast::ParenExpr::can_cast(kind)
+ || ast::BreakExpr::can_cast(kind)
+ || ast::ReturnExpr::can_cast(kind)
+ || ast::PrefixExpr::can_cast(kind)
+ || ast::FormatArgsArg::can_cast(kind)
+ || ast::RecordExprField::can_cast(kind)
+ || ast::BinExpr::cast(node.clone()).is_some_and(|expr| expr.rhs().as_ref() == Some(it))
+ || ast::IndexExpr::cast(node).is_some_and(|expr| expr.index().as_ref() == Some(it))
+}
+
#[cfg(test)]
mod tests {
use expect_test::expect;
@@ -1148,6 +1204,66 @@ fn main() {
}
#[test]
+ fn postfix_completion_if_else_in_value() {
+ check_edit(
+ "if",
+ r#"
+fn main() {
+ let s = cond.is_some().$0;
+}
+"#,
+ r#"
+fn main() {
+ let s = if cond.is_some() {
+ $1
+} else {
+ $0
+};
+}
+"#,
+ );
+
+ check_edit(
+ "ifl",
+ r#"
+//- minicore: option
+fn main() {
+ let cond = Some("x");
+ let s = cond.$0;
+}
+"#,
+ r#"
+fn main() {
+ let cond = Some("x");
+ let s = if let Some(${1:cond}) = cond {
+ $2
+} else {
+ $0
+};
+}
+"#,
+ );
+
+ check_edit(
+ "if",
+ r#"
+fn main() {
+ 2 + true.$0;
+}
+"#,
+ r#"
+fn main() {
+ 2 + if true {
+ $1
+} else {
+ $0
+};
+}
+"#,
+ );
+ }
+
+ #[test]
fn postfix_completion_for_unsafe() {
postfix_completion_for_block("unsafe");
}
@@ -1186,7 +1302,9 @@ fn main() {
);
check_edit(
kind,
- r#"fn main() { for i in 0..10 {}.$0 }"#,
+ r#"
+//- minicore: iterator
+fn main() { for i in 0..10 {}.$0 }"#,
&format!("fn main() {{ {kind} {{ for i in 0..10 {{}} }} }}"),
);
check_edit(
@@ -1456,6 +1574,15 @@ fn main() {
r#"fn main() { &raw const Foo::bar::SOME_CONST.$0 }"#,
r#"fn main() { (&raw const Foo::bar::SOME_CONST) }"#,
);
+
+ check_edit_with_config(
+ CompletionConfig { snippets: vec![snippet.clone()], ..TEST_CONFIG },
+ "group",
+ r#"macro_rules! id { ($($t:tt)*) => ($($t)*); }
+fn main() { id!(&raw const Foo::bar::SOME_CONST.$0) }"#,
+ r#"macro_rules! id { ($($t:tt)*) => ($($t)*); }
+fn main() { id!((&raw const Foo::bar::SOME_CONST)) }"#,
+ );
}
#[test]
@@ -1545,7 +1672,9 @@ fn foo(x: Option<i32>, y: Option<i32>) {
let _f = || {
x
.and(y)
- .map(|it| it+2)
+ .map(|it| {
+ it+2
+ })
.$0
};
}
@@ -1554,11 +1683,74 @@ fn foo(x: Option<i32>, y: Option<i32>) {
fn foo(x: Option<i32>, y: Option<i32>) {
let _f = || {
let $0 = x
- .and(y)
- .map(|it| it+2);
+.and(y)
+.map(|it| {
+ it+2
+});
};
}
"#,
);
}
+
+ #[test]
+ fn postfix_new() {
+ check_edit(
+ "new",
+ r#"
+struct OtherThing;
+struct RefCell<T>(T);
+impl<T> RefCell<T> {
+ fn new(t: T) -> Self { RefCell(t) }
+}
+
+fn main() {
+ let other_thing = OtherThing;
+ let thing: RefCell<OtherThing> = other_thing.$0;
+}
+"#,
+ r#"
+struct OtherThing;
+struct RefCell<T>(T);
+impl<T> RefCell<T> {
+ fn new(t: T) -> Self { RefCell(t) }
+}
+
+fn main() {
+ let other_thing = OtherThing;
+ let thing: RefCell<OtherThing> = RefCell::new(other_thing$0);
+}
+"#,
+ );
+
+ check_edit(
+ "new",
+ r#"
+mod foo {
+ pub struct OtherThing;
+ pub struct RefCell<T>(T);
+ impl<T> RefCell<T> {
+ pub fn new(t: T) -> Self { RefCell(t) }
+ }
+}
+
+fn main() {
+ let thing: foo::RefCell<foo::OtherThing> = foo::OtherThing.$0;
+}
+"#,
+ r#"
+mod foo {
+ pub struct OtherThing;
+ pub struct RefCell<T>(T);
+ impl<T> RefCell<T> {
+ pub fn new(t: T) -> Self { RefCell(t) }
+ }
+}
+
+fn main() {
+ let thing: foo::RefCell<foo::OtherThing> = foo::RefCell::new(foo::OtherThing$0);
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide-completion/src/completions/postfix/format_like.rs b/crates/ide-completion/src/completions/postfix/format_like.rs
index 85a8899fd1..3b22e8a266 100644
--- a/crates/ide-completion/src/completions/postfix/format_like.rs
+++ b/crates/ide-completion/src/completions/postfix/format_like.rs
@@ -44,7 +44,7 @@ static SNIPPET_RETURNS_NON_UNIT: &[&str] = &["format"];
pub(crate) fn add_format_like_completions(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
dot_receiver: &ast::Expr,
cap: SnippetCap,
receiver_text: &ast::String,
@@ -73,7 +73,7 @@ pub(crate) fn add_format_like_completions(
format!(r#"{}({}, {}){semi}"#, macro_name, out, exprs.join(", "))
};
- postfix_snippet(label, macro_name, &snippet).add_to(acc, ctx.db);
+ postfix_snippet(label, macro_name, snippet).add_to(acc, ctx.db);
}
}
}
diff --git a/crates/ide-completion/src/completions/ra_fixture.rs b/crates/ide-completion/src/completions/ra_fixture.rs
index 5a8881edc7..08ad37b7f2 100644
--- a/crates/ide-completion/src/completions/ra_fixture.rs
+++ b/crates/ide-completion/src/completions/ra_fixture.rs
@@ -14,7 +14,7 @@ use crate::{
pub(crate) fn complete_ra_fixture(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
original: &ast::String,
expanded: &ast::String,
) -> Option<()> {
diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs
index 12c564af5c..1238a91dad 100644
--- a/crates/ide-completion/src/completions/record.rs
+++ b/crates/ide-completion/src/completions/record.rs
@@ -13,7 +13,7 @@ use crate::{
pub(crate) fn complete_record_pattern_fields(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
pattern_ctx: &PatternContext,
) {
if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx {
@@ -44,7 +44,7 @@ pub(crate) fn complete_record_pattern_fields(
pub(crate) fn complete_record_expr_fields(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
record_expr: &ast::RecordExpr,
&dot_prefix: &bool,
) {
@@ -98,7 +98,7 @@ pub(crate) fn complete_record_expr_fields(
pub(crate) fn add_default_update(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
ty: Option<&hir::TypeInfo<'_>>,
) {
let default_trait = ctx.famous_defs().core_default_Default();
@@ -127,7 +127,7 @@ pub(crate) fn add_default_update(
fn complete_fields(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
missing_fields: Vec<(hir::Field, hir::Type<'_>)>,
) {
for (field, ty) in missing_fields {
diff --git a/crates/ide-completion/src/completions/snippet.rs b/crates/ide-completion/src/completions/snippet.rs
index 04450aea75..7432c5226b 100644
--- a/crates/ide-completion/src/completions/snippet.rs
+++ b/crates/ide-completion/src/completions/snippet.rs
@@ -10,7 +10,7 @@ use crate::{
pub(crate) fn complete_expr_snippet(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
&PathExprCtx { in_block_expr, .. }: &PathExprCtx<'_>,
) {
@@ -50,7 +50,7 @@ macro_rules! $1 {
pub(crate) fn complete_item_snippet(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
kind: &ItemListKind,
) {
@@ -117,7 +117,12 @@ macro_rules! $1 {
}
}
-fn snippet(ctx: &CompletionContext<'_>, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
+fn snippet(
+ ctx: &CompletionContext<'_, '_>,
+ cap: SnippetCap,
+ label: &str,
+ snippet: &str,
+) -> Builder {
let mut item =
CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), label, ctx.edition);
item.insert_snippet(cap, snippet);
@@ -126,7 +131,7 @@ fn snippet(ctx: &CompletionContext<'_>, cap: SnippetCap, label: &str, snippet: &
fn add_custom_completions(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
cap: SnippetCap,
scope: SnippetScope,
) -> Option<()> {
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index 20bbf0dd8b..0b2b6682aa 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -11,7 +11,7 @@ use crate::{
pub(crate) fn complete_type_path(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
location: &TypeLocation,
) {
@@ -217,7 +217,7 @@ pub(crate) fn complete_type_path(
pub(crate) fn complete_ascribed_type(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
ascription: &TypeAscriptionTarget,
) -> Option<()> {
diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs
index f39b641649..1ff7dd6def 100644
--- a/crates/ide-completion/src/completions/use_.rs
+++ b/crates/ide-completion/src/completions/use_.rs
@@ -12,7 +12,7 @@ use crate::{
pub(crate) fn complete_use_path(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx @ PathCompletionCtx { qualified, use_tree_parent, .. }: &PathCompletionCtx<'_>,
name_ref: &Option<ast::NameRef>,
) {
diff --git a/crates/ide-completion/src/completions/vis.rs b/crates/ide-completion/src/completions/vis.rs
index 28d906d91c..49a52f2986 100644
--- a/crates/ide-completion/src/completions/vis.rs
+++ b/crates/ide-completion/src/completions/vis.rs
@@ -7,7 +7,7 @@ use crate::{
pub(crate) fn complete_vis_path(
acc: &mut Completions,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
&has_in_token: &bool,
) {
diff --git a/crates/ide-completion/src/config.rs b/crates/ide-completion/src/config.rs
index 80c1572972..21f624be2c 100644
--- a/crates/ide-completion/src/config.rs
+++ b/crates/ide-completion/src/config.rs
@@ -25,6 +25,7 @@ pub struct CompletionConfig<'a> {
pub term_search_fuel: u64,
pub full_function_signatures: bool,
pub callable: Option<CallableSnippets>,
+ pub add_colons_to_module: bool,
pub add_semicolon_to_unit: bool,
pub snippet_cap: Option<SnippetCap>,
pub insert_use: InsertUseConfig,
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index b9520e9132..f7fced3f06 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -458,10 +458,10 @@ pub(crate) enum ParamKind {
/// `CompletionContext` is created early during completion to figure out, where
/// exactly is the cursor, syntax-wise.
#[derive(Debug)]
-pub(crate) struct CompletionContext<'a> {
- pub(crate) sema: Semantics<'a, RootDatabase>,
- pub(crate) scope: SemanticsScope<'a>,
- pub(crate) db: &'a RootDatabase,
+pub(crate) struct CompletionContext<'a, 'db> {
+ pub(crate) sema: Semantics<'db, RootDatabase>,
+ pub(crate) scope: SemanticsScope<'db>,
+ pub(crate) db: &'db RootDatabase,
pub(crate) config: &'a CompletionConfig<'a>,
pub(crate) position: FilePosition,
@@ -487,7 +487,7 @@ pub(crate) struct CompletionContext<'a> {
/// This is usually the parameter name of the function argument we are completing.
pub(crate) expected_name: Option<NameOrNameRef>,
/// The expected type of what we are completing.
- pub(crate) expected_type: Option<Type<'a>>,
+ pub(crate) expected_type: Option<Type<'db>>,
pub(crate) qualifier_ctx: QualifierCtx,
@@ -523,7 +523,7 @@ pub(crate) enum CompleteSemicolon {
CompleteComma,
}
-impl CompletionContext<'_> {
+impl<'db> CompletionContext<'_, 'db> {
/// The range of the identifier that is being completed.
pub(crate) fn source_range(&self) -> TextRange {
let kind = self.original_token.kind();
@@ -540,7 +540,7 @@ impl CompletionContext<'_> {
}
}
- pub(crate) fn famous_defs(&self) -> FamousDefs<'_, '_> {
+ pub(crate) fn famous_defs(&self) -> FamousDefs<'_, 'db> {
FamousDefs(&self.sema, self.krate)
}
@@ -732,13 +732,13 @@ impl CompletionContext<'_> {
}
// CompletionContext construction
-impl<'db> CompletionContext<'db> {
+impl<'a, 'db> CompletionContext<'a, 'db> {
pub(crate) fn new(
db: &'db RootDatabase,
position @ FilePosition { file_id, offset }: FilePosition,
- config: &'db CompletionConfig<'db>,
+ config: &'a CompletionConfig<'a>,
trigger_character: Option<char>,
- ) -> Option<(CompletionContext<'db>, CompletionAnalysis<'db>)> {
+ ) -> Option<(CompletionContext<'a, 'db>, CompletionAnalysis<'db>)> {
let _p = tracing::info_span!("CompletionContext::new").entered();
let sema = Semantics::new(db);
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 58c0f683a3..faeb97f93f 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -22,7 +22,7 @@ use syntax::{
};
use crate::{
- completions::postfix::is_in_condition,
+ completions::postfix::{is_in_condition, is_in_value},
context::{
AttrCtx, BreakableKind, COMPLETION_MARKER, CompletionAnalysis, DotAccess, DotAccessExprCtx,
DotAccessKind, ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind,
@@ -1097,25 +1097,6 @@ fn classify_name_ref<'db>(
.and_then(|next| next.first_token())
.is_some_and(|token| token.kind() == SyntaxKind::ELSE_KW)
};
- let is_in_value = |it: &SyntaxNode| {
- let Some(node) = it.parent() else { return false };
- let kind = node.kind();
- ast::LetStmt::can_cast(kind)
- || ast::ArgList::can_cast(kind)
- || ast::ArrayExpr::can_cast(kind)
- || ast::ParenExpr::can_cast(kind)
- || ast::BreakExpr::can_cast(kind)
- || ast::ReturnExpr::can_cast(kind)
- || ast::PrefixExpr::can_cast(kind)
- || ast::FormatArgsArg::can_cast(kind)
- || ast::RecordExprField::can_cast(kind)
- || ast::BinExpr::cast(node.clone())
- .and_then(|expr| expr.rhs())
- .is_some_and(|expr| expr.syntax() == it)
- || ast::IndexExpr::cast(node)
- .and_then(|expr| expr.index())
- .is_some_and(|expr| expr.syntax() == it)
- };
// We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
// ex. trait Foo $0 {}
@@ -1429,7 +1410,7 @@ fn classify_name_ref<'db>(
.find_map(ast::LetStmt::cast)
.is_some_and(|it| it.semicolon_token().is_none())
|| after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw;
- let in_value = is_in_value(it);
+ let in_value = is_in_value(&expr);
let impl_ = fetch_immediate_impl_or_trait(sema, original_file, expr.syntax())
.and_then(Either::left);
@@ -2094,12 +2075,12 @@ fn next_non_trivia_token(e: impl Into<SyntaxElement>) -> Option<SyntaxToken> {
}
fn next_non_trivia_sibling(ele: SyntaxElement) -> Option<SyntaxElement> {
- let mut e = ele.next_sibling_or_token();
- while let Some(inner) = e {
- if !inner.kind().is_trivia() {
- return Some(inner);
+ let mut e = ele;
+ while let Some(next) = e.next_sibling_or_token() {
+ if !next.kind().is_trivia() {
+ return Some(next);
} else {
- e = inner.next_sibling_or_token();
+ e = next;
}
}
None
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 6abf4f632a..cfadec6287 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -61,6 +61,9 @@ pub struct CompletionItem {
pub documentation: Option<Documentation<'static>>,
/// Whether this item is marked as deprecated
+ ///
+ /// NOTE: this field is used in the LSP protocol. For the use of this information in completion
+ /// scoring, see [`CompletionRelevance::is_deprecated`].
pub deprecated: bool,
/// If completing a function call, ask the editor to show parameter popup
@@ -194,6 +197,11 @@ pub struct CompletionRelevance {
pub is_skipping_completion: bool,
/// if inherent impl already exists in current module, user may not want to implement it again.
pub has_local_inherent_impl: bool,
+ /// Set when the completion item is deprecated.
+ ///
+ /// NOTE: This is duplicated from [`CompletionItem::deprecated`] in order to allow using this
+ /// information in the calculation of the relevance score.
+ pub is_deprecated: bool,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct CompletionRelevanceTraitInfo {
@@ -286,6 +294,7 @@ impl CompletionRelevance {
function,
is_skipping_completion,
has_local_inherent_impl,
+ is_deprecated,
} = self;
// only applicable for completions within use items
@@ -362,6 +371,11 @@ impl CompletionRelevance {
score -= 5;
}
+ // lower rank for deprecated items
+ if is_deprecated {
+ score -= 5;
+ }
+
score
}
@@ -521,7 +535,7 @@ pub(crate) struct Builder {
impl Builder {
pub(crate) fn from_resolution(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
local_name: hir::Name,
resolution: hir::ScopeDef,
@@ -590,6 +604,9 @@ impl Builder {
None => TextEdit::replace(self.source_range, insert_text),
};
+ // Copy `deprecated` to `self.relevance.is_deprecated`
+ let relevance = CompletionRelevance { is_deprecated: self.deprecated, ..self.relevance };
+
let import_to_add = self
.imports_to_add
.into_iter()
@@ -622,7 +639,7 @@ impl Builder {
kind: self.kind,
deprecated: self.deprecated,
trigger_call_info: self.trigger_call_info,
- relevance: self.relevance,
+ relevance,
ref_match: self.ref_match,
import_to_add,
}
@@ -693,6 +710,15 @@ impl Builder {
self
}
pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder {
+ // The default value of `CompletionRelevance.is_deprecated` is `false`, so it being `true`
+ // would mean it was set manually. Advise using the other function instead.
+ //
+ // This is technically not necessary, because `deprecated` will get reconciled in
+ // `Builder::build` anyway -- it just helps keep the callers consistent.
+ assert!(
+ !relevance.is_deprecated,
+ "`deprecated` should be set using `Builder::set_deprecated` instead"
+ );
self.relevance = relevance;
self
}
@@ -727,9 +753,25 @@ mod tests {
use test_utils::assert_eq_text;
use super::{
- CompletionRelevance, CompletionRelevancePostfixMatch, CompletionRelevanceTypeMatch,
+ CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch,
+ CompletionRelevanceTypeMatch,
};
+ #[test]
+ fn builder_deprecated_from_set_deprecated() {
+ // setting just `item.deprecated` also sets `item.relevance.is_deprecated`
+ let mut builder = CompletionItem::new(
+ CompletionItemKind::Expression,
+ Default::default(),
+ "",
+ syntax::Edition::DEFAULT,
+ );
+ builder.set_deprecated(true);
+ let item = builder.build(&Default::default());
+ assert!(item.deprecated);
+ assert!(item.relevance.is_deprecated);
+ }
+
/// Check that these are CompletionRelevance are sorted in ascending order
/// by their relevance score.
///
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index 3df511a5ad..4ca3257c5c 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -23,7 +23,7 @@ use ide_db::{
syntax_helpers::tree_diff::diff,
text_edit::TextEdit,
};
-use syntax::ast::make;
+use syntax::{AstNode, syntax_editor::SyntaxEditor};
use crate::{
completions::Completions,
@@ -258,7 +258,7 @@ pub fn completions(
completions::attribute::complete_known_attribute_input(
acc,
ctx,
- colon_prefix,
+ *colon_prefix,
attr,
extern_crate.as_ref(),
);
@@ -296,23 +296,26 @@ pub fn resolve_completion_edits(
let current_module = sema.scope(position_for_import)?.module();
let current_crate = current_module.krate(db);
let current_edition = current_crate.edition(db);
- let new_ast = scope.clone_for_update();
let mut import_insert = TextEdit::builder();
+ let (editor, _) = SyntaxEditor::new(original_file.syntax().clone());
+ let make = editor.make();
imports.into_iter().for_each(|import| {
- let full_path = make::path_from_text_with_edition(&import.path, current_edition);
+ let full_path = make.path_from_text_with_edition(&import.path, current_edition);
if import.as_underscore {
- insert_use::insert_use_as_alias(
- &new_ast,
+ insert_use::insert_use_as_alias_with_editor(
+ &scope,
full_path,
&config.insert_use,
current_edition,
+ &editor,
);
} else {
- insert_use::insert_use(&new_ast, full_path, &config.insert_use);
+ insert_use::insert_use_with_editor(&scope, full_path, &config.insert_use, &editor);
}
});
- diff(scope.as_syntax_node(), new_ast.as_syntax_node()).into_text_edit(&mut import_insert);
+ let edit = editor.finish();
+ diff(edit.old_root(), edit.new_root()).into_text_edit(&mut import_insert);
Some(vec![import_insert.finish()])
}
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index a636c0603b..fbbdffefe3 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -35,15 +35,15 @@ use crate::{
};
/// Interface for data and methods required for items rendering.
#[derive(Debug, Clone)]
-pub(crate) struct RenderContext<'a> {
- completion: &'a CompletionContext<'a>,
+pub(crate) struct RenderContext<'a, 'db> {
+ completion: &'a CompletionContext<'a, 'db>,
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> {
+impl<'a, 'db> RenderContext<'a, 'db> {
+ pub(crate) fn new(completion: &'a CompletionContext<'a, 'db>) -> RenderContext<'a, 'db> {
RenderContext {
completion,
is_private_editable: false,
@@ -120,6 +120,15 @@ impl<'a> RenderContext<'a> {
})
}
+ /// Whether an enum variant should be rendered as deprecated.
+ ///
+ /// A variant inherits deprecation from its parent enum, matching rustc's
+ /// behavior where `#[deprecated]` on an enum applies to its variants.
+ fn is_variant_deprecated(&self, variant: hir::EnumVariant) -> bool {
+ let db = self.db();
+ variant.attrs(db).is_deprecated() || variant.parent_enum(db).attrs(db).is_deprecated()
+ }
+
// FIXME: remove this
fn docs(&self, def: impl HasDocs) -> Option<Documentation<'a>> {
def.docs(self.db())
@@ -127,7 +136,7 @@ impl<'a> RenderContext<'a> {
}
pub(crate) fn render_field(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
dot_access: &DotAccess<'_>,
receiver: Option<SmolStr>,
field: hir::Field,
@@ -204,7 +213,7 @@ fn field_with_receiver(receiver: Option<&str>, field_name: &str) -> SmolStr {
}
pub(crate) fn render_tuple_field(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
receiver: Option<SmolStr>,
field: usize,
ty: &hir::Type<'_>,
@@ -226,7 +235,7 @@ pub(crate) fn render_tuple_field(
pub(crate) fn render_type_inference(
ty_string: String,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
) -> CompletionItem {
let mut builder = CompletionItem::new(
@@ -245,7 +254,7 @@ pub(crate) fn render_type_inference(
}
pub(crate) fn render_path_resolution(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
local_name: hir::Name,
resolution: ScopeDef,
@@ -254,7 +263,7 @@ pub(crate) fn render_path_resolution(
}
pub(crate) fn render_pattern_resolution(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
pattern_ctx: &PatternContext,
local_name: hir::Name,
resolution: ScopeDef,
@@ -263,7 +272,7 @@ pub(crate) fn render_pattern_resolution(
}
pub(crate) fn render_resolution_with_import(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
import_edit: LocatedImport,
) -> Option<Builder> {
@@ -276,7 +285,7 @@ pub(crate) fn render_resolution_with_import(
}
pub(crate) fn render_resolution_with_import_pat(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
pattern_ctx: &PatternContext,
import_edit: LocatedImport,
) -> Option<Builder> {
@@ -286,7 +295,7 @@ pub(crate) fn render_resolution_with_import_pat(
}
pub(crate) fn render_expr(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
expr: &hir::term_search::Expr<'_>,
) -> Option<Builder> {
let mut i = 1;
@@ -349,7 +358,7 @@ pub(crate) fn render_expr(
fn get_import_name(
resolution: ScopeDef,
- ctx: &RenderContext<'_>,
+ ctx: &RenderContext<'_, '_>,
import_edit: &LocatedImport,
) -> Option<hir::Name> {
// FIXME: Temporary workaround for handling aliased import.
@@ -367,7 +376,7 @@ fn get_import_name(
fn scope_def_to_name(
resolution: ScopeDef,
- ctx: &RenderContext<'_>,
+ ctx: &RenderContext<'_, '_>,
import_edit: &LocatedImport,
) -> Option<hir::Name> {
Some(match resolution {
@@ -379,7 +388,7 @@ fn scope_def_to_name(
}
fn render_resolution_pat(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
pattern_ctx: &PatternContext,
local_name: hir::Name,
import_to_add: Option<LocatedImport>,
@@ -397,7 +406,7 @@ fn render_resolution_pat(
}
fn render_resolution_path(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
local_name: hir::Name,
import_to_add: Option<LocatedImport>,
@@ -462,6 +471,11 @@ fn render_resolution_path(
.insert_snippet(cap, ""); // set is snippet
}
}
+ let allow_module_path = matches!(path_ctx.kind, PathKind::Use) || !config.add_colons_to_module;
+ if !allow_module_path && matches!(resolution, ScopeDef::ModuleDef(Module(_))) {
+ insert_text = format_smolstr!("{insert_text}::");
+ item.lookup_by(name.clone()).label(insert_text.clone());
+ }
adds_ret_type_arrow(completion, path_ctx, &mut item, insert_text.into());
let mut set_item_relevance = |ty: Type<'_>| {
@@ -506,7 +520,7 @@ fn render_resolution_path(
}
fn render_resolution_simple_(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
local_name: &hir::Name,
import_to_add: Option<LocatedImport>,
resolution: ScopeDef,
@@ -580,9 +594,10 @@ fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<Documentati
}
}
-fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> bool {
+fn scope_def_is_deprecated(ctx: &RenderContext<'_, '_>, resolution: ScopeDef) -> bool {
let db = ctx.db();
match resolution {
+ ScopeDef::ModuleDef(hir::ModuleDef::EnumVariant(it)) => ctx.is_variant_deprecated(it),
ScopeDef::ModuleDef(it) => ctx.is_deprecated(it, it.as_assoc_item(db)),
ScopeDef::GenericParam(it) => {
ctx.is_deprecated(it, None /* generic params can't be assoc items */)
@@ -595,7 +610,7 @@ fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> boo
}
pub(crate) fn render_type_keyword_snippet(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
label: &str,
snippet: &str,
@@ -619,7 +634,7 @@ pub(crate) fn render_type_keyword_snippet(
}
fn adds_ret_type_arrow(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
item: &mut Builder,
insert_text: String,
@@ -638,7 +653,7 @@ fn adds_ret_type_arrow(
// FIXME: This checks types without possible coercions which some completions might want to do
fn match_types(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
ty1: &hir::Type<'_>,
ty2: &hir::Type<'_>,
) -> Option<CompletionRelevanceTypeMatch> {
@@ -652,7 +667,7 @@ fn match_types(
}
fn compute_type_match(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
completion_ty: &hir::Type<'_>,
) -> Option<CompletionRelevanceTypeMatch> {
let expected_type = ctx.expected_type.as_ref()?;
@@ -686,12 +701,12 @@ fn compute_has_local_inherent_impl(
.any(|imp| imp.trait_(db).is_none() && imp.module(db) == curr_module)
}
-fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool {
+fn compute_exact_name_match(ctx: &CompletionContext<'_, '_>, completion_name: &str) -> bool {
ctx.expected_name.as_ref().is_some_and(|name| name.text() == completion_name)
}
fn compute_ref_match(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
completion_ty: &hir::Type<'_>,
) -> Option<CompletionItemRefMode> {
let expected_type = ctx.expected_type.as_ref()?;
@@ -703,7 +718,9 @@ fn compute_ref_match(
if let Some(expected_without_ref) = &expected_without_ref
&& (completion_without_ref.is_none()
|| completion_ty.could_unify_with(ctx.db, expected_without_ref))
- && completion_ty.autoderef(ctx.db).any(|ty| ty == *expected_without_ref)
+ && completion_ty
+ .autoderef(ctx.db)
+ .any(|ty| ty.could_unify_with(ctx.db, expected_without_ref))
{
cov_mark::hit!(suggest_ref);
let mutability = if expected_type.is_mutable_reference() {
@@ -726,7 +743,7 @@ fn compute_ref_match(
}
fn path_ref_match(
- completion: &CompletionContext<'_>,
+ completion: &CompletionContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
ty: &hir::Type<'_>,
item: &mut Builder,
@@ -828,7 +845,7 @@ mod tests {
items.push(format!(
"{tag} {} {} {relevance}\n",
it.label.primary,
- it.label.detail_right.clone().unwrap_or_default(),
+ it.label.detail_right.as_deref().unwrap_or_default(),
));
if let Some((label, _indel, relevance)) = it.ref_match() {
@@ -844,24 +861,33 @@ mod tests {
expect.assert_eq(&actual);
fn display_relevance(relevance: CompletionRelevance) -> String {
- let relevance_factors = vec![
- (relevance.type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"),
- (
- relevance.type_match == Some(CompletionRelevanceTypeMatch::CouldUnify),
- "type_could_unify",
- ),
- (relevance.exact_name_match, "name"),
- (relevance.is_local, "local"),
- (
- relevance.postfix_match == Some(CompletionRelevancePostfixMatch::Exact),
- "snippet",
- ),
- (relevance.trait_.is_some_and(|it| it.is_op_method), "op_method"),
- (relevance.requires_import, "requires_import"),
- (relevance.has_local_inherent_impl, "has_local_inherent_impl"),
+ let CompletionRelevance {
+ exact_name_match,
+ type_match,
+ is_local,
+ trait_,
+ is_name_already_imported: _,
+ requires_import,
+ is_private_editable: _,
+ postfix_match,
+ function: _,
+ is_skipping_completion: _,
+ has_local_inherent_impl,
+ is_deprecated,
+ } = relevance;
+ let relevance_factors = [
+ (type_match == Some(CompletionRelevanceTypeMatch::Exact), "type"),
+ (type_match == Some(CompletionRelevanceTypeMatch::CouldUnify), "type_could_unify"),
+ (exact_name_match, "name"),
+ (is_local, "local"),
+ (postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet"),
+ (trait_.is_some_and(|it| it.is_op_method), "op_method"),
+ (requires_import, "requires_import"),
+ (has_local_inherent_impl, "has_local_inherent_impl"),
+ (is_deprecated, "deprecated"),
]
.into_iter()
- .filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
+ .filter_map(|(cond, desc)| cond.then_some(desc))
.join("+");
format!("[{relevance_factors}]")
@@ -869,6 +895,33 @@ mod tests {
}
#[test]
+ fn trait_imported_as_underscore_should_not_appear_auto_import_again() {
+ // make sure there has no `requires_import`
+ // see https://github.com/rust-lang/rust-analyzer/issues/19767
+ check_relevance(
+ r#"
+//- /dep.rs crate:dep
+pub trait MyTrait {
+ fn my_method(&self);
+}
+
+//- /main.rs crate:main deps:dep
+use dep::MyTrait as _;
+struct MyStruct;
+impl dep::MyTrait for MyStruct {
+ fn my_method(&self) {}
+}
+fn main() {
+ MyStruct::my_method$0
+}
+"#,
+ expect![[r#"
+ me my_method(…) fn(&self) []
+ "#]],
+ );
+ }
+
+ #[test]
fn set_struct_type_completion_info() {
check_relevance(
r#"
@@ -894,7 +947,7 @@ fn main() {
st dep::test_mod_b::Struct {…} dep::test_mod_b::Struct { } [type_could_unify]
ex dep::test_mod_b::Struct { } [type_could_unify]
st Struct Struct [type_could_unify+requires_import]
- md dep []
+ md dep:: []
fn main() fn() []
fn test(…) fn(Struct) []
st Struct Struct [requires_import]
@@ -932,7 +985,7 @@ fn main() {
"#,
expect![[r#"
un Union Union [type_could_unify+requires_import]
- md dep []
+ md dep:: []
fn main() fn() []
fn test(…) fn(Union) []
en Union Union [requires_import]
@@ -970,7 +1023,7 @@ fn main() {
ev dep::test_mod_b::Enum::variant dep::test_mod_b::Enum::variant [type_could_unify]
ex dep::test_mod_b::Enum::variant [type_could_unify]
en Enum Enum [type_could_unify+requires_import]
- md dep []
+ md dep:: []
fn main() fn() []
fn test(…) fn(Enum) []
en Enum Enum [requires_import]
@@ -1007,7 +1060,7 @@ fn main() {
expect![[r#"
ev dep::test_mod_b::Enum::Variant dep::test_mod_b::Enum::Variant [type_could_unify]
ex dep::test_mod_b::Enum::Variant [type_could_unify]
- md dep []
+ md dep:: []
fn main() fn() []
fn test(…) fn(Enum) []
"#]],
@@ -1037,7 +1090,7 @@ fn main() {
}
"#,
expect![[r#"
- md dep []
+ md dep:: []
fn main() fn() []
fn test(…) fn(fn(usize) -> i32) []
fn function fn(usize) -> i32 [requires_import]
@@ -1070,7 +1123,7 @@ fn main() {
"#,
expect![[r#"
ct CONST i32 [type_could_unify+requires_import]
- md dep []
+ md dep:: []
fn main() fn() []
fn test(…) fn(i32) []
ct CONST i64 [requires_import]
@@ -1102,7 +1155,7 @@ fn main() {
"#,
expect![[r#"
sc STATIC i32 [type_could_unify+requires_import]
- md dep []
+ md dep:: []
fn main() fn() []
fn test(…) fn(i32) []
sc STATIC i64 [requires_import]
@@ -1242,6 +1295,7 @@ fn main() { Foo::Fo$0 }
),
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
trigger_call_info: true,
},
@@ -1293,6 +1347,7 @@ fn main() { Foo::Fo$0 }
),
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
trigger_call_info: true,
},
@@ -1437,6 +1492,7 @@ fn main() { Foo::Fo$0 }
),
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
trigger_call_info: true,
},
@@ -1477,15 +1533,16 @@ fn main() { let _: m::Spam = S$0 }
detail: "fn()",
},
CompletionItem {
- label: "m",
+ label: "m::",
detail_left: None,
detail_right: None,
source_range: 75..76,
delete: 75..76,
- insert: "m",
+ insert: "m::",
kind: SymbolKind(
Module,
),
+ lookup: "m",
},
CompletionItem {
label: "m::Spam::Bar(…)",
@@ -1521,6 +1578,7 @@ fn main() { let _: m::Spam = S$0 }
),
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
trigger_call_info: true,
},
@@ -1558,6 +1616,7 @@ fn main() { let _: m::Spam = S$0 }
),
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
trigger_call_info: true,
},
@@ -1579,16 +1638,31 @@ fn main() { som$0 }
expect![[r#"
[
CompletionItem {
- label: "something_deprecated",
+ label: "something_deprecated::",
detail_left: None,
detail_right: None,
source_range: 55..58,
delete: 55..58,
- insert: "something_deprecated",
+ insert: "something_deprecated::",
kind: SymbolKind(
Module,
),
+ lookup: "something_deprecated",
deprecated: true,
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ trait_: None,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_private_editable: false,
+ postfix_match: None,
+ function: None,
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ is_deprecated: true,
+ },
},
]
"#]],
@@ -1634,6 +1708,20 @@ fn main() { som$0 }
lookup: "something_deprecated",
detail: "fn()",
deprecated: true,
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ trait_: None,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_private_editable: false,
+ postfix_match: None,
+ function: None,
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ is_deprecated: true,
+ },
},
]
"#]],
@@ -1663,6 +1751,20 @@ fn main() { A$0 }
),
detail: "A",
deprecated: true,
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ trait_: None,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_private_editable: false,
+ postfix_match: None,
+ function: None,
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ is_deprecated: true,
+ },
},
]
"#]],
@@ -1692,6 +1794,20 @@ fn main() { A$0 }
),
detail: "A",
deprecated: true,
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ trait_: None,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_private_editable: false,
+ postfix_match: None,
+ function: None,
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ is_deprecated: true,
+ },
},
]
"#]],
@@ -1741,6 +1857,7 @@ fn main() { A::$0 }
),
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
trigger_call_info: true,
},
@@ -1776,6 +1893,7 @@ fn main() { A::$0 }
),
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: true,
},
trigger_call_info: true,
},
@@ -1807,6 +1925,20 @@ fn main() { A$0 }
),
detail: "i32",
deprecated: true,
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ trait_: None,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_private_editable: false,
+ postfix_match: None,
+ function: None,
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ is_deprecated: true,
+ },
},
]
"#]],
@@ -1836,6 +1968,20 @@ fn main() { A$0 }
),
detail: "i32",
deprecated: true,
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ trait_: None,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_private_editable: false,
+ postfix_match: None,
+ function: None,
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ is_deprecated: true,
+ },
},
]
"#]],
@@ -1862,6 +2008,20 @@ impl A$0
Trait,
),
deprecated: true,
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ trait_: None,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_private_editable: false,
+ postfix_match: None,
+ function: None,
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ is_deprecated: true,
+ },
},
]
"#]],
@@ -1888,6 +2048,20 @@ fn main() { A$0 }
TypeAlias,
),
deprecated: true,
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ trait_: None,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_private_editable: false,
+ postfix_match: None,
+ function: None,
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ is_deprecated: true,
+ },
},
]
"#]],
@@ -1918,6 +2092,20 @@ fn main() { a$0 }
lookup: "a!",
detail: "macro_rules! a",
deprecated: true,
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: false,
+ trait_: None,
+ is_name_already_imported: false,
+ requires_import: false,
+ is_private_editable: false,
+ postfix_match: None,
+ function: None,
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ is_deprecated: true,
+ },
},
]
"#]],
@@ -1960,6 +2148,7 @@ fn main() { A { the$0 } }
function: None,
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: true,
},
},
]
@@ -2020,6 +2209,7 @@ impl S {
),
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
},
CompletionItem {
@@ -2112,6 +2302,7 @@ use self::E::*;
),
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
trigger_call_info: true,
},
@@ -2183,6 +2374,7 @@ fn foo(s: S) { s.$0 }
),
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
},
]
@@ -2396,6 +2588,7 @@ fn f() -> i32 {
function: None,
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
},
]
@@ -2502,6 +2695,7 @@ fn main() {
function: None,
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
ref_match: "&@65",
},
@@ -2624,8 +2818,8 @@ mod b {
expect![[r#"
st Fooa Fooa []
tt Foob []
- md a []
- md b []
+ md a:: []
+ md b:: []
"#]],
);
}
@@ -2732,6 +2926,26 @@ fn main() {
fn main() fn() []
"#]],
);
+ check_relevance(
+ r#"
+struct S<T>(T);
+fn foo<T>(s: &mut S<T>) {}
+fn main() {
+ let mut ssss = S(2u32);
+ foo($0);
+}
+ "#,
+ expect![[r#"
+ st S(…) S(T) []
+ st &mut S(…) [type]
+ lc ssss S<u32> [local]
+ lc &mut ssss [type+local]
+ st S S<{unknown}> []
+ st &mut S [type]
+ fn foo(…) fn(&mut S<T>) []
+ fn main() fn() []
+ "#]],
+ );
}
#[test]
@@ -2763,7 +2977,7 @@ fn main() {
tt Clone []
tt Copy []
fn bar(…) fn(Foo) []
- md core []
+ md core:: []
fn main() fn() []
"#]],
);
@@ -2805,7 +3019,7 @@ fn main() {
st &S [type]
st T T []
st &T [type]
- md core []
+ md core:: []
fn foo(…) fn(&S) []
fn main() fn() []
"#]],
@@ -2854,7 +3068,7 @@ fn main() {
st &mut S [type]
st T T []
st &mut T [type]
- md core []
+ md core:: []
fn foo(…) fn(&mut S) []
fn main() fn() []
"#]],
@@ -2957,7 +3171,7 @@ fn main() {
st &T [type]
fn bar() fn() -> T []
fn &bar() [type]
- md core []
+ md core:: []
fn foo(…) fn(&S) []
fn main() fn() []
"#]],
@@ -3299,6 +3513,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
),
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
ref_match: "&@107",
},
@@ -3387,6 +3602,7 @@ fn foo() {
function: None,
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
},
]
@@ -3446,6 +3662,7 @@ fn main() {
),
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
ref_match: "&@92",
},
@@ -3632,7 +3849,7 @@ fn f() {
expect![[r#"
st Buffer Buffer []
fn f() fn() []
- md std []
+ md std:: []
tt BufRead [requires_import]
st BufReader BufReader [requires_import]
st BufWriter BufWriter [requires_import]
@@ -3641,6 +3858,25 @@ fn f() {
}
#[test]
+ /// Issue: https://github.com/rust-lang/rust-analyzer/issues/18554
+ fn float_consts_relevance() {
+ check_relevance(
+ r#"
+//- minicore: float_consts
+fn main() {
+ let x = f32::INF$0
+}
+"#,
+ expect![[r#"
+ ct INFINITY pub const INFINITY: f32 []
+ ct NEG_INFINITY pub const NEG_INFINITY: f32 []
+ ct INFINITY f32 [type_could_unify+requires_import+deprecated]
+ ct NEG_INFINITY f32 [type_could_unify+requires_import+deprecated]
+ "#]],
+ );
+ }
+
+ #[test]
fn completes_struct_with_raw_identifier() {
check_edit(
"type",
@@ -3917,6 +4153,7 @@ fn main() {
function: None,
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
},
CompletionItem {
@@ -3952,6 +4189,7 @@ fn main() {
function: None,
is_skipping_completion: false,
has_local_inherent_impl: false,
+ is_deprecated: false,
},
},
]
diff --git a/crates/ide-completion/src/render/const_.rs b/crates/ide-completion/src/render/const_.rs
index 134a77a899..c14fc1704c 100644
--- a/crates/ide-completion/src/render/const_.rs
+++ b/crates/ide-completion/src/render/const_.rs
@@ -6,12 +6,15 @@ use syntax::ToSmolStr;
use crate::{item::CompletionItem, render::RenderContext};
-pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem> {
+pub(crate) fn render_const(
+ ctx: RenderContext<'_, '_>,
+ const_: hir::Const,
+) -> Option<CompletionItem> {
let _p = tracing::info_span!("render_const").entered();
render(ctx, const_)
}
-fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem> {
+fn render(ctx: RenderContext<'_, '_>, const_: hir::Const) -> Option<CompletionItem> {
let db = ctx.db();
let name = const_.name(db)?;
let (name, escaped_name) =
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index 18151cffcd..97d5a25f49 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -27,7 +27,7 @@ enum FuncKind<'ctx> {
}
pub(crate) fn render_fn(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
local_name: Option<hir::Name>,
func: hir::Function,
@@ -37,7 +37,7 @@ pub(crate) fn render_fn(
}
pub(crate) fn render_method(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
dot_access: &DotAccess<'_>,
receiver: Option<SmolStr>,
local_name: Option<hir::Name>,
@@ -48,7 +48,7 @@ pub(crate) fn render_method(
}
fn render(
- ctx @ RenderContext { completion, .. }: RenderContext<'_>,
+ ctx @ RenderContext { completion, .. }: RenderContext<'_, '_>,
local_name: Option<hir::Name>,
func: hir::Function,
func_kind: FuncKind<'_>,
@@ -183,7 +183,7 @@ fn render(
fn compute_return_type_match(
db: &dyn HirDatabase,
- ctx: &RenderContext<'_>,
+ ctx: &RenderContext<'_, '_>,
self_type: hir::Type<'_>,
ret_type: &hir::Type<'_>,
) -> CompletionRelevanceReturnType {
@@ -210,7 +210,7 @@ fn compute_return_type_match(
pub(super) fn add_call_parens<'b>(
builder: &'b mut Builder,
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
cap: SnippetCap,
name: SmolStr,
escaped_name: SmolStr,
@@ -286,7 +286,7 @@ pub(super) fn add_call_parens<'b>(
builder.label(SmolStr::from_iter([&name, label_suffix])).insert_snippet(cap, snippet)
}
-fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type<'_>) -> &'static str {
+fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str {
if let Some(derefed_ty) = ty.remove_ref() {
for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) {
if name.as_str() == arg {
@@ -301,7 +301,7 @@ fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type<'_>) -> &
""
}
-fn detail(ctx: &CompletionContext<'_>, func: hir::Function) -> String {
+fn detail(ctx: &CompletionContext<'_, '_>, func: hir::Function) -> String {
let mut ret_ty = func.ret_type(ctx.db);
let mut detail = String::new();
@@ -327,7 +327,7 @@ fn detail(ctx: &CompletionContext<'_>, func: hir::Function) -> String {
detail
}
-fn detail_full(ctx: &CompletionContext<'_>, func: hir::Function) -> String {
+fn detail_full(ctx: &CompletionContext<'_, '_>, func: hir::Function) -> String {
let signature = format!("{}", func.display(ctx.db, ctx.display_target));
let mut detail = String::with_capacity(signature.len());
@@ -342,7 +342,7 @@ fn detail_full(ctx: &CompletionContext<'_>, func: hir::Function) -> String {
detail
}
-fn params_display(ctx: &CompletionContext<'_>, detail: &mut String, func: hir::Function) {
+fn params_display(ctx: &CompletionContext<'_, '_>, detail: &mut String, func: hir::Function) {
if let Some(self_param) = func.self_param(ctx.db) {
format_to!(detail, "{}", self_param.display(ctx.db, ctx.display_target));
let assoc_fn_params = func.assoc_fn_params(ctx.db);
@@ -368,7 +368,7 @@ fn params_display(ctx: &CompletionContext<'_>, detail: &mut String, func: hir::F
}
fn params<'db>(
- ctx: &CompletionContext<'db>,
+ ctx: &CompletionContext<'_, 'db>,
func: hir::Function,
func_kind: &FuncKind<'_>,
has_dot_receiver: bool,
diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs
index b7de3da468..9e0cec62e6 100644
--- a/crates/ide-completion/src/render/literal.rs
+++ b/crates/ide-completion/src/render/literal.rs
@@ -20,7 +20,7 @@ use crate::{
};
pub(crate) fn render_variant_lit(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
local_name: Option<hir::Name>,
variant: hir::EnumVariant,
@@ -34,7 +34,7 @@ pub(crate) fn render_variant_lit(
}
pub(crate) fn render_struct_literal(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
strukt: hir::Struct,
path: Option<hir::ModPath>,
@@ -48,7 +48,7 @@ pub(crate) fn render_struct_literal(
}
fn render(
- ctx @ RenderContext { completion, .. }: RenderContext<'_>,
+ ctx @ RenderContext { completion, .. }: RenderContext<'_, '_>,
path_ctx: &PathCompletionCtx<'_>,
thing: Variant,
name: hir::Name,
@@ -154,7 +154,7 @@ enum Variant {
}
impl Variant {
- fn fields(self, ctx: &CompletionContext<'_>) -> Option<Vec<hir::Field>> {
+ fn fields(self, ctx: &CompletionContext<'_, '_>) -> Option<Vec<hir::Field>> {
let fields = match self {
Variant::Struct(it) => it.fields(ctx.db),
Variant::EnumVariant(it) => it.fields(ctx.db),
@@ -187,14 +187,12 @@ impl Variant {
}
}
- fn is_deprecated(self, ctx: &RenderContext<'_>) -> bool {
+ fn is_deprecated(self, ctx: &RenderContext<'_, '_>) -> bool {
match self {
Variant::Struct(it) => {
ctx.is_deprecated(it, None /* structs can't be assoc items */)
}
- Variant::EnumVariant(it) => {
- ctx.is_deprecated(it, None /* enum variants can't be assoc items */)
- }
+ Variant::EnumVariant(it) => ctx.is_variant_deprecated(it),
}
}
diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs
index ff4cf9a75b..85a0761c17 100644
--- a/crates/ide-completion/src/render/macro_.rs
+++ b/crates/ide-completion/src/render/macro_.rs
@@ -11,7 +11,7 @@ use crate::{
};
pub(crate) fn render_macro(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
PathCompletionCtx { kind, has_macro_bang, has_call_parens, .. }: &PathCompletionCtx<'_>,
name: hir::Name,
@@ -22,7 +22,7 @@ pub(crate) fn render_macro(
}
pub(crate) fn render_macro_pat(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
_pattern_ctx: &PatternContext,
name: hir::Name,
macro_: hir::Macro,
@@ -32,7 +32,7 @@ pub(crate) fn render_macro_pat(
}
fn render(
- ctx @ RenderContext { completion, .. }: RenderContext<'_>,
+ ctx @ RenderContext { completion, .. }: RenderContext<'_, '_>,
is_use_path: bool,
has_macro_bang: bool,
has_call_parens: bool,
@@ -91,7 +91,7 @@ fn render(
}
fn label(
- ctx: &RenderContext<'_>,
+ ctx: &RenderContext<'_, '_>,
needs_bang: bool,
bra: &str,
ket: &str,
diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs
index 022e97e4f7..392ecbc302 100644
--- a/crates/ide-completion/src/render/pattern.rs
+++ b/crates/ide-completion/src/render/pattern.rs
@@ -15,7 +15,7 @@ use crate::{
};
pub(crate) fn render_struct_pat(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
pattern_ctx: &PatternContext,
strukt: hir::Struct,
local_name: Option<Name>,
@@ -44,7 +44,7 @@ pub(crate) fn render_struct_pat(
}
pub(crate) fn render_variant_pat(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
pattern_ctx: &PatternContext,
path_ctx: Option<&PathCompletionCtx<'_>>,
variant: hir::EnumVariant,
@@ -104,7 +104,7 @@ pub(crate) fn render_variant_pat(
}
fn build_completion(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
label: SmolStr,
lookup: SmolStr,
pat: String,
@@ -140,7 +140,7 @@ fn build_completion(
}
fn render_pat(
- ctx: &RenderContext<'_>,
+ ctx: &RenderContext<'_, '_>,
pattern_ctx: &PatternContext,
name: &str,
kind: StructKind,
diff --git a/crates/ide-completion/src/render/type_alias.rs b/crates/ide-completion/src/render/type_alias.rs
index 2b79ca2deb..ce1be2d551 100644
--- a/crates/ide-completion/src/render/type_alias.rs
+++ b/crates/ide-completion/src/render/type_alias.rs
@@ -7,7 +7,7 @@ use syntax::{SmolStr, ToSmolStr};
use crate::{item::CompletionItem, render::RenderContext};
pub(crate) fn render_type_alias(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
type_alias: hir::TypeAlias,
) -> Option<CompletionItem> {
let _p = tracing::info_span!("render_type_alias").entered();
@@ -15,7 +15,7 @@ pub(crate) fn render_type_alias(
}
pub(crate) fn render_type_alias_with_eq(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
type_alias: hir::TypeAlias,
) -> Option<CompletionItem> {
let _p = tracing::info_span!("render_type_alias_with_eq").entered();
@@ -23,7 +23,7 @@ pub(crate) fn render_type_alias_with_eq(
}
fn render(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
type_alias: hir::TypeAlias,
with_eq: bool,
) -> Option<CompletionItem> {
diff --git a/crates/ide-completion/src/render/union_literal.rs b/crates/ide-completion/src/render/union_literal.rs
index 7164c94fde..e7ee59d489 100644
--- a/crates/ide-completion/src/render/union_literal.rs
+++ b/crates/ide-completion/src/render/union_literal.rs
@@ -14,7 +14,7 @@ use crate::{
};
pub(crate) fn render_union_literal(
- ctx: RenderContext<'_>,
+ ctx: RenderContext<'_, '_>,
un: hir::Union,
path: Option<hir::ModPath>,
local_name: Option<Name>,
diff --git a/crates/ide-completion/src/render/variant.rs b/crates/ide-completion/src/render/variant.rs
index ce35ab135f..f86af6cdcb 100644
--- a/crates/ide-completion/src/render/variant.rs
+++ b/crates/ide-completion/src/render/variant.rs
@@ -17,7 +17,7 @@ pub(crate) struct RenderedLiteral {
/// Render a record type (or sub-type) to a `RenderedCompound`. Use `None` for
/// the `name` argument for an anonymous type.
pub(crate) fn render_record_lit(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
snippet_cap: Option<SnippetCap>,
fields: &[hir::Field],
path: &str,
@@ -63,7 +63,7 @@ pub(crate) fn render_record_lit(
/// Render a tuple type (or sub-type) to a `RenderedCompound`. Use `None` for
/// the `name` argument for an anonymous type.
pub(crate) fn render_tuple_lit(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
snippet_cap: Option<SnippetCap>,
fields: &[hir::Field],
path: &str,
@@ -93,7 +93,7 @@ pub(crate) fn render_tuple_lit(
/// fields, plus a boolean for whether the list is comprehensive (contains no
/// private fields and its item is not marked `#[non_exhaustive]`).
pub(crate) fn visible_fields(
- ctx: &CompletionContext<'_>,
+ ctx: &CompletionContext<'_, '_>,
fields: &[hir::Field],
item: impl HasAttrs + HasCrate + Copy,
) -> Option<(Vec<hir::Field>, bool)> {
diff --git a/crates/ide-completion/src/snippet.rs b/crates/ide-completion/src/snippet.rs
index d326098f94..67ca9db230 100644
--- a/crates/ide-completion/src/snippet.rs
+++ b/crates/ide-completion/src/snippet.rs
@@ -150,7 +150,7 @@ impl Snippet {
}
/// Returns [`None`] if the required items do not resolve.
- pub(crate) fn imports(&self, ctx: &CompletionContext<'_>) -> Option<Vec<LocatedImport>> {
+ pub(crate) fn imports(&self, ctx: &CompletionContext<'_, '_>) -> Option<Vec<LocatedImport>> {
import_edits(ctx, &self.requires)
}
@@ -163,7 +163,10 @@ impl Snippet {
}
}
-fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option<Vec<LocatedImport>> {
+fn import_edits(
+ ctx: &CompletionContext<'_, '_>,
+ requires: &[ModPath],
+) -> Option<Vec<LocatedImport>> {
let import_cfg = ctx.config.find_path_config(ctx.is_nightly);
let resolve = |import| {
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs
index 02e299b2a9..e574d4de0a 100644
--- a/crates/ide-completion/src/tests.rs
+++ b/crates/ide-completion/src/tests.rs
@@ -72,6 +72,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig {
term_search_fuel: 200,
full_function_signatures: false,
callable: Some(CallableSnippets::FillArguments),
+ add_colons_to_module: true,
add_semicolon_to_unit: true,
snippet_cap: SnippetCap::new(true),
insert_use: InsertUseConfig {
diff --git a/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs
index 131911be91..6dcd4f9cdd 100644
--- a/crates/ide-completion/src/tests/attribute.rs
+++ b/crates/ide-completion/src/tests/attribute.rs
@@ -64,7 +64,7 @@ pub struct Foo(#[m$0] i32);
at unsafe(…)
at used
at warn(…)
- md mac
+ md mac::
kw crate::
kw self::
"#]],
@@ -128,7 +128,7 @@ pub struct Foo(#[$0] i32);
at unsafe(…)
at used
at warn(…)
- md mac
+ md mac::
kw crate::
kw self::
"#]],
@@ -162,7 +162,7 @@ struct Foo;
at repr(…)
at unsafe(…)
at warn(…)
- md proc_macros
+ md proc_macros::
kw crate::
kw self::
"#]],
@@ -463,7 +463,7 @@ struct Foo;
at repr(…)
at unsafe(…)
at warn(…)
- md core
+ md core::
kw crate::
kw self::
"#]],
@@ -1137,7 +1137,7 @@ mod derive {
de PartialEq, Eq
de PartialEq, Eq, PartialOrd, Ord
de PartialEq, PartialOrd
- md core
+ md core::
kw crate::
kw self::
"#]],
@@ -1159,7 +1159,7 @@ mod derive {
de Eq
de Eq, PartialOrd, Ord
de PartialOrd
- md core
+ md core::
kw crate::
kw self::
"#]],
@@ -1181,7 +1181,7 @@ mod derive {
de Eq
de Eq, PartialOrd, Ord
de PartialOrd
- md core
+ md core::
kw crate::
kw self::
"#]],
@@ -1202,7 +1202,7 @@ mod derive {
de Default macro Default
de PartialOrd
de PartialOrd, Ord
- md core
+ md core::
kw crate::
kw self::
"#]],
@@ -1219,8 +1219,8 @@ mod derive {
"#,
expect![[r#"
de DeriveIdentity (use proc_macros::DeriveIdentity) proc_macro DeriveIdentity
- md core
- md proc_macros
+ md core::
+ md proc_macros::
kw crate::
kw self::
"#]],
@@ -1234,8 +1234,8 @@ use proc_macros::DeriveIdentity;
"#,
expect![[r#"
de DeriveIdentity proc_macro DeriveIdentity
- md core
- md proc_macros
+ md core::
+ md proc_macros::
kw crate::
kw self::
"#]],
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index 294434297e..c1205f9e18 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -5,8 +5,8 @@ use crate::{
CompletionConfig,
config::AutoImportExclusionType,
tests::{
- BASE_ITEMS_FIXTURE, TEST_CONFIG, check, check_edit, check_with_base_items,
- completion_list_with_config,
+ BASE_ITEMS_FIXTURE, TEST_CONFIG, check, check_edit, check_edit_with_config,
+ check_with_base_items, completion_list_with_config,
},
};
@@ -48,8 +48,8 @@ fn baz() {
fn create_foo(…) fn(&FooDesc)
fn function() fn()
ma makro!(…) macro_rules! makro
- md _69latrick
- md module
+ md _69latrick::
+ md module::
sc STATIC Unit
st FooDesc FooDesc
st Record Record
@@ -149,8 +149,8 @@ impl Unit {
me self.foo() fn(self)
lc self Unit
ma makro!(…) macro_rules! makro
- md module
- md qualified
+ md module::
+ md qualified::
sp Self Unit
sc STATIC Unit
st Record Record
@@ -212,8 +212,8 @@ impl Unit {
en Enum Enum
fn function() fn()
ma makro!(…) macro_rules! makro
- md module
- md qualified
+ md module::
+ md qualified::
sc STATIC Unit
st Record Record
st Tuple Tuple
@@ -1150,6 +1150,22 @@ fn break_value_no_block() {
}
#[test]
+fn complete_module_colons() {
+ check_edit(
+ "module",
+ r#"mod module {} fn foo() { $0 }"#,
+ r#"mod module {} fn foo() { module:: }"#,
+ );
+
+ check_edit_with_config(
+ CompletionConfig { add_colons_to_module: false, ..TEST_CONFIG },
+ "module",
+ r#"mod module {} fn foo() { $0 }"#,
+ r#"mod module {} fn foo() { module }"#,
+ );
+}
+
+#[test]
fn else_completion_after_if() {
check(
r#"
@@ -2224,7 +2240,7 @@ pub struct UnstableThisShouldNotBeListed;
"#,
expect![[r#"
fn main() fn()
- md std
+ md std::
bt u32 u32
kw async
kw const
@@ -2278,7 +2294,7 @@ pub struct UnstableButWeAreOnNightlyAnyway;
"#,
expect![[r#"
fn main() fn()
- md std
+ md std::
st UnstableButWeAreOnNightlyAnyway UnstableButWeAreOnNightlyAnyway
bt u32 u32
kw async
@@ -2333,7 +2349,7 @@ pub mod intrinsics {}
"#,
expect![[r#"
fn main() fn()
- md std
+ md std::
bt u32 u32
kw async
kw const
@@ -2383,10 +2399,10 @@ fn main() {
pub mod intrinsics {}
"#,
expect![[r#"
- fn main() fn()
- md intrinsics
- md std
- bt u32 u32
+ fn main() fn()
+ md intrinsics::
+ md std::
+ bt u32 u32
kw async
kw const
kw crate::
@@ -2622,7 +2638,7 @@ fn main() {
ma helper!(…) macro_rules! helper
ma m!(…) macro_rules! m
ma makro!(…) macro_rules! makro
- md module
+ md module::
sc STATIC Unit
st Record Record
st Tuple Tuple
@@ -3085,12 +3101,12 @@ fn bar() {
ma format_args_nl!(…) macro_rules! format_args_nl
ma panic!(…) macro_rules! panic
ma print!(…) macro_rules! print
- md core
- md result (use core::result)
- md rust_2015 (use core::prelude::rust_2015)
- md rust_2018 (use core::prelude::rust_2018)
- md rust_2021 (use core::prelude::rust_2021)
- md rust_2024 (use core::prelude::rust_2024)
+ md core::
+ md result:: (use core::result)
+ md rust_2015:: (use core::prelude::rust_2015)
+ md rust_2018:: (use core::prelude::rust_2018)
+ md rust_2021:: (use core::prelude::rust_2021)
+ md rust_2024:: (use core::prelude::rust_2024)
tt Clone
tt Copy
tt FromIterator
@@ -3162,6 +3178,37 @@ fn foo() {
}
#[test]
+fn deprecated_enum_marks_variants_deprecated() {
+ check(
+ r#"
+#[deprecated]
+enum Foo { Bar }
+fn main() { let _ = Foo::$0; }
+"#,
+ expect![[r#"
+ ev Bar Bar DEPRECATED
+ "#]],
+ );
+}
+
+#[test]
+fn deprecated_variant_of_undeprecated_enum_still_deprecated() {
+ check(
+ r#"
+enum Foo {
+ #[deprecated] Bar,
+ Baz,
+}
+fn main() { let _ = Foo::$0; }
+"#,
+ expect![[r#"
+ ev Bar Bar DEPRECATED
+ ev Baz Baz
+ "#]],
+ );
+}
+
+#[test]
fn non_std_test_attr_macro() {
check(
r#"
@@ -3174,9 +3221,9 @@ fn foo() {
}
"#,
expect![[r#"
- fn foo() fn()
- md proc_macros
- bt u32 u32
+ fn foo() fn()
+ md proc_macros::
+ bt u32 u32
kw async
kw const
kw crate::
@@ -3224,9 +3271,9 @@ fn foo() {
}
"#,
expect![[r#"
- fn foo() fn()
- md proc_macros
- bt u32 u32
+ fn foo() fn()
+ md proc_macros::
+ bt u32 u32
kw async
kw const
kw crate::
@@ -3798,3 +3845,59 @@ fn baz(v: impl Bar) {
"#]],
);
}
+
+#[test]
+fn regression_21697() {
+ check(
+ r#"
+trait SuperTrait {
+ type AssocTy;
+}
+
+trait SubTrait<T = <Self as SuperTrait>::AssocTy>: SuperTrait {}
+
+fn tryme(param: impl SubTrait) {
+ param$0
+}
+ "#,
+ expect![[r#"
+ fn tryme(…) fn(impl SubTrait<<impl SubTrait<<… as SuperTrait>::AssocTy> + ?Sized as SuperTrait>::AssocTy> + ?Sized)
+ lc param impl SubTrait<<impl SubTrait<<… as SuperTrait>::AssocTy> + ?Sized as SuperTrait>::AssocTy> + ?Sized
+ tt SubTrait
+ tt SuperTrait
+ bt u32 u32
+ kw async
+ kw const
+ kw crate::
+ kw enum
+ kw extern
+ kw false
+ kw fn
+ kw for
+ kw if
+ kw if let
+ kw impl
+ kw impl for
+ kw let
+ kw letm
+ 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/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index 60ae077d01..231623a42f 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -781,9 +781,9 @@ fn main() {
}
"#,
expect![[r#"
+ me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED
ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED
fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED
- me random_method(…) (use dep::test_mod::TestTrait) fn(&self) DEPRECATED
"#]],
);
}
@@ -1772,7 +1772,7 @@ fn function() {
"#,
expect![[r#"
st FooStruct (use outer::FooStruct) BarStruct
- md foo (use outer::foo)
+ md foo:: (use outer::foo)
fn foo_fun() (use outer::foo_fun) fn()
"#]],
);
@@ -1809,9 +1809,8 @@ fn intrinsics() {
r#"
//- /core.rs crate:core
pub mod intrinsics {
- extern "rust-intrinsic" {
- pub fn transmute<Src, Dst>(src: Src) -> Dst;
- }
+ #[rustc_intrinsic]
+ pub unsafe fn transmute<Src, Dst>(src: Src) -> Dst;
}
pub mod mem {
pub use crate::intrinsics::transmute;
@@ -1829,9 +1828,8 @@ fn intrinsics() {
r#"
//- /core.rs crate:core
pub mod intrinsics {
- extern "rust-intrinsic" {
- pub fn transmute<Src, Dst>(src: Src) -> Dst;
- }
+ #[rustc_intrinsic]
+ pub unsafe fn transmute<Src, Dst>(src: Src) -> Dst;
}
pub mod mem {
pub use crate::intrinsics::transmute;
diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs
index 45024ad216..bb79af7e98 100644
--- a/crates/ide-completion/src/tests/item.rs
+++ b/crates/ide-completion/src/tests/item.rs
@@ -2,7 +2,7 @@
//!
//! Except for use items which are tested in [super::use_tree] and mod declarations with are tested
//! in [crate::completions::mod_].
-use expect_test::expect;
+use expect_test::{Expect, expect};
use crate::tests::{check, check_edit, check_with_base_items};
@@ -15,7 +15,7 @@ impl Tra$0
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -41,7 +41,7 @@ impl Trait for Str$0
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -118,7 +118,7 @@ fn completes_where() {
expect![[r#"
en Enum (adds ->) Enum
ma makro!(…) macro_rules! makro
- md module (adds ->)
+ md module:: (adds ->)
st Record (adds ->) Record
st Tuple (adds ->) Tuple
st Unit (adds ->) Unit
@@ -135,6 +135,12 @@ fn completes_where() {
"#]],
);
check_with_base_items(
+ r"fn func() -> foo::Bar $0",
+ expect![[r#"
+ kw where
+ "#]],
+ );
+ check_with_base_items(
r"enum Enum $0",
expect![[r#"
kw where
@@ -155,6 +161,62 @@ fn completes_where() {
}
#[test]
+fn completes_where_in_stmt_list() {
+ fn check_in_stmt_list(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
+ check(&format!("const _: () = {{{ra_fixture}}};"), expect);
+ }
+ check_in_stmt_list(
+ r"struct Struct $0",
+ expect![[r#"
+ kw where
+ "#]],
+ );
+ check_in_stmt_list(
+ r"struct Struct $0 {}",
+ expect![[r#"
+ kw where
+ "#]],
+ );
+ check_in_stmt_list(
+ r"fn func() $0",
+ expect![[r#"
+ bt u32 (adds ->) u32
+ kw crate:: (adds ->)
+ kw dyn (adds ->)
+ kw fn (adds ->)
+ kw for (adds ->)
+ kw impl (adds ->)
+ kw self:: (adds ->)
+ kw where
+ "#]],
+ );
+ check_in_stmt_list(
+ r"fn func() -> foo::Bar $0",
+ expect![[r#"
+ kw where
+ "#]],
+ );
+ check_in_stmt_list(
+ r"enum Enum $0",
+ expect![[r#"
+ kw where
+ "#]],
+ );
+ check_in_stmt_list(
+ r"enum Enum $0 {}",
+ expect![[r#"
+ kw where
+ "#]],
+ );
+ check_in_stmt_list(
+ r"trait Trait $0 {}",
+ expect![[r#"
+ kw where
+ "#]],
+ );
+}
+
+#[test]
fn before_record_field() {
check_with_base_items(
r#"
@@ -301,7 +363,7 @@ fn bar() {
ma expand_to_test!(…) macro_rules! expand_to_test
ma makro!(…) macro_rules! makro
ma test!(…) macro test
- md module
+ md module::
sc STATIC Unit
st Record Record
st Tuple Tuple
diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs
index 0b2be0265f..430c61887a 100644
--- a/crates/ide-completion/src/tests/item_list.rs
+++ b/crates/ide-completion/src/tests/item_list.rs
@@ -43,7 +43,7 @@ fn in_source_file_item_list() {
r#"$0"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
kw async
kw const
kw crate::
@@ -77,7 +77,7 @@ fn in_item_list_after_attr() {
r#"#[attr] $0"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
kw async
kw const
kw crate::
@@ -111,7 +111,7 @@ fn in_item_list_after_inner_attr() {
r#"#![attr] $0"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
kw async
kw const
kw crate::
@@ -145,7 +145,7 @@ fn in_qualified_path() {
r#"crate::$0"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
"#]],
)
}
@@ -315,7 +315,7 @@ fn in_impl_assoc_item_list() {
r#"impl Struct { $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
kw async
kw const
kw crate::
@@ -335,7 +335,7 @@ fn in_impl_assoc_item_list_after_attr() {
r#"impl Struct { #[attr] $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
kw async
kw const
kw crate::
@@ -355,7 +355,7 @@ fn in_trait_assoc_item_list() {
r"trait Foo { $0 }",
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
kw async
kw const
kw crate::
@@ -373,7 +373,7 @@ fn in_trait_assoc_fn_missing_body() {
r#"trait Foo { fn function(); $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
kw async
kw const
kw crate::
@@ -391,7 +391,7 @@ fn in_trait_assoc_const_missing_body() {
r#"trait Foo { const CONST: (); $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
kw async
kw const
kw crate::
@@ -409,7 +409,7 @@ fn in_trait_assoc_type_aliases_missing_ty() {
r#"trait Foo { type Type; $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
kw async
kw const
kw crate::
@@ -448,7 +448,7 @@ impl Test for () {
fn fn function1()
fn fn function2()
ma makro!(…) macro_rules! makro
- md module
+ md module::
ta type Type1 =
kw crate::
kw self::
@@ -514,7 +514,7 @@ fn after_unit_struct() {
r#"struct S; f$0"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
kw async
kw const
kw crate::
@@ -664,7 +664,7 @@ fn inside_extern_blocks() {
r#"extern { $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
kw crate::
kw fn
kw pub
@@ -681,7 +681,7 @@ fn inside_extern_blocks() {
r#"unsafe extern { $0 }"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
kw crate::
kw fn
kw pub
diff --git a/crates/ide-completion/src/tests/predicate.rs b/crates/ide-completion/src/tests/predicate.rs
index 682b8904e5..9826a8ed7b 100644
--- a/crates/ide-completion/src/tests/predicate.rs
+++ b/crates/ide-completion/src/tests/predicate.rs
@@ -13,7 +13,7 @@ struct Foo<'lt, T, const C: usize> where $0 {}
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Foo<…> Foo<'_, {unknown}, _>
st Record Record
st Tuple Tuple
@@ -39,7 +39,7 @@ struct Foo<'lt, T, const C: usize> where T: $0 {}
"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
tt Trait
kw crate::
kw self::
@@ -57,7 +57,7 @@ struct Foo<'lt, T, const C: usize> where 'lt: $0 {}
"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
tt Trait
kw crate::
kw self::
@@ -73,7 +73,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> T: $0 {}
"#,
expect![[r#"
ma makro!(…) macro_rules! makro
- md module
+ md module::
tt Trait
kw crate::
kw self::
@@ -90,7 +90,7 @@ struct Foo<'lt, T, const C: usize> where for<'a> $0 {}
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Foo<…> Foo<'_, {unknown}, _>
st Record Record
st Tuple Tuple
@@ -119,7 +119,7 @@ impl Record {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
sp Self Record
st Record Record
st Tuple Tuple
@@ -149,7 +149,7 @@ struct Foo<T> where T: $0 {}
pub trait Trait {}
"#,
expect![[r#"
- md std
+ md std::
kw crate::
kw self::
"#]],
@@ -169,7 +169,7 @@ struct Foo<T> where T: $0 {}
pub trait Trait {}
"#,
expect![[r#"
- md std
+ md std::
tt Trait
kw crate::
kw self::
diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs
index c1274f6640..ddb9294469 100644
--- a/crates/ide-completion/src/tests/record.rs
+++ b/crates/ide-completion/src/tests/record.rs
@@ -176,7 +176,7 @@ fn main() {
fn main() fn()
lc foo Foo
lc thing i32
- md core
+ md core::
st Foo Foo
st Foo {…} Foo { foo1: u32, foo2: u32 }
tt Default
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index 55059a4035..0454f4e350 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -65,7 +65,7 @@ pub mod prelude {
}
"#,
expect![[r#"
- md std
+ md std::
st Option Option
bt u32 u32
"#]],
@@ -95,7 +95,7 @@ mod macros {
expect![[r#"
fn f() fn()
ma concat!(…) macro_rules! concat
- md std
+ md std::
bt u32 u32
"#]],
);
@@ -123,8 +123,8 @@ pub mod prelude {
}
"#,
expect![[r#"
- md core
- md std
+ md core::
+ md std::
st String String
bt u32 u32
"#]],
@@ -153,7 +153,7 @@ pub mod prelude {
"#,
expect![[r#"
fn f() fn()
- md std
+ md std::
bt u32 u32
"#]],
);
@@ -181,8 +181,8 @@ pub mod prelude {
}
"#,
expect![[r#"
- md std
- "#]],
+ md std::
+ "#]],
);
}
@@ -714,7 +714,7 @@ mod m {
"#,
expect![[r#"
fn z() fn()
- md z
+ md z::
"#]],
);
}
@@ -1126,7 +1126,7 @@ fn foo { ::$0 }
"#,
Some(':'),
expect![[r#"
- md core
+ md core::
"#]],
);
check_with_trigger_character(
@@ -1136,7 +1136,7 @@ fn foo { /* test */::$0 }
"#,
Some(':'),
expect![[r#"
- md core
+ md core::
"#]],
);
@@ -1488,7 +1488,7 @@ fn here_we_go() {
"#,
expect![[r#"
fn here_we_go() fn()
- md foo
+ md foo::
st Bar (alias Qux) (use foo::Bar) Bar
bt u32 u32
kw const
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index 7d4a7fe6b8..24080334ae 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -14,7 +14,7 @@ struct Foo<'lt, T, const C: usize> {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
sp Self Foo<'_, {unknown}, _>
st Foo<…> Foo<'_, {unknown}, _>
st Record Record
@@ -43,7 +43,7 @@ struct Foo<'lt, T, const C: usize>(f$0);
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
sp Self Foo<'_, {unknown}, _>
st Foo<…> Foo<'_, {unknown}, _>
st Record Record
@@ -75,7 +75,7 @@ fn x<'lt, T, const C: usize>() -> $0
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -102,7 +102,7 @@ fn x() u$0
expect![[r#"
en Enum (adds ->) Enum
ma makro!(…) macro_rules! makro
- md module (adds ->)
+ md module:: (adds ->)
st Record (adds ->) Record
st Tuple (adds ->) Tuple
st Unit (adds ->) Unit
@@ -126,7 +126,7 @@ fn x() $0
expect![[r#"
en Enum (adds ->) Enum
ma makro!(…) macro_rules! makro
- md module (adds ->)
+ md module:: (adds ->)
st Record (adds ->) Record
st Tuple (adds ->) Tuple
st Unit (adds ->) Unit
@@ -142,6 +142,26 @@ fn x() $0
kw where
"#]],
);
+
+ check_with_base_items(
+ r#"
+mod foo { pub struct Bar; }
+fn x() foo::$0
+"#,
+ expect![[r#"
+ st Bar (adds ->) Bar
+ "#]],
+ );
+
+ check_with_base_items(
+ r#"
+mod foo { pub struct Bar; }
+fn x() foo::b$0
+"#,
+ expect![[r#"
+ st Bar (adds ->) Bar
+ "#]],
+ );
}
#[test]
@@ -196,7 +216,7 @@ fn foo() $0
"#,
r#"
mod foo { pub type Num = u32; }
-fn foo() -> foo
+fn foo() -> foo::
"#,
);
@@ -233,7 +253,7 @@ fn foo()$0
"#,
r#"
mod foo { pub type Num = u32; }
-fn foo() ->foo
+fn foo() ->foo::
"#,
);
}
@@ -286,7 +306,7 @@ fn x() u$0 {&2u32}
expect![[r#"
en Enum (adds ->) Enum
ma makro!(…) macro_rules! makro
- md module (adds ->)
+ md module:: (adds ->)
st Record (adds ->) Record
st Tuple (adds ->) Tuple
st Unit (adds ->) Unit
@@ -326,7 +346,7 @@ fn x<'lt, T, const C: usize>(_: &()) -> &$0
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -360,7 +380,7 @@ fn foo() -> B$0 {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -388,7 +408,7 @@ const FOO: $0 = Foo(2);
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Foo<…> Foo<{unknown}>
st Record Record
st Tuple Tuple
@@ -417,7 +437,7 @@ static FOO: $0 = Foo(2);
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Foo<…> Foo<{unknown}>
st Record Record
st Tuple Tuple
@@ -448,7 +468,7 @@ fn f2() {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -480,7 +500,7 @@ fn f2() {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -509,7 +529,7 @@ fn f2(x: u64) -> $0 {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -539,7 +559,7 @@ fn f2(x: $0) {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -575,8 +595,8 @@ fn foo<'lt, T, const C: usize>() {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md a
- md module
+ md a::
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -608,7 +628,7 @@ fn foo<'lt, T, const C: usize>() {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Foo<…> Foo<{unknown}>
st Record Record
st Tuple Tuple
@@ -640,7 +660,7 @@ fn foo<'lt, T, const C: usize>() {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -666,7 +686,7 @@ fn foo<'lt, T, const C: usize>() {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -709,7 +729,7 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -738,7 +758,7 @@ fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {}
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -763,7 +783,7 @@ impl Tr<$0
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
sp Self dyn Tr<{unknown}> + 'static
st Record Record
st S S
@@ -814,7 +834,7 @@ fn f(t: impl MyTrait<u$0
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -843,7 +863,7 @@ fn f(t: impl MyTrait<u8, u$0
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -890,7 +910,7 @@ fn f(t: impl MyTrait<u$0
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -919,7 +939,7 @@ fn f(t: impl MyTrait<u8, u$0
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -968,7 +988,7 @@ fn f(t: impl MyTrait<Item1 = $0
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -997,7 +1017,7 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Record Record
st Tuple Tuple
st Unit Unit
@@ -1049,7 +1069,7 @@ struct Foo {
pub struct S;
"#,
expect![[r#"
- md std
+ md std::
sp Self Foo
st Foo Foo
bt u32 u32
@@ -1078,7 +1098,7 @@ struct Foo {
pub struct S;
"#,
expect![[r#"
- md std
+ md std::
sp Self Foo
st Foo Foo
st S S
@@ -1108,7 +1128,7 @@ fn completes_const_and_type_generics_separately() {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Foo Foo
st Record Record
st Tuple Tuple
@@ -1162,7 +1182,7 @@ fn completes_const_and_type_generics_separately() {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Foo Foo
st Record Record
st Tuple Tuple
@@ -1213,7 +1233,7 @@ fn completes_const_and_type_generics_separately() {
expect![[r#"
en Enum Enum
ma makro!(…) macro_rules! makro
- md module
+ md module::
st Foo Foo
st Record Record
st Tuple Tuple
@@ -1424,7 +1444,7 @@ struct Bar;
impl $0 for Bar { }
"#,
expect![[r#"
- md module
+ md module::
tt Foo
tt Trait
kw crate::
@@ -1447,7 +1467,7 @@ mod outer {
impl outer::$0 for Bar { }
"#,
expect![[r#"
- md inner
+ md inner::
tt Foo
"#]],
);
diff --git a/crates/ide-completion/src/tests/visibility.rs b/crates/ide-completion/src/tests/visibility.rs
index b404011dfe..113c41226f 100644
--- a/crates/ide-completion/src/tests/visibility.rs
+++ b/crates/ide-completion/src/tests/visibility.rs
@@ -44,7 +44,7 @@ mod foo {
mod bar {}
"#,
expect![[r#"
- md foo
+ md foo::
"#]],
);
check(
@@ -59,7 +59,7 @@ mod qux {
mod bar {}
"#,
expect![[r#"
- md qux
+ md qux::
"#]],
);
check(
@@ -74,7 +74,7 @@ mod qux {
mod bar {}
"#,
expect![[r#"
- md foo
+ md foo::
"#]],
);
}
diff --git a/crates/ide-db/src/active_parameter.rs b/crates/ide-db/src/active_parameter.rs
index 8bd4c6c46b..506645261b 100644
--- a/crates/ide-db/src/active_parameter.rs
+++ b/crates/ide-db/src/active_parameter.rs
@@ -27,7 +27,7 @@ impl<'db> ActiveParameter<'db> {
/// Returns information about the call argument this token is part of.
pub fn at_arg(
- sema: &'db Semantics<'db, RootDatabase>,
+ sema: &Semantics<'db, RootDatabase>,
list: ast::ArgList,
at: TextSize,
) -> Option<Self> {
diff --git a/crates/ide-db/src/generated/lints.rs b/crates/ide-db/src/generated/lints.rs
index 52a5a95450..264bb4fa81 100644
--- a/crates/ide-db/src/generated/lints.rs
+++ b/crates/ide-db/src/generated/lints.rs
@@ -258,6 +258,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "dead_code_pub_in_binary",
+ description: r##"detect public items in executable crates that are never used"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "default_overrides_default_fields",
description: r##"detect `Default` impl that should use the type's default field values"##,
default_severity: Severity::Error,
@@ -736,7 +743,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[
Lint {
label: "linker_messages",
description: r##"warnings emitted at runtime by the target-specific linker program"##,
- default_severity: Severity::Allow,
+ default_severity: Severity::Warning,
warn_since: None,
deny_since: None,
},
@@ -1238,6 +1245,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "tail_call_track_caller",
+ description: r##"detects tail calls of functions marked with `#[track_caller]`"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "tail_expr_drop_order",
description: r##"Detect and warn on significant change in drop order in tail expression location"##,
default_severity: Severity::Allow,
@@ -4313,7 +4327,7 @@ defined in Rust. They may be called both from within Rust and via FFI.
pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize {
let mut sum = 0;
for _ in 0..n {
- sum += args.arg::<usize>();
+ sum += args.next_arg::<usize>();
}
sum
}
@@ -4324,6 +4338,22 @@ pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize {
deny_since: None,
},
Lint {
+ label: "c_variadic_experimental_arch",
+ description: r##"# `c_variadic_experimental_arch`
+
+Allows defining c-variadic functions on targets where this feature has not yet undergone sufficient testing for stabilization.
+
+The tracking issue for this feature is: [#155973]
+
+[#155973]: https://github.com/rust-lang/rust/issues/155973
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "c_variadic_naked_functions",
description: r##"# `c_variadic_naked_functions`
@@ -4806,22 +4836,6 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "char_max_len",
- description: r##"# `char_max_len`
-
-
-
-The tracking issue for this feature is: [#121714]
-
-[#121714]: https://github.com/rust-lang/rust/issues/121714
-
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
label: "clamp_magnitude",
description: r##"# `clamp_magnitude`
@@ -5552,6 +5566,20 @@ The tracking issue for this feature is: [#95174]
deny_since: None,
},
Lint {
+ label: "const_param_ty_unchecked",
+ description: r##"# `const_param_ty_unchecked`
+
+Allows skipping `ConstParamTy_` trait implementation checks
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "const_path_separators",
description: r##"# `const_path_separators`
@@ -6004,6 +6032,22 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
+ label: "core_io",
+ description: r##"# `core_io`
+
+
+
+The tracking issue for this feature is: [#154046]
+
+[#154046]: https://github.com/rust-lang/rust/issues/154046
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "core_io_borrowed_buf",
description: r##"# `core_io_borrowed_buf`
@@ -6020,6 +6064,20 @@ The tracking issue for this feature is: [#117693]
deny_since: None,
},
Lint {
+ label: "core_io_internals",
+ description: r##"# `core_io_internals`
+
+
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "core_private_bignum",
description: r##"# `core_private_bignum`
@@ -6986,13 +7044,96 @@ The tracking issue for this feature is: [#143874]
label: "diagnostic_on_move",
description: r##"# `diagnostic_on_move`
-Allows giving on-move borrowck custom diagnostic messages for a type
-
The tracking issue for this feature is: [#154181]
-[#154181]: https://github.com/rust-lang/rust/issues/154181
-
------------------------
+
+The `diagnostic_on_move` feature allows use of the `#[diagnostic::on_move]` attribute. It should be
+placed on struct, enum and union declarations, though it is not an error to be located in other
+positions. This attribute is a hint to the compiler to supplement the error message when the
+annotated type is involved in a borrowcheck error.
+
+For example, [`File`] is annotated as such:
+```rust
+#![feature(diagnostic_on_move)]
+
+#[diagnostic::on_move(note = "you can use `File::try_clone` \
+ to duplicate a `File` instance")]
+pub struct File {
+ // ...
+}
+```
+
+When you try to use a `File` after it's already been moved, it will helpfully tell you about `try_clone`.
+
+The message and label can also be customized:
+
+```rust
+#![feature(diagnostic_on_move)]
+
+use std::marker::PhantomData;
+
+#[diagnostic::on_move(
+ message = "`{Self}` cannot be used multiple times",
+ label = "this token may only be used once",
+ note = "you can create a new `Token` with `Token::conjure()`"
+)]
+pub struct Token<'brand> {
+ spooky: PhantomData<&'brand ()>,
+}
+
+impl Token<'_> {
+ pub fn conjure<'u>() -> Token<'u> {
+ Token {
+ spooky: PhantomData,
+ }
+ }
+}
+```
+The user may try to use it like this:
+```rust,compile_fail,E0382
+# #![feature(diagnostic_on_move)]
+#
+# use std::marker::PhantomData;
+#
+# #[diagnostic::on_move(
+# message = "`{Self}` cannot be used multiple times",
+# label = "this token may only be used once",
+# note = "you can create a new `Token` with `Token::conjure()`"
+# )]
+# pub struct Token<'brand> {
+# spooky: PhantomData<&'brand ()>,
+# }
+#
+# impl Token<'_> {
+# pub fn conjure<'u>() -> Token<'u> {
+# Token {
+# spooky: PhantomData,
+# }
+# }
+# }
+# fn main() {
+let token = Token::conjure();
+let _ = (token, token);
+# }
+```
+This will result in the following error:
+```text
+error[E0382]: `Token` cannot be used multiple times
+ --> src/main.rs:24:21
+ |
+ 1 | let token = Token::conjure();
+ | ----- this token may only be used once
+ 2 | let _ = (token, token);
+ | ----- ^^^^^ value used here after move
+ | |
+ | value moved here
+ |
+ = note: you can create a new `Token` with `Token::conjure()`
+```
+
+[`File`]: https://doc.rust-lang.org/nightly/std/fs/struct.File.html "File in std::fs"
+[#154181]: https://github.com/rust-lang/rust/issues/154181 "Tracking Issue for #[diagnostic::on_move]"
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -7015,6 +7156,66 @@ The tracking issue for this feature is: [#152900]
deny_since: None,
},
Lint {
+ label: "diagnostic_on_unmatch_args",
+ description: r##"# `diagnostic_on_unmatch_args`
+
+The tracking issue for this feature is: [#155642]
+
+[#155642]: https://github.com/rust-lang/rust/issues/155642
+
+------------------------
+
+The `diagnostic_on_unmatch_args` feature adds the
+`#[diagnostic::on_unmatch_args(...)]` attribute for declarative macros.
+It lets a macro definition customize diagnostics for matcher failures after all arms have been
+tried, such as incomplete invocations or trailing extra arguments.
+
+This attribute currently applies to declarative macros such as `macro_rules!` and `pub macro`.
+It is currently used for errors emitted by declarative macro matching itself; fragment parser
+errors still use their existing diagnostics.
+
+```rust,compile_fail
+#![feature(diagnostic_on_unmatch_args)]
+
+#[diagnostic::on_unmatch_args(
+ message = "invalid arguments to {This} macro invocation",
+ label = "expected a type and value here",
+ note = "this macro expects a type and a value, like `pair!(u8, 0)`",
+ note = "see <link/to/docs>",
+)]
+macro_rules! pair {
+ ($ty:ty, $value:expr) => {};
+}
+
+pair!(u8);
+```
+
+This emits output like:
+
+```text
+error: invalid arguments to pair macro invocation
+ --> example.rs:13:9
+ |
+9 | macro_rules! pair {
+ | ----------------- when calling this macro
+...
+13 | pair!(u8);
+ | ^ expected a type and value here
+ |
+note: while trying to match `,`
+ --> example.rs:10:12
+ |
+10 | ($ty:ty, $value:expr) => {};
+ | ^
+ = note: this macro expects a type and a value, like `pair!(u8, 0)`
+ = note: see <link/to/docs>
+```
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "dir_entry_ext2",
description: r##"# `dir_entry_ext2`
@@ -8077,6 +8278,22 @@ The tracking issue for this feature is: [#99842]
deny_since: None,
},
Lint {
+ label: "float_masks",
+ description: r##"# `float_masks`
+
+
+
+The tracking issue for this feature is: [#154064]
+
+[#154064]: https://github.com/rust-lang/rust/issues/154064
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "float_minimum_maximum",
description: r##"# `float_minimum_maximum`
@@ -8516,7 +8733,7 @@ The tracking issue for this feature is: [#130539]
},
Lint {
label: "generic_const_args",
- description: r##"# `generic_const_args`
+ description: r##"# generic_const_args
Allows using generics in more complex const expressions, based on definitional equality.
@@ -8525,6 +8742,35 @@ The tracking issue for this feature is: [#151972]
[#151972]: https://github.com/rust-lang/rust/issues/151972
------------------------
+
+Warning: This feature is incomplete; its design and syntax may change.
+
+This feature enables many of the same use cases supported by [generic_const_exprs],
+but based on the machinery developed for [min_generic_const_args]. In a way, it is
+meant to be an interim successor for GCE (though it might not currently support all
+the valid cases that supported by GCE).
+
+See also: [generic_const_items]
+
+[min_generic_const_args]: min-generic-const-args.md
+[generic_const_exprs]: generic-const-exprs.md
+[generic_const_items]: generic-const-items.md
+
+## Examples
+
+<!-- NOTE(ignore) generic_const_args requires -Znext-solver to compile -->
+```rust,ignore (requires-Z-next-solver)
+#![feature(generic_const_items)]
+#![feature(min_generic_const_args)]
+#![feature(generic_const_args)]
+#![expect(incomplete_features)]
+
+type const ADD1<const N: usize>: usize = const { N + 1 };
+
+type const INC<const N: usize>: usize = ADD1::<N>;
+
+const ARR: [(); ADD1::<0>] = [(); INC::<0>];
+```
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -8532,15 +8778,79 @@ The tracking issue for this feature is: [#151972]
},
Lint {
label: "generic_const_exprs",
- description: r##"# `generic_const_exprs`
+ description: r##"# generic_const_exprs
-Allows non-trivial generic constants which have to have wfness manually propagated to callers
+Allows non-trivial generic constants which have to be shown to successfully evaluate
+to a value by being part of an item signature.
The tracking issue for this feature is: [#76560]
+
[#76560]: https://github.com/rust-lang/rust/issues/76560
------------------------
+
+Warning: This feature is incomplete; its design and syntax may change.
+
+See also: [min_generic_const_args], [generic_const_args]
+
+[min_generic_const_args]: min-generic-const-args.md
+[generic_const_args]: generic-const-args.md
+
+## Examples
+
+```rust
+#![allow(incomplete_features)]
+#![feature(generic_const_exprs)]
+
+// Use parameters that depend on a generic argument.
+struct Foo<const N: usize>
+where
+ [(); N + 1]:,
+{
+ array: [usize; N + 1],
+}
+
+// Use generic parameters in const operations.
+trait Bar {
+ const X: usize;
+ const Y: usize;
+}
+
+// Note `B::X * B::Y`.
+const fn baz<B: Bar>(x: [usize; B::X], y: [usize; B::Y]) -> [usize; B::X * B::Y] {
+ let mut out = [0; B::X * B::Y];
+ let mut i = 0;
+ while i < B::Y {
+ let mut j = 0;
+ while j < B::X {
+ out[i * B::X + j] = y[i].saturating_mul(x[j]);
+ j += 1;
+ }
+ i += 1;
+ }
+ out
+}
+
+
+// Create a new type based on a generic argument.
+pub struct Grow<const N: usize> {
+ arr: [usize; N],
+}
+
+impl<const N: usize> Grow<N> {
+ pub const fn grow(self, val: usize) -> Grow<{ N + 1 }> {
+ let mut new_arr = [0; { N + 1 }];
+ let mut idx = 0;
+ while idx < N {
+ new_arr[idx] = self.arr[idx];
+ idx += 1;
+ }
+ new_arr[N] = val;
+ Grow { arr: new_arr }
+ }
+}
+```
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -8548,7 +8858,7 @@ The tracking issue for this feature is: [#76560]
},
Lint {
label: "generic_const_items",
- description: r##"# `generic_const_items`
+ description: r##"# generic_const_items
Allows generic parameters and where-clauses on free & associated const items.
@@ -8557,6 +8867,50 @@ The tracking issue for this feature is: [#113521]
[#113521]: https://github.com/rust-lang/rust/issues/113521
------------------------
+
+Warning: This feature is an [experiment] and lacks an RFC.
+There are no guarantees that it will ever be stabilized.
+
+See also: [generic_const_exprs], [min_generic_const_args].
+
+[experiment]: https://lang-team.rust-lang.org/how_to/experiment.html
+[generic_const_exprs]: generic-const-exprs.md
+[min_generic_const_args]: min-generic-const-args.md
+
+## Examples
+
+### Generic constant values
+
+```rust
+#![allow(incomplete_features)]
+#![feature(generic_const_items)]
+
+const GENERIC_VAL<const ARG: usize>: usize = ARG + 1;
+
+#[test]
+fn generic_const_arg() {
+ assert_eq!(GENERIC_VAL::<1>, 2);
+ assert_eq!(GENERIC_VAL::<2>, 3);
+}
+```
+
+### Conditional constants
+
+```rust
+#![allow(incomplete_features)]
+#![feature(generic_const_items)]
+
+// `GENERIC_VAL::<0>` will fail to compile
+const GENERIC_VAL<const ARG: usize>: usize = if ARG > 0 { ARG + 1 } else { panic!("0 value") };
+
+// Will fail to compile if the `Copy` derive is removed.
+const COPY_MARKER<C: Copy>: () = ();
+
+#[derive(Clone, Copy)]
+struct Foo;
+
+const FOO_IS_COPY: () = COPY_MARKER::<Foo>;
+```
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -8671,6 +9025,22 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
+ label: "gpu_launch_sized_workgroup_mem",
+ description: r##"# `gpu_launch_sized_workgroup_mem`
+
+
+
+The tracking issue for this feature is: [#135513]
+
+[#135513]: https://github.com/rust-lang/rust/issues/135513
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "guard_patterns",
description: r##"# `guard_patterns`
@@ -9034,8 +9404,24 @@ The tracking issue for this feature is: [#99069]
deny_since: None,
},
Lint {
- label: "integer_extend_truncate",
- description: r##"# `integer_extend_truncate`
+ label: "integer_cast_extras",
+ description: r##"# `integer_cast_extras`
+
+
+
+The tracking issue for this feature is: [#154650]
+
+[#154650]: https://github.com/rust-lang/rust/issues/154650
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "integer_widen_truncate",
+ description: r##"# `integer_widen_truncate`
@@ -10547,15 +10933,116 @@ The tracking issue for this feature is: [#154042]
},
Lint {
label: "min_generic_const_args",
- description: r##"# `min_generic_const_args`
+ description: r##"# min_generic_const_args
-Enables the generic const args MVP (only bare paths, not arbitrary computation).
+Enables the generic const args MVP (paths to type const items and constructors for ADTs and primitives).
The tracking issue for this feature is: [#132980]
[#132980]: https://github.com/rust-lang/rust/issues/132980
------------------------
+
+Warning: This feature is incomplete; its design and syntax may change.
+
+This feature acts as a minimal alternative to [generic_const_exprs] that allows a smaller subset of functionality,
+and uses a different approach for implementation. It is intentionally more restrictive, which helps with avoiding edge
+cases that make the `generic_const_exprs` hard to implement properly. See [Feature background][feature_background]
+for more details.
+
+Related features: [generic_const_args], [generic_const_items].
+
+[feature_background]: https://github.com/rust-lang/project-const-generics/blob/main/documents/min_const_generics_plan.md
+[generic_const_exprs]: generic-const-exprs.md
+[generic_const_args]: generic-const-args.md
+[generic_const_items]: generic-const-items.md
+
+## `type const` syntax
+
+This feature introduces new syntax: `type const`.
+Constants marked as `type const` are allowed to be used in type contexts, e.g.:
+
+```compile_fail
+#![allow(incomplete_features)]
+#![feature(min_generic_const_args)]
+
+type const X: usize = 1;
+const Y: usize = 1;
+
+struct Foo {
+ good_arr: [(); X], // Allowed
+ bad_arr: [(); Y], // Will not compile, `Y` must be `type const`.
+}
+```
+
+## Examples
+
+```rust
+#![allow(incomplete_features)]
+#![feature(min_generic_const_args)]
+
+trait Bar {
+ type const VAL: usize;
+ type const VAL2: usize;
+}
+
+struct Baz;
+
+impl Bar for Baz {
+ type const VAL: usize = 2;
+ type const VAL2: usize = const { Self::VAL * 2 };
+}
+
+struct Foo<B: Bar> {
+ arr1: [usize; B::VAL],
+ arr2: [usize; B::VAL2],
+}
+```
+
+Note that with [generic_const_exprs] the same example would look as follows:
+
+```rust
+#![allow(incomplete_features)]
+#![feature(generic_const_exprs)]
+
+trait Bar {
+ const VAL: usize;
+ const VAL2: usize;
+}
+
+struct Baz;
+
+impl Bar for Baz {
+ const VAL: usize = 2;
+ const VAL2: usize = const { Self::VAL * 2 };
+}
+
+struct Foo<B: Bar>
+where
+ [(); B::VAL]:,
+ [(); B::VAL2]:,
+{
+ arr1: [usize; B::VAL],
+ arr2: [usize; B::VAL2],
+}
+```
+
+Use of const functions is allowed:
+
+```rust
+#![allow(incomplete_features)]
+#![feature(min_generic_const_args)]
+
+const VAL: usize = 1;
+
+const fn inc(val: usize) -> usize {
+ val + 1
+}
+
+type const INC: usize = const { inc(VAL) };
+
+const ARR: [usize; INC] = [0; INC];
+```
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -11802,6 +12289,22 @@ The tracking issue for this feature is: [#142503]
deny_since: None,
},
Lint {
+ label: "pathbuf_into_string",
+ description: r##"# `pathbuf_into_string`
+
+
+
+The tracking issue for this feature is: [#156203]
+
+[#156203]: https://github.com/rust-lang/rust/issues/156203
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "pattern",
description: r##"# `pattern`
@@ -11958,6 +12461,20 @@ The tracking issue for this feature is: [#130494]
deny_since: None,
},
Lint {
+ label: "pin_macro_internals",
+ description: r##"# `pin_macro_internals`
+
+
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "pointer_is_aligned_to",
description: r##"# `pointer_is_aligned_to`
@@ -14442,6 +14959,22 @@ The tracking issue for this feature is: [#119639]
deny_since: None,
},
Lint {
+ label: "tcp_keepalive",
+ description: r##"# `tcp_keepalive`
+
+
+
+The tracking issue for this feature is: [#155889]
+
+[#155889]: https://github.com/rust-lang/rust/issues/155889
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "tcp_linger",
description: r##"# `tcp_linger`
@@ -14912,7 +15445,7 @@ pub fn main() {
foo(&1);
// Use trait alias for trait objects.
- let a: &Bar = &123;
+ let a: &dyn Bar = &123;
println!("{:?}", a);
let b = Box::new(456) as Box<dyn Foo>;
println!("{:?}", b);
@@ -16265,6 +16798,22 @@ The tracking issue for this feature is: [#146954]
deny_since: None,
},
Lint {
+ label: "view_types",
+ description: r##"# `view_types`
+
+Allows view types.
+
+The tracking issue for this feature is: [#155938]
+
+[#155938]: https://github.com/rust-lang/rust/issues/155938
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "waker_fn",
description: r##"# `waker_fn`
@@ -16445,6 +16994,22 @@ This feature is internal to the Rust compiler and is not intended for general us
deny_since: None,
},
Lint {
+ label: "windows_permissions_ext",
+ description: r##"# `windows_permissions_ext`
+
+
+
+The tracking issue for this feature is: [#152956]
+
+[#152956]: https://github.com/rust-lang/rust/issues/152956
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "windows_process_exit_code_from",
description: r##"# `windows_process_exit_code_from`
diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs
index 08cf1eeed3..838aac2283 100644
--- a/crates/ide-db/src/helpers.rs
+++ b/crates/ide-db/src/helpers.rs
@@ -7,7 +7,7 @@ use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics};
use span::{Edition, FileId};
use syntax::{
AstToken, SyntaxKind, SyntaxToken, ToSmolStr, TokenAtOffset,
- ast::{self, make},
+ ast::{self, make, syntax_factory::SyntaxFactory},
};
use crate::{
@@ -57,6 +57,31 @@ pub fn mod_path_to_ast(path: &hir::ModPath, edition: Edition) -> ast::Path {
make::path_from_segments(segments, is_abs)
}
+pub fn mod_path_to_ast_with_factory(
+ make: &SyntaxFactory,
+ path: &hir::ModPath,
+ edition: Edition,
+) -> ast::Path {
+ let _p = tracing::info_span!("mod_path_to_ast").entered();
+
+ let mut segments = Vec::new();
+ let mut is_abs = false;
+ match path.kind {
+ hir::PathKind::Plain => {}
+ hir::PathKind::SELF => segments.push(make.path_segment_self()),
+ hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make.path_segment_super())),
+ hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => {
+ segments.push(make.path_segment_crate())
+ }
+ hir::PathKind::Abs => is_abs = true,
+ }
+
+ segments.extend(path.segments().iter().map(|segment| {
+ make.path_segment(make.name_ref(&segment.display_no_db(edition).to_smolstr()))
+ }));
+ make.path_from_segments(segments, is_abs)
+}
+
/// Iterates all `ModuleDef`s and `Impl` blocks of the given file.
pub fn visit_file_defs(
sema: &Semantics<'_, RootDatabase>,
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index 9018552afb..e0501b5e44 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -314,7 +314,7 @@ pub struct LocatedImport {
pub item_to_import: ItemInNs,
/// The path import candidate, resolved.
///
- /// Not necessary matches the import:
+ /// Not necessarily matches the import:
/// For any associated constant from the trait, we try to access as `some::path::SomeStruct::ASSOC_`
/// the original item is the associated constant, but the import has to be a trait that
/// defines this constant.
@@ -454,6 +454,7 @@ impl<'db> ImportAssets<'db> {
|trait_to_import| {
!scope_definitions
.contains(&ScopeDef::ModuleDef(ModuleDef::Trait(trait_to_import)))
+ && !scope.can_use_trait_methods(trait_to_import)
},
),
}
diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs
index fe30a4dc5c..c3949f8713 100644
--- a/crates/ide-db/src/imports/insert_use.rs
+++ b/crates/ide-db/src/imports/insert_use.rs
@@ -6,13 +6,11 @@ use std::cmp::Ordering;
use hir::Semantics;
use syntax::{
- Direction, NodeOrToken, SyntaxKind, SyntaxNode, algo,
+ NodeOrToken, SyntaxKind, SyntaxNode,
ast::{
- self, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind,
- edit_in_place::Removable, make,
+ self, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind, edit::IndentLevel,
},
syntax_editor::{Position, SyntaxEditor},
- ted,
};
use crate::{
@@ -150,24 +148,6 @@ impl ImportScope {
ImportScopeKind::Block(block) => block.syntax(),
}
}
-
- pub fn clone_for_update(&self) -> Self {
- Self {
- kind: match &self.kind {
- ImportScopeKind::File(file) => ImportScopeKind::File(file.clone_for_update()),
- ImportScopeKind::Module(item_list) => {
- ImportScopeKind::Module(item_list.clone_for_update())
- }
- ImportScopeKind::Block(block) => ImportScopeKind::Block(block.clone_for_update()),
- },
- required_cfgs: self.required_cfgs.iter().map(|attr| attr.clone_for_update()).collect(),
- }
- }
-}
-
-/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
-pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) {
- insert_use_with_alias_option(scope, path, cfg, None);
}
/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
@@ -180,11 +160,43 @@ pub fn insert_use_with_editor(
insert_use_with_alias_option_with_editor(scope, path, cfg, None, syntax_editor);
}
-pub fn insert_use_as_alias(
+pub fn insert_uses_with_editor(
+ scope: &ImportScope,
+ paths: impl IntoIterator<Item = ast::Path>,
+ cfg: &InsertUseConfig,
+ syntax_editor: &SyntaxEditor,
+) {
+ let paths = paths.into_iter().collect::<Vec<_>>();
+ if paths.len() > 1
+ && scope.as_syntax_node().parent().is_none()
+ && scope.required_cfgs.is_empty()
+ && !scope.as_syntax_node().children().any(|node| ast::Use::cast(node).is_some())
+ {
+ let make = syntax_editor.make();
+ let elements = paths
+ .into_iter()
+ .flat_map(|path| {
+ let use_tree = make.use_tree(path, None, None, false);
+ let use_item = make.use_(None, None, use_tree);
+ [use_item.syntax().clone().into(), make.whitespace("\n").into()]
+ })
+ .chain([make.whitespace("\n").into()])
+ .collect();
+ syntax_editor.insert_all(Position::first_child_of(scope.as_syntax_node()), elements);
+ return;
+ }
+
+ for path in paths {
+ insert_use_with_editor(scope, path, cfg, syntax_editor);
+ }
+}
+
+pub fn insert_use_as_alias_with_editor(
scope: &ImportScope,
path: ast::Path,
cfg: &InsertUseConfig,
edition: span::Edition,
+ editor: &SyntaxEditor,
) {
let text: &str = "use foo as _";
let parse = syntax::SourceFile::parse(text, edition);
@@ -196,71 +208,7 @@ pub fn insert_use_as_alias(
.expect("Failed to make ast node `Rename`");
let alias = node.rename();
- insert_use_with_alias_option(scope, path, cfg, alias);
-}
-
-fn insert_use_with_alias_option(
- scope: &ImportScope,
- path: ast::Path,
- cfg: &InsertUseConfig,
- alias: Option<ast::Rename>,
-) {
- let _p = tracing::info_span!("insert_use_with_alias_option").entered();
- let mut mb = match cfg.granularity {
- ImportGranularity::Crate => Some(MergeBehavior::Crate),
- ImportGranularity::Module => Some(MergeBehavior::Module),
- ImportGranularity::One => Some(MergeBehavior::One),
- ImportGranularity::Item => None,
- };
- if !cfg.enforce_granularity {
- let file_granularity = guess_granularity_from_scope(scope);
- mb = match file_granularity {
- ImportGranularityGuess::Unknown => mb,
- ImportGranularityGuess::Item => None,
- ImportGranularityGuess::Module => Some(MergeBehavior::Module),
- // We use the user's setting to infer if this is module or item.
- ImportGranularityGuess::ModuleOrItem => match mb {
- Some(MergeBehavior::Module) | None => mb,
- // There isn't really a way to decide between module or item here, so we just pick one.
- // FIXME: Maybe it is possible to infer based on semantic analysis?
- Some(MergeBehavior::One | MergeBehavior::Crate) => Some(MergeBehavior::Module),
- },
- ImportGranularityGuess::Crate => Some(MergeBehavior::Crate),
- ImportGranularityGuess::CrateOrModule => match mb {
- Some(MergeBehavior::Crate | MergeBehavior::Module) => mb,
- Some(MergeBehavior::One) | None => Some(MergeBehavior::Crate),
- },
- ImportGranularityGuess::One => Some(MergeBehavior::One),
- };
- }
-
- let mut use_tree = make::use_tree(path, None, alias, false);
- if mb == Some(MergeBehavior::One) && use_tree.path().is_some() {
- use_tree = use_tree.clone_for_update();
- use_tree.wrap_in_tree_list();
- }
- let use_item = make::use_(None, None, use_tree).clone_for_update();
- for attr in
- scope.required_cfgs.iter().map(|attr| attr.syntax().clone_subtree().clone_for_update())
- {
- ted::insert(ted::Position::first_child_of(use_item.syntax()), attr);
- }
-
- // merge into existing imports if possible
- if let Some(mb) = mb {
- let filter = |it: &_| !(cfg.skip_glob_imports && ast::Use::is_simple_glob(it));
- for existing_use in
- scope.as_syntax_node().children().filter_map(ast::Use::cast).filter(filter)
- {
- if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) {
- ted::replace(existing_use.syntax(), merged.syntax());
- return;
- }
- }
- }
- // either we weren't allowed to merge or there is no import that fits the merge conditions
- // so look for the place we have to insert to
- insert_use_(scope, use_item, cfg.group);
+ insert_use_with_alias_option_with_editor(scope, path, cfg, alias, editor);
}
fn insert_use_with_alias_option_with_editor(
@@ -300,14 +248,14 @@ fn insert_use_with_alias_option_with_editor(
};
}
- let use_tree = make.use_tree(path, None, alias, false);
- if mb == Some(MergeBehavior::One) && use_tree.path().is_some() {
- use_tree.wrap_in_tree_list();
- }
- let use_item = make::use_(None, None, use_tree);
- for attr in scope.required_cfgs.iter().map(|attr| attr.syntax().clone()) {
- syntax_editor.insert(Position::first_child_of(use_item.syntax()), attr);
+ let mut use_tree = make.use_tree(path, None, alias, false);
+ if mb == Some(MergeBehavior::One)
+ && use_tree.path().is_some()
+ && let Some(wrapped) = use_tree.wrap_in_tree_list_with_editor()
+ {
+ use_tree = wrapped;
}
+ let use_item = make.use_(scope.required_cfgs.iter().cloned().rev(), None, use_tree);
// merge into existing imports if possible
if let Some(mb) = mb {
@@ -326,24 +274,14 @@ fn insert_use_with_alias_option_with_editor(
insert_use_with_editor_(scope, use_item, cfg.group, syntax_editor);
}
-pub fn ast_to_remove_for_path_in_use_stmt(path: &ast::Path) -> Option<Box<dyn Removable>> {
- // FIXME: improve this
- if path.parent_path().is_some() {
- return None;
- }
- let use_tree = path.syntax().parent().and_then(ast::UseTree::cast)?;
+pub fn remove_use_tree_if_simple(use_tree: &ast::UseTree, editor: &SyntaxEditor) {
if use_tree.use_tree_list().is_some() || use_tree.star_token().is_some() {
- return None;
+ return;
}
if let Some(use_) = use_tree.syntax().parent().and_then(ast::Use::cast) {
- return Some(Box::new(use_));
- }
- Some(Box::new(use_tree))
-}
-
-pub fn remove_path_if_in_use_stmt(path: &ast::Path) {
- if let Some(node) = ast_to_remove_for_path_in_use_stmt(path) {
- node.remove();
+ syntax::syntax_editor::Removable::remove(&use_, editor);
+ } else {
+ syntax::syntax_editor::Removable::remove(use_tree, editor);
}
}
@@ -482,123 +420,6 @@ fn guess_granularity_from_scope(scope: &ImportScope) -> ImportGranularityGuess {
}
}
-fn insert_use_(scope: &ImportScope, use_item: ast::Use, group_imports: bool) {
- let scope_syntax = scope.as_syntax_node();
- let insert_use_tree =
- use_item.use_tree().expect("`use_item` should have a use tree for `insert_path`");
- let group = ImportGroup::new(&insert_use_tree);
- let path_node_iter = scope_syntax
- .children()
- .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node)))
- .flat_map(|(use_, node)| {
- let tree = use_.use_tree()?;
- Some((tree, node))
- });
-
- if group_imports {
- // Iterator that discards anything that's not in the required grouping
- // This implementation allows the user to rearrange their import groups as this only takes the first group that fits
- let group_iter = path_node_iter
- .clone()
- .skip_while(|(use_tree, ..)| ImportGroup::new(use_tree) != group)
- .take_while(|(use_tree, ..)| ImportGroup::new(use_tree) == group);
-
- // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place
- let mut last = None;
- // find the element that would come directly after our new import
- let post_insert: Option<(_, SyntaxNode)> = group_iter
- .inspect(|(.., node)| last = Some(node.clone()))
- .find(|(use_tree, _)| use_tree_cmp(&insert_use_tree, use_tree) != Ordering::Greater);
-
- if let Some((.., node)) = post_insert {
- cov_mark::hit!(insert_group);
- // insert our import before that element
- return ted::insert(ted::Position::before(node), use_item.syntax());
- }
- if let Some(node) = last {
- cov_mark::hit!(insert_group_last);
- // there is no element after our new import, so append it to the end of the group
- return ted::insert(ted::Position::after(node), use_item.syntax());
- }
-
- // the group we were looking for actually doesn't exist, so insert
-
- let mut last = None;
- // find the group that comes after where we want to insert
- let post_group = path_node_iter
- .inspect(|(.., node)| last = Some(node.clone()))
- .find(|(use_tree, ..)| ImportGroup::new(use_tree) > group);
- if let Some((.., node)) = post_group {
- cov_mark::hit!(insert_group_new_group);
- ted::insert(ted::Position::before(&node), use_item.syntax());
- if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) {
- ted::insert(ted::Position::after(node), make::tokens::single_newline());
- }
- return;
- }
- // there is no such group, so append after the last one
- if let Some(node) = last {
- cov_mark::hit!(insert_group_no_group);
- ted::insert(ted::Position::after(&node), use_item.syntax());
- ted::insert(ted::Position::after(node), make::tokens::single_newline());
- return;
- }
- } else {
- // There exists a group, so append to the end of it
- if let Some((_, node)) = path_node_iter.last() {
- cov_mark::hit!(insert_no_grouping_last);
- ted::insert(ted::Position::after(node), use_item.syntax());
- return;
- }
- }
-
- let l_curly = match &scope.kind {
- ImportScopeKind::File(_) => None,
- // don't insert the imports before the item list/block expr's opening curly brace
- ImportScopeKind::Module(item_list) => item_list.l_curly_token(),
- // don't insert the imports before the item list's opening curly brace
- ImportScopeKind::Block(block) => block.l_curly_token(),
- };
- // there are no imports in this file at all
- // so put the import after all inner module attributes and possible license header comments
- if let Some(last_inner_element) = scope_syntax
- .children_with_tokens()
- // skip the curly brace
- .skip(l_curly.is_some() as usize)
- .take_while(|child| match child {
- NodeOrToken::Node(node) => {
- is_inner_attribute(node.clone()) && ast::Item::cast(node.clone()).is_none()
- }
- NodeOrToken::Token(token) => {
- [SyntaxKind::WHITESPACE, SyntaxKind::COMMENT, SyntaxKind::SHEBANG]
- .contains(&token.kind())
- }
- })
- .filter(|child| child.as_token().is_none_or(|t| t.kind() != SyntaxKind::WHITESPACE))
- .last()
- {
- cov_mark::hit!(insert_empty_inner_attr);
- ted::insert(ted::Position::after(&last_inner_element), use_item.syntax());
- ted::insert(ted::Position::after(last_inner_element), make::tokens::single_newline());
- } else {
- match l_curly {
- Some(b) => {
- cov_mark::hit!(insert_empty_module);
- ted::insert(ted::Position::after(&b), make::tokens::single_newline());
- ted::insert(ted::Position::after(&b), use_item.syntax());
- }
- None => {
- cov_mark::hit!(insert_empty_file);
- ted::insert(
- ted::Position::first_child_of(scope_syntax),
- make::tokens::blank_line(),
- );
- ted::insert(ted::Position::first_child_of(scope_syntax), use_item.syntax());
- }
- }
- }
-}
-
fn insert_use_with_editor_(
scope: &ImportScope,
use_item: ast::Use,
@@ -636,12 +457,18 @@ fn insert_use_with_editor_(
if let Some((.., node)) = post_insert {
cov_mark::hit!(insert_group);
// insert our import before that element
- return syntax_editor.insert(Position::before(node), use_item.syntax());
+ return syntax_editor.insert_all(
+ Position::before(node),
+ vec![use_item.syntax().clone().into(), make.whitespace("\n").into()],
+ );
}
if let Some(node) = last {
cov_mark::hit!(insert_group_last);
// there is no element after our new import, so append it to the end of the group
- return syntax_editor.insert(Position::after(node), use_item.syntax());
+ return syntax_editor.insert_all(
+ Position::after(node),
+ vec![make.whitespace("\n").into(), use_item.syntax().clone().into()],
+ );
}
// the group we were looking for actually doesn't exist, so insert
@@ -653,24 +480,29 @@ fn insert_use_with_editor_(
.find(|(use_tree, ..)| ImportGroup::new(use_tree) > group);
if let Some((.., node)) = post_group {
cov_mark::hit!(insert_group_new_group);
- syntax_editor.insert(Position::before(&node), use_item.syntax());
- if let Some(node) = algo::non_trivia_sibling(node.into(), Direction::Prev) {
- syntax_editor.insert(Position::after(node), make.whitespace("\n"));
- }
+ syntax_editor.insert_all(
+ Position::before(&node),
+ vec![use_item.syntax().clone().into(), make.whitespace("\n\n").into()],
+ );
return;
}
// there is no such group, so append after the last one
if let Some(node) = last {
cov_mark::hit!(insert_group_no_group);
- syntax_editor.insert(Position::after(&node), use_item.syntax());
- syntax_editor.insert(Position::after(node), make.whitespace("\n"));
+ syntax_editor.insert_all(
+ Position::after(&node),
+ vec![make.whitespace("\n\n").into(), use_item.syntax().clone().into()],
+ );
return;
}
} else {
// There exists a group, so append to the end of it
if let Some((_, node)) = path_node_iter.last() {
cov_mark::hit!(insert_no_grouping_last);
- syntax_editor.insert(Position::after(node), use_item.syntax());
+ syntax_editor.insert_all(
+ Position::after(node),
+ vec![make.whitespace("\n").into(), use_item.syntax().clone().into()],
+ );
return;
}
}
@@ -701,20 +533,38 @@ fn insert_use_with_editor_(
.last()
{
cov_mark::hit!(insert_empty_inner_attr);
- syntax_editor.insert(Position::after(&last_inner_element), use_item.syntax());
- syntax_editor.insert(Position::after(last_inner_element), make.whitespace("\n"));
+ let indent = if l_curly.is_some() {
+ IndentLevel::from_node(scope_syntax) + 1
+ } else {
+ IndentLevel::zero()
+ };
+ syntax_editor.insert_all(
+ Position::after(&last_inner_element),
+ vec![
+ make.whitespace(&format!("\n\n{indent}")).into(),
+ use_item.syntax().clone().into(),
+ ],
+ );
} else {
match l_curly {
Some(b) => {
cov_mark::hit!(insert_empty_module);
- syntax_editor.insert(Position::after(&b), make.whitespace("\n"));
- syntax_editor.insert_with_whitespace(Position::after(&b), use_item.syntax());
+ let indent = IndentLevel::from_node(scope_syntax) + 1;
+ syntax_editor.insert_all(
+ Position::after(&b),
+ vec![
+ make.whitespace(&format!("\n{indent}")).into(),
+ use_item.syntax().clone().into(),
+ make.whitespace("\n").into(),
+ ],
+ );
}
None => {
cov_mark::hit!(insert_empty_file);
- syntax_editor
- .insert(Position::first_child_of(scope_syntax), make.whitespace("\n\n"));
- syntax_editor.insert(Position::first_child_of(scope_syntax), use_item.syntax());
+ syntax_editor.insert_all(
+ Position::first_child_of(scope_syntax),
+ vec![use_item.syntax().clone().into(), make.whitespace("\n\n").into()],
+ );
}
}
}
diff --git a/crates/ide-db/src/imports/insert_use/tests.rs b/crates/ide-db/src/imports/insert_use/tests.rs
index 6c7b97458d..4fa05c4603 100644
--- a/crates/ide-db/src/imports/insert_use/tests.rs
+++ b/crates/ide-db/src/imports/insert_use/tests.rs
@@ -1342,14 +1342,14 @@ fn check_with_config(
};
let sema = &Semantics::new(&db);
let source_file = sema.parse(file_id);
+ let (editor, _) = SyntaxEditor::new(source_file.syntax().clone());
let file = pos
.and_then(|pos| source_file.syntax().token_at_offset(pos.expect_offset()).next()?.parent())
.and_then(|it| ImportScope::find_insert_use_container(&it, sema))
.unwrap_or_else(|| ImportScope {
- kind: ImportScopeKind::File(source_file),
+ kind: ImportScopeKind::File(source_file.clone()),
required_cfgs: vec![],
- })
- .clone_for_update();
+ });
let path = ast::SourceFile::parse(&format!("use {path};"), span::Edition::CURRENT)
.tree()
.syntax()
@@ -1357,8 +1357,9 @@ fn check_with_config(
.find_map(ast::Path::cast)
.unwrap();
- insert_use(&file, path, config);
- let result = file.as_syntax_node().ancestors().last().unwrap().to_string();
+ insert_use_with_editor(&file, path, config, &editor);
+ let edit = editor.finish();
+ let result = edit.new_root().to_string();
assert_eq_text!(&trim_indent(ra_fixture_after), &result);
}
diff --git a/crates/ide-db/src/imports/merge_imports.rs b/crates/ide-db/src/imports/merge_imports.rs
index 76645464dd..bbd351a4cc 100644
--- a/crates/ide-db/src/imports/merge_imports.rs
+++ b/crates/ide-db/src/imports/merge_imports.rs
@@ -256,10 +256,7 @@ pub fn try_normalize_import(use_item: &ast::Use, style: NormalizationStyle) -> O
Some(use_item)
}
-pub fn try_normalize_use_tree_mut(
- use_tree: &ast::UseTree,
- style: NormalizationStyle,
-) -> Option<()> {
+fn try_normalize_use_tree_mut(use_tree: &ast::UseTree, style: NormalizationStyle) -> Option<()> {
if style == NormalizationStyle::One {
let mut modified = false;
modified |= use_tree.wrap_in_tree_list().is_some();
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index 6b72a30339..6180e3186c 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -112,7 +112,7 @@ impl Clone for RootDatabase {
storage: self.storage.clone(),
files: self.files.clone(),
crates_map: self.crates_map.clone(),
- nonce: Nonce::new(),
+ nonce: self.nonce,
}
}
}
@@ -402,10 +402,16 @@ impl<'a> MiniCore<'a> {
impl std::fmt::Debug for MiniCore<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_tuple("MiniCore")
- // don't print the whole contents if they correspond to the default
- .field(if self.0 == test_utils::MiniCore::RAW_SOURCE { &"<default>" } else { &self.0 })
- .finish()
+ let mut d = f.debug_tuple("MiniCore");
+ if self.0 == test_utils::MiniCore::RAW_SOURCE {
+ // Don't print the whole contents if they correspond to the default.
+ // The `format_args!` makes it so that the output is
+ // `MiniCore(<default>)` and not `MiniCore("<default>").
+ d.field(&format_args!("<default>"));
+ } else {
+ d.field(&self.0);
+ };
+ d.finish()
}
}
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index 2d4a6b8b5b..661f0cff8e 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -1,6 +1,6 @@
//! See [`PathTransform`].
-use crate::helpers::mod_path_to_ast;
+use crate::helpers::mod_path_to_ast_with_factory;
use either::Either;
use hir::{
AsAssocItem, FindPathConfig, HirDisplay, HirFileId, ModuleDef, SemanticsScope,
@@ -151,7 +151,7 @@ impl<'a> PathTransform<'a> {
prettify_macro_expansion(
db,
node,
- &db.expansion_span_map(file_id),
+ db.expansion_span_map(file_id),
self.target_scope.module().krate(db).into(),
)
}
@@ -218,8 +218,7 @@ impl<'a> PathTransform<'a> {
}
}
(Either::Left(k), None) => {
- if let Some(default) =
- k.default(db, target_module.krate(db).to_display_target(db))
+ if let Some(default) = k.default_source_code(db, target_module)
&& let Some(default) = default.expr()
{
const_substs.insert(k, default.syntax().clone());
@@ -354,6 +353,7 @@ impl Ctx<'_> {
}
fn transform_path_(&self, editor: &SyntaxEditor, path: &ast::Path) -> Option<()> {
+ let make = editor.make();
if path.qualifier().is_some() {
return None;
}
@@ -397,7 +397,14 @@ impl Ctx<'_> {
hir::ModuleDef::Trait(trait_ref),
cfg,
)?;
- match make::ty_path(mod_path_to_ast(&found_path, self.target_edition)) {
+ match make
+ .ty_path(mod_path_to_ast_with_factory(
+ make,
+ &found_path,
+ self.target_edition,
+ ))
+ .into()
+ {
ast::Type::PathType(path_ty) => Some(path_ty),
_ => None,
}
@@ -447,7 +454,7 @@ impl Ctx<'_> {
allow_unstable: true,
};
let found_path = self.target_module.find_path(self.source_scope.db, def, cfg)?;
- let res = mod_path_to_ast(&found_path, self.target_edition);
+ let res = mod_path_to_ast_with_factory(make, &found_path, self.target_edition);
let (res_editor, res) = SyntaxEditor::with_ast_node(&res);
if let Some(args) = path.segment().and_then(|it| it.generic_arg_list())
&& let Some(segment) = res.segment()
@@ -501,7 +508,8 @@ impl Ctx<'_> {
)?;
if let Some(qual) =
- mod_path_to_ast(&found_path, self.target_edition).qualifier()
+ mod_path_to_ast_with_factory(make, &found_path, self.target_edition)
+ .qualifier()
{
editor.replace(
path.syntax(),
@@ -524,8 +532,9 @@ impl Ctx<'_> {
fn transform_ident_pat(&self, editor: &SyntaxEditor, ident_pat: &ast::IdentPat) -> Option<()> {
let name = ident_pat.name()?;
+ let make = editor.make();
- let temp_path = make::path_from_text(&name.text());
+ let temp_path = make.path_from_text(&name.text());
let resolution = self.source_scope.speculative_resolve(&temp_path)?;
@@ -580,7 +589,7 @@ impl Ctx<'_> {
let found_path = self.target_module.find_path(self.source_scope.db, def, cfg)?;
editor.replace(
ident_pat.syntax(),
- mod_path_to_ast(&found_path, self.target_edition).syntax(),
+ mod_path_to_ast_with_factory(make, &found_path, self.target_edition).syntax(),
);
Some(())
}
diff --git a/crates/ide-db/src/prime_caches.rs b/crates/ide-db/src/prime_caches.rs
index 12a48d65ac..fb7edb1acd 100644
--- a/crates/ide-db/src/prime_caches.rs
+++ b/crates/ide-db/src/prime_caches.rs
@@ -5,7 +5,7 @@
use std::panic::AssertUnwindSafe;
use base_db::all_crates;
-use hir::{Symbol, import_map::ImportMap};
+use hir::{Symbol, import_map::ImportMap, sym};
use rustc_hash::FxHashMap;
use salsa::{Cancelled, Database};
@@ -315,5 +315,5 @@ fn crate_name(db: &RootDatabase, krate: Crate) -> Symbol {
.display_name
.as_deref()
.cloned()
- .unwrap_or_else(|| Symbol::integer(salsa::plumbing::AsId::as_id(&krate).index() as usize))
+ .unwrap_or_else(|| sym::Integer::get(salsa::plumbing::AsId::as_id(&krate).index() as usize))
}
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs
index b18ed69d80..ff4d5a2886 100644
--- a/crates/ide-db/src/rename.rs
+++ b/crates/ide-db/src/rename.rs
@@ -28,7 +28,9 @@ use crate::{
};
use base_db::AnchoredPathBuf;
use either::Either;
-use hir::{FieldSource, FileRange, InFile, ModuleSource, Name, Semantics, sym};
+use hir::{FieldSource, FileRange, HasCrate, InFile, ModuleSource, Name, Semantics, sym};
+use itertools::Itertools;
+use rustc_hash::FxHashSet;
use span::{Edition, FileId, SyntaxContext};
use stdx::{TupleExt, never};
use syntax::{
@@ -405,6 +407,11 @@ fn rename_reference(
source_edit_from_references(sema.db, references, def, &new_name, edition),
)
}));
+
+ if let Definition::Field(field) = def {
+ rename_field_constructors(sema, field, &new_name, &mut source_change, config);
+ }
+
if rename_definition == RenameDefinition::Yes {
// This needs to come after the references edits, because we change the annotation of existing edits
// if a conflict is detected.
@@ -415,6 +422,104 @@ fn rename_reference(
Ok(source_change)
}
+fn rename_field_constructors(
+ sema: &Semantics<'_, RootDatabase>,
+ field: hir::Field,
+ new_name: &Name,
+ source_change: &mut SourceChange,
+ config: &RenameConfig,
+) {
+ let db = sema.db;
+ let old_name = field.name(db);
+ let adt = field.parent_def(db).adt(db);
+ adt.ty(db).iterate_assoc_items(db, |assoc_item| {
+ let ctor = assoc_item.as_function()?;
+ if ctor.has_self_param(db) {
+ return None;
+ }
+ if ctor.ret_type(db).as_adt() != Some(adt) {
+ return None;
+ }
+
+ let source = sema.source(ctor);
+ let return_values = sema
+ .fn_return_points(ctor)
+ .into_iter()
+ .filter_map(|ret| ret.value.expr())
+ .chain(source.and_then(|source| source.value.body()?.tail_expr()));
+ // FIXME: We could maybe skip ifs etc..
+
+ let get_renamed_field = |mut expr| {
+ while let ast::Expr::ParenExpr(e) = &expr {
+ expr = e.expr()?;
+ }
+ let ast::Expr::RecordExpr(expr) = expr else { return None };
+ if sema.type_of_expr(&expr.clone().into())?.original.as_adt()? != adt {
+ return None;
+ };
+ expr.record_expr_field_list()?.fields().find_map(|record_field| {
+ if record_field.name_ref().is_none()
+ && Name::new_root(&record_field.field_name()?.text()) == old_name
+ && let ast::Expr::PathExpr(field_name) = record_field.expr()?
+ {
+ field_name.path()
+ } else {
+ None
+ }
+ })
+ };
+ let renamed_fields = return_values
+ .map(get_renamed_field)
+ .map(|renamed_field| {
+ let renamed_field = renamed_field?;
+ let hir::PathResolution::Local(local) = sema.resolve_path(&renamed_field)? else {
+ return None;
+ };
+ let range = sema.original_range_opt(renamed_field.syntax())?.range;
+ Some((range, local))
+ })
+ .collect::<Option<Vec<_>>>()?;
+
+ let edition = ctor.krate(db).edition(db);
+ let locals = renamed_fields.iter().map(|&(_, local)| local).collect::<FxHashSet<_>>();
+ let mut all_locals_source_change = SourceChange::default();
+ for local in locals {
+ let mut local_source_change = Definition::Local(local)
+ .rename(sema, new_name.as_str(), RenameDefinition::Yes, config)
+ .ok()?;
+
+ let (edit, _snippet) =
+ local_source_change.source_file_edits.values_mut().exactly_one().ok()?;
+
+ // The struct literal will have an edit `old_name -> old_name: new_name`, and we need to remove
+ // that, as we want an overlapping edit `old_name -> new_name`.
+ for &(field_range, _) in &renamed_fields {
+ edit.cancel_edits_touching(field_range);
+ }
+
+ all_locals_source_change =
+ std::mem::take(&mut all_locals_source_change).merge(local_source_change);
+ }
+ let (edit, _snippet) =
+ all_locals_source_change.source_file_edits.values_mut().exactly_one().ok()?;
+ for &(field_range, _) in &renamed_fields {
+ edit.union(TextEdit::replace(field_range, new_name.display(db, edition).to_string()))
+ .unwrap();
+ }
+
+ let file_id = *all_locals_source_change.source_file_edits.keys().exactly_one().ok()?;
+ if let Some((edit, _snippet)) = source_change.source_file_edits.get_mut(&file_id) {
+ for &(field_range, _) in &renamed_fields {
+ edit.cancel_edits_touching(field_range);
+ }
+ }
+
+ *source_change = std::mem::take(source_change).merge(all_locals_source_change);
+
+ None::<std::convert::Infallible>
+ });
+}
+
pub fn source_edit_from_references(
db: &RootDatabase,
references: &[FileReference],
@@ -596,28 +701,36 @@ fn source_edit_from_def(
for source in local.sources(sema.db) {
let source = match source.source.clone().original_ast_node_rooted(sema.db) {
Some(source) => source,
- None => match source
- .source
- .syntax()
- .original_file_range_opt(sema.db)
- .map(TupleExt::head)
- {
- Some(FileRange { file_id: file_id2, range }) => {
- file_id = Some(file_id2);
- edit.replace(
- range,
- new_name.display(sema.db, file_id2.edition(sema.db)).to_string(),
- );
- continue;
- }
- None => {
- bail!("Can't rename local that is defined in a macro declaration")
+ None => {
+ match source
+ .as_ident_pat()
+ .and_then(|x| x.name())
+ .and_then(|x| sema.original_range_opt(x.syntax()))
+ .or_else(|| {
+ source
+ .source
+ .syntax()
+ .original_file_range_opt(sema.db)
+ .map(TupleExt::head)
+ }) {
+ Some(FileRange { file_id: file_id2, range }) => {
+ file_id = Some(file_id2);
+ edit.replace(
+ range,
+ new_name.display(sema.db, file_id2.edition(sema.db)).to_string(),
+ );
+ continue;
+ }
+ None => {
+ bail!("Can't rename local that is defined in a macro declaration")
+ }
}
- },
+ }
};
file_id = Some(source.file_id);
if let Either::Left(pat) = source.value {
let name_range = pat.name().unwrap().syntax().text_range();
+
// special cases required for renaming fields/locals in Record patterns
if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) {
if let Some(name_ref) = pat_field.name_ref() {
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index f41e293070..d59df3601f 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -440,7 +440,7 @@ impl Definition {
}
}
- pub fn usages<'a>(self, sema: &'a Semantics<'_, RootDatabase>) -> FindUsages<'a> {
+ pub fn usages<'a, 'db>(self, sema: &'a Semantics<'db, RootDatabase>) -> FindUsages<'a, 'db> {
FindUsages {
def: self,
rename: None,
@@ -456,10 +456,10 @@ impl Definition {
}
#[derive(Clone)]
-pub struct FindUsages<'a> {
+pub struct FindUsages<'a, 'db> {
def: Definition,
rename: Option<&'a Rename>,
- sema: &'a Semantics<'a, RootDatabase>,
+ sema: &'a Semantics<'db, RootDatabase>,
scope: Option<&'a SearchScope>,
/// The container of our definition should it be an assoc item
assoc_item_container: Option<hir::AssocItemContainer>,
@@ -473,7 +473,7 @@ pub struct FindUsages<'a> {
exclude_library_files: bool,
}
-impl<'a> FindUsages<'a> {
+impl<'a, 'db> FindUsages<'a, 'db> {
/// Enable searching for `Self` when the definition is a type or `self` for modules.
pub fn include_self_refs(mut self) -> Self {
self.include_self_kw_refs = def_to_ty(self.sema, &self.def);
@@ -858,7 +858,7 @@ impl<'a> FindUsages<'a> {
}
fn search(
- this: &FindUsages<'_>,
+ this: &FindUsages<'_, '_>,
finder: &Finder<'_>,
name: &str,
files: impl Iterator<Item = (Arc<str>, EditionedFileId, TextRange)>,
diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs
index 81b679ead2..766d7251a1 100644
--- a/crates/ide-db/src/source_change.rs
+++ b/crates/ide-db/src/source_change.rs
@@ -1,11 +1,10 @@
//! This modules defines type to represent changes to the source code, that flow
//! from the server to the client.
//!
-//! It can be viewed as a dual for `Change`.
+//! It can be viewed as a dual for [`Change`][vfs::Change].
use std::{collections::hash_map::Entry, fmt, iter, mem};
-use crate::imports::insert_use::{ImportScope, ImportScopeKind};
use crate::text_edit::{TextEdit, TextEditBuilder};
use crate::{SnippetCap, assists::Command, syntax_helpers::tree_diff::diff};
use base_db::AnchoredPathBuf;
@@ -16,7 +15,7 @@ use rustc_hash::FxHashMap;
use span::FileId;
use stdx::never;
use syntax::{
- AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize,
+ AstNode, SyntaxElement, SyntaxNode, SyntaxToken, TextRange, TextSize,
syntax_editor::{SyntaxAnnotation, SyntaxEditor},
};
@@ -229,12 +228,12 @@ pub struct SourceChangeBuilder {
pub snippet_annotations: Vec<(AnnotationSnippet, SyntaxAnnotation)>,
/// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin.
- pub mutated_tree: Option<TreeMutator>,
+ mutated_tree: Option<TreeMutator>,
/// Keeps track of where to place snippets
pub snippet_builder: Option<SnippetBuilder>,
}
-pub struct TreeMutator {
+struct TreeMutator {
immutable: SyntaxNode,
mutable_clone: SyntaxNode,
}
@@ -245,23 +244,6 @@ pub struct SnippetBuilder {
places: Vec<PlaceSnippet>,
}
-impl TreeMutator {
- pub fn new(immutable: &SyntaxNode) -> TreeMutator {
- let immutable = immutable.ancestors().last().unwrap();
- let mutable_clone = immutable.clone_for_update();
- TreeMutator { immutable, mutable_clone }
- }
-
- pub fn make_mut<N: AstNode>(&self, node: &N) -> N {
- N::cast(self.make_syntax_mut(node.syntax())).unwrap()
- }
-
- pub fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode {
- let ptr = SyntaxNodePtr::new(node);
- ptr.to_node(&self.mutable_clone)
- }
-}
-
impl SourceChangeBuilder {
pub fn new(file_id: impl Into<FileId>) -> SourceChangeBuilder {
SourceChangeBuilder {
@@ -366,34 +348,6 @@ impl SourceChangeBuilder {
}
}
- pub fn make_mut<N: AstNode>(&mut self, node: N) -> N {
- self.mutated_tree.get_or_insert_with(|| TreeMutator::new(node.syntax())).make_mut(&node)
- }
-
- pub fn make_import_scope_mut(&mut self, scope: ImportScope) -> ImportScope {
- ImportScope {
- kind: match scope.kind.clone() {
- ImportScopeKind::File(it) => ImportScopeKind::File(self.make_mut(it)),
- ImportScopeKind::Module(it) => ImportScopeKind::Module(self.make_mut(it)),
- ImportScopeKind::Block(it) => ImportScopeKind::Block(self.make_mut(it)),
- },
- required_cfgs: scope.required_cfgs.iter().map(|it| self.make_mut(it.clone())).collect(),
- }
- }
- /// Returns a copy of the `node`, suitable for mutation.
- ///
- /// Syntax trees in rust-analyzer are typically immutable, and mutating
- /// operations panic at runtime. However, it is possible to make a copy of
- /// the tree and mutate the copy freely. Mutation is based on interior
- /// 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 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)
- }
-
/// Remove specified `range` of text.
pub fn delete(&mut self, range: TextRange) {
self.edit.delete(range)
@@ -434,12 +388,6 @@ impl SourceChangeBuilder {
self.add_snippet(PlaceSnippet::Before(node.syntax().clone().into()));
}
- /// 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().into()));
- }
-
/// Adds a tabstop snippet to place the cursor before `token`
pub fn add_tabstop_before_token(&mut self, _cap: SnippetCap, token: SyntaxToken) {
assert!(token.parent().is_some());
@@ -458,23 +406,6 @@ impl SourceChangeBuilder {
self.add_snippet(PlaceSnippet::Over(node.syntax().clone().into()))
}
- /// Adds a snippet to move the cursor selected over `token`
- pub fn add_placeholder_snippet_token(&mut self, _cap: SnippetCap, token: SyntaxToken) {
- assert!(token.parent().is_some());
- self.add_snippet(PlaceSnippet::Over(token.into()))
- }
-
- /// Adds a snippet to move the cursor selected over `nodes`
- ///
- /// This allows for renaming newly generated items without having to go
- /// through a separate rename step.
- pub fn add_placeholder_snippet_group(&mut self, _cap: SnippetCap, nodes: Vec<SyntaxNode>) {
- assert!(nodes.iter().all(|node| node.parent().is_some()));
- self.add_snippet(PlaceSnippet::OverGroup(
- nodes.into_iter().map(|node| node.into()).collect(),
- ))
- }
-
fn add_snippet(&mut self, snippet: PlaceSnippet) {
let snippet_builder = self.snippet_builder.get_or_insert(SnippetBuilder { places: vec![] });
snippet_builder.places.push(snippet);
@@ -553,9 +484,6 @@ enum PlaceSnippet {
After(SyntaxElement),
/// Place a placeholder snippet in place of the element
Over(SyntaxElement),
- /// Place a group of placeholder snippets which are linked together
- /// in place of the elements
- OverGroup(Vec<SyntaxElement>),
}
impl PlaceSnippet {
@@ -564,9 +492,6 @@ impl PlaceSnippet {
PlaceSnippet::Before(it) => vec![Snippet::Tabstop(it.text_range().start())],
PlaceSnippet::After(it) => vec![Snippet::Tabstop(it.text_range().end())],
PlaceSnippet::Over(it) => vec![Snippet::Placeholder(it.text_range())],
- PlaceSnippet::OverGroup(it) => {
- vec![Snippet::PlaceholderGroup(it.into_iter().map(|it| it.text_range()).collect())]
- }
}
}
}
diff --git a/crates/ide-db/src/syntax_helpers/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs
index 09e6115320..76fea5c262 100644
--- a/crates/ide-db/src/syntax_helpers/suggest_name.rs
+++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs
@@ -193,7 +193,10 @@ impl NameGenerator {
pub fn for_impl_trait_as_generic(&mut self, ty: &ast::ImplTraitType) -> SmolStr {
let c = ty
.type_bound_list()
- .and_then(|bounds| bounds.syntax().text().char_at(0.into()))
+ .and_then(|bounds| {
+ let ty = bounds.bounds().next()?.ty()?;
+ ty.syntax().text().char_at(0.into()).filter(|ch| ch.is_alphabetic())
+ })
.unwrap_or('T');
self.suggest_name(&c.to_string())
diff --git a/crates/ide-db/src/text_edit.rs b/crates/ide-db/src/text_edit.rs
index d2a73710d5..2dd558b0b7 100644
--- a/crates/ide-db/src/text_edit.rs
+++ b/crates/ide-db/src/text_edit.rs
@@ -133,9 +133,9 @@ impl TextEdit {
let mut res = offset;
for indel in &self.indels {
if indel.delete.start() >= offset {
- break;
+ continue;
}
- if offset < indel.delete.end() {
+ if indel.delete.contains(offset) {
return None;
}
res += TextSize::of(&indel.insert);
@@ -151,6 +151,10 @@ impl TextEdit {
pub fn change_annotation(&self) -> Option<ChangeAnnotationId> {
self.annotation
}
+
+ pub fn cancel_edits_touching(&mut self, touching: TextRange) {
+ self.indels.retain(|indel| indel.delete.intersect(touching).is_none());
+ }
}
impl IntoIterator for TextEdit {
diff --git a/crates/ide-db/src/traits.rs b/crates/ide-db/src/traits.rs
index 60bdc2d82c..d38d9b6708 100644
--- a/crates/ide-db/src/traits.rs
+++ b/crates/ide-db/src/traits.rs
@@ -34,38 +34,33 @@ pub fn get_missing_assoc_items(
// may share the same name as a function or constant.
let mut impl_fns_consts = FxHashSet::default();
let mut impl_type = FxHashSet::default();
- let edition = imp.module(sema.db).krate(sema.db).edition(sema.db);
for item in imp.items(sema.db) {
match item {
hir::AssocItem::Function(it) => {
- impl_fns_consts.insert(it.name(sema.db).display(sema.db, edition).to_string());
+ impl_fns_consts.insert(it.name(sema.db));
}
hir::AssocItem::Const(it) => {
if let Some(name) = it.name(sema.db) {
- impl_fns_consts.insert(name.display(sema.db, edition).to_string());
+ impl_fns_consts.insert(name);
}
}
hir::AssocItem::TypeAlias(it) => {
- impl_type.insert(it.name(sema.db).display(sema.db, edition).to_string());
+ impl_type.insert(it.name(sema.db));
}
}
}
- resolve_target_trait(sema, impl_def).map_or(vec![], |target_trait| {
+ imp.trait_(sema.db).map_or(vec![], |target_trait| {
target_trait
.items(sema.db)
.into_iter()
.filter(|i| match i {
- hir::AssocItem::Function(f) => !impl_fns_consts
- .contains(&f.name(sema.db).display(sema.db, edition).to_string()),
- hir::AssocItem::TypeAlias(t) => {
- !impl_type.contains(&t.name(sema.db).display(sema.db, edition).to_string())
+ hir::AssocItem::Function(f) => !impl_fns_consts.contains(&f.name(sema.db)),
+ hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db)),
+ hir::AssocItem::Const(c) => {
+ c.name(sema.db).map(|n| !impl_fns_consts.contains(&n)).unwrap_or_default()
}
- hir::AssocItem::Const(c) => c
- .name(sema.db)
- .map(|n| !impl_fns_consts.contains(&n.display(sema.db, edition).to_string()))
- .unwrap_or_default(),
})
.collect()
})
@@ -158,7 +153,8 @@ mod tests {
let file = sema.parse(position.file_id);
let impl_block: ast::Impl =
sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
- let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
+ let items =
+ hir::attach_db(&db, || crate::traits::get_missing_assoc_items(&sema, &impl_block));
let actual = items
.into_iter()
.map(|item| item.name(&db).unwrap().display(&db, Edition::CURRENT).to_string())
diff --git a/crates/ide-db/src/use_trivial_constructor.rs b/crates/ide-db/src/use_trivial_constructor.rs
index a91d436afc..0b94a1fa5a 100644
--- a/crates/ide-db/src/use_trivial_constructor.rs
+++ b/crates/ide-db/src/use_trivial_constructor.rs
@@ -4,7 +4,7 @@ use hir::StructKind;
use span::Edition;
use syntax::{
ToSmolStr,
- ast::{Expr, Path, make},
+ ast::{Expr, Path, make, syntax_factory::SyntaxFactory},
};
/// given a type return the trivial constructor (if one exists)
@@ -37,3 +37,34 @@ pub fn use_trivial_constructor(
None
}
+
+pub fn use_trivial_constructor_with_factory(
+ make: &SyntaxFactory,
+ db: &crate::RootDatabase,
+ path: Path,
+ ty: &hir::Type<'_>,
+ edition: Edition,
+) -> Option<Expr> {
+ match ty.as_adt() {
+ Some(hir::Adt::Enum(x)) => {
+ if let &[variant] = &*x.variants(db)
+ && variant.kind(db) == hir::StructKind::Unit
+ {
+ let path = make.path_qualified(
+ path,
+ make.path_segment(
+ make.name_ref(&variant.name(db).display_no_db(edition).to_smolstr()),
+ ),
+ );
+
+ return Some(make.expr_path(path));
+ }
+ }
+ Some(hir::Adt::Struct(x)) if x.kind(db) == StructKind::Unit => {
+ return Some(make.expr_path(path));
+ }
+ _ => {}
+ }
+
+ None
+}
diff --git a/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs b/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs
index 2a7b0098ed..e6adac9da7 100644
--- a/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs
+++ b/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs
@@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticsContext, adjusted_display_range};
//
// This diagnostic is triggered if the `await` keyword is used outside of an async function or block
pub(crate) fn await_outside_of_async(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::AwaitOutsideOfAsync,
) -> Diagnostic {
let display_range =
@@ -52,6 +52,7 @@ async fn bar() {
fn await_inside_closure() {
check_diagnostics(
r#"
+//- minicore: future
async fn foo() {}
async fn bar() {
@@ -66,6 +67,7 @@ async fn bar() {
fn await_inside_async_block() {
check_diagnostics(
r#"
+//- minicore: future
async fn foo() {}
fn bar() {
@@ -79,6 +81,7 @@ fn bar() {
fn await_in_complex_context() {
check_diagnostics(
r#"
+//- minicore: future
async fn foo() {}
fn bar() {
diff --git a/crates/ide-diagnostics/src/handlers/bad_rtn.rs b/crates/ide-diagnostics/src/handlers/bad_rtn.rs
index ae42a88c31..c84b29dbe2 100644
--- a/crates/ide-diagnostics/src/handlers/bad_rtn.rs
+++ b/crates/ide-diagnostics/src/handlers/bad_rtn.rs
@@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: bad-rtn
//
// This diagnostic is shown when a RTN (Return Type Notation, `Type::method(..): Send`) is written in an improper place.
-pub(crate) fn bad_rtn(ctx: &DiagnosticsContext<'_>, d: &hir::BadRtn) -> Diagnostic {
+pub(crate) fn bad_rtn(ctx: &DiagnosticsContext<'_, '_>, d: &hir::BadRtn) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
ctx,
DiagnosticCode::Ra("bad-rtn", Severity::Error),
diff --git a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
index cbcaab6c74..b7265c47b6 100644
--- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is triggered if the `break` keyword is used outside of a loop.
pub(crate) fn break_outside_of_loop(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::BreakOutsideOfLoop,
) -> Diagnostic {
let message = if d.bad_value_break {
@@ -147,7 +147,7 @@ fn test() {
r#"
//- minicore: option, try
fn test() {
- try {
+ let _: Option<_> = try {
|| {
let x = Some(2);
Some(x?)
diff --git a/crates/ide-diagnostics/src/handlers/duplicate_field.rs b/crates/ide-diagnostics/src/handlers/duplicate_field.rs
new file mode 100644
index 0000000000..08748bf8af
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/duplicate_field.rs
@@ -0,0 +1,123 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: duplicate-field
+//
+// This diagnostic is triggered when a record expression or pattern specifies
+// the same field more than once.
+pub(crate) fn duplicate_field(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::DuplicateField,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0062"),
+ "field specified more than once",
+ d.field.map(Into::into),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn duplicate_field_in_struct_literal() {
+ check_diagnostics(
+ r#"
+struct S { foo: i32, bar: i32 }
+fn main() {
+ let _ = S {
+ foo: 1,
+ bar: 2,
+ foo: 3,
+ //^^^^^^ error: field specified more than once
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn duplicate_field_in_enum_variant_literal() {
+ check_diagnostics(
+ r#"
+enum E { V { foo: i32 } }
+fn main() {
+ let _ = E::V {
+ foo: 1,
+ foo: 2,
+ //^^^^^^ error: field specified more than once
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_duplicate_when_each_field_specified_once() {
+ check_diagnostics(
+ r#"
+struct S { foo: i32, bar: i32 }
+fn main() {
+ let _ = S { foo: 1, bar: 2 };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_duplicate_for_unknown_field_falls_through_to_no_such_field() {
+ check_diagnostics(
+ r#"
+struct S { foo: i32 }
+fn main() {
+ let _ = S {
+ foo: 1,
+ bar: 2,
+ //^^^^^^ 💡 error: no such field
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn duplicate_field_in_struct_pattern() {
+ check_diagnostics(
+ r#"
+struct S { foo: i32, bar: i32 }
+fn f(s: S) {
+ let S {
+ foo,
+ bar,
+ foo,
+ //^^^ error: field specified more than once
+ ..
+ } = s;
+ let _ = (foo, bar);
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn duplicate_field_in_enum_variant_pattern() {
+ check_diagnostics(
+ r#"
+enum E { V { foo: i32, bar: i32 } }
+fn f(e: E) {
+ match e {
+ E::V {
+ foo,
+ bar,
+ foo,
+ //^^^ error: field specified more than once
+ ..
+ } => { let _ = (foo, bar); }
+ }
+}
+"#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs b/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs
index b284d9b351..8df9959859 100644
--- a/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs
+++ b/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs
@@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// This diagnostic is triggered when lifetimes are elided in paths. It is a lint only for some cases,
// and a hard error for others.
pub(crate) fn elided_lifetimes_in_path(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::ElidedLifetimesInPath,
) -> Diagnostic {
if d.hard_error {
diff --git a/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs b/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs
new file mode 100644
index 0000000000..ab2c3ccd12
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/expected_array_or_slice_pat.rs
@@ -0,0 +1,50 @@
+use hir::HirDisplay;
+
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: expected-array-or-slice-pat
+//
+// This diagnostic is triggered when an array or slice pattern is matched
+// against a type that is neither an array nor a slice.
+pub(crate) fn expected_array_or_slice_pat(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::ExpectedArrayOrSlicePat<'_>,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0529"),
+ format!(
+ "expected an array or slice, found {}",
+ d.found.display(ctx.sema.db, ctx.display_target)
+ ),
+ d.pat.map(Into::into),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn expected_array_or_slice() {
+ check_diagnostics(
+ r#"
+fn f([_a, _b]: i32) {}
+ //^^^^^^^^ error: expected an array or slice, found i32
+"#,
+ );
+ }
+
+ #[test]
+ fn expected_array_or_slice_let_pattern() {
+ check_diagnostics(
+ r#"
+fn f(x: i32) {
+ let [_a, _b] = x;
+ //^^^^^^^^ error: expected an array or slice, found i32
+}
+"#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/expected_function.rs b/crates/ide-diagnostics/src/handlers/expected_function.rs
index afd1687ae0..25e9dc09eb 100644
--- a/crates/ide-diagnostics/src/handlers/expected_function.rs
+++ b/crates/ide-diagnostics/src/handlers/expected_function.rs
@@ -6,7 +6,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is triggered if a call is made on something that is not callable.
pub(crate) fn expected_function(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::ExpectedFunction<'_>,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
diff --git a/crates/ide-diagnostics/src/handlers/field_shorthand.rs b/crates/ide-diagnostics/src/handlers/field_shorthand.rs
index 1dc6a7bf9c..0c77fbbd55 100644
--- a/crates/ide-diagnostics/src/handlers/field_shorthand.rs
+++ b/crates/ide-diagnostics/src/handlers/field_shorthand.rs
@@ -220,4 +220,24 @@ fn f(a: A) {
"#,
);
}
+
+ #[test]
+ fn diagnostic_range_respect_allows() {
+ check_diagnostics(
+ r#"
+#![allow(clippy::redundant_field_names, unused)]
+
+struct Foo {
+ bar: u32,
+}
+
+fn main() {
+ let bar = 23;
+ let foo = Foo {
+ bar: bar,
+ };
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs b/crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs
new file mode 100644
index 0000000000..8b5a235bfb
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/functional_record_update_on_non_struct.rs
@@ -0,0 +1,55 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: functional-record-update-on-non-struct
+//
+// This diagnostic is triggered when functional record update syntax is used on
+// something other than a struct.
+pub(crate) fn functional_record_update_on_non_struct(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::FunctionalRecordUpdateOnNonStruct,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0436"),
+ "functional record update syntax requires a struct",
+ d.base_expr.map(Into::into),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn enum_variant_record_update() {
+ check_diagnostics(
+ r#"
+enum E {
+ V { x: i32, y: i32 },
+}
+
+fn f(e: E) {
+ let _ = E::V { x: 0, ..e };
+ //^ error: functional record update syntax requires a struct
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn struct_record_update() {
+ check_diagnostics(
+ r#"
+struct S {
+ x: i32,
+ y: i32,
+}
+
+fn f(s: S) {
+ let _ = S { x: 0, ..s };
+}
+"#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
index 9ae6f013c7..515878fd47 100644
--- a/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
+++ b/crates/ide-diagnostics/src/handlers/generic_args_prohibited.rs
@@ -12,7 +12,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
// This diagnostic is shown when generic arguments are provided for a type that does not accept
// generic arguments.
pub(crate) fn generic_args_prohibited(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::GenericArgsProhibited,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
@@ -42,7 +42,7 @@ fn describe_reason(reason: GenericArgsProhibitedReason) -> String {
format!("generic arguments are not allowed on {kind}")
}
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::GenericArgsProhibited) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::GenericArgsProhibited) -> Option<Vec<Assist>> {
let file_id = d.args.file_id.file_id()?;
let syntax = d.args.to_node(ctx.sema.db);
let range = match &syntax {
diff --git a/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs b/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs
new file mode 100644
index 0000000000..926c517bc9
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/generic_default_refers_to_self.rs
@@ -0,0 +1,43 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: generic-default-refers-to-self
+//
+// This diagnostic is shown when a generic default refers to `Self`
+pub(crate) fn generic_default_refers_to_self(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::GenericDefaultRefersToSelf,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0735"),
+ "generic parameters cannot use `Self` in their defaults",
+ d.segment.map(Into::into),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn plain_self() {
+ check_diagnostics(
+ r#"
+struct Foo<T = Self>(T);
+ // ^^^^ error: generic parameters cannot use `Self` in their defaults
+"#,
+ );
+ }
+
+ #[test]
+ fn self_as_generic() {
+ check_diagnostics(
+ r#"
+struct Wrapper<T>(T);
+struct Foo<T = Wrapper<Self>>(T);
+ // ^^^^ error: generic parameters cannot use `Self` in their defaults
+"#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs
index be4fe763a0..09f3e8bfb3 100644
--- a/crates/ide-diagnostics/src/handlers/inactive_code.rs
+++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs
@@ -7,7 +7,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
//
// This diagnostic is shown for code with inactive `#[cfg]` attributes.
pub(crate) fn inactive_code(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::InactiveCode,
) -> Option<Diagnostic> {
// If there's inactive code somewhere in a macro, don't propagate to the call-site.
diff --git a/crates/ide-diagnostics/src/handlers/incoherent_impl.rs b/crates/ide-diagnostics/src/handlers/incoherent_impl.rs
index a0c364b001..bd8b804af4 100644
--- a/crates/ide-diagnostics/src/handlers/incoherent_impl.rs
+++ b/crates/ide-diagnostics/src/handlers/incoherent_impl.rs
@@ -6,7 +6,10 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran
// Diagnostic: incoherent-impl
//
// This diagnostic is triggered if the targe type of an impl is from a foreign crate.
-pub(crate) fn incoherent_impl(ctx: &DiagnosticsContext<'_>, d: &hir::IncoherentImpl) -> Diagnostic {
+pub(crate) fn incoherent_impl(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::IncoherentImpl,
+) -> Diagnostic {
let display_range = adjusted_display_range(ctx, InFile::new(d.file_id, d.impl_), &|node| {
Some(TextRange::new(
node.syntax().text_range().start(),
diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs
index 5410f8b58a..bda3f9bf0a 100644
--- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs
+++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs
@@ -13,7 +13,10 @@ use crate::{
// Diagnostic: incorrect-ident-case
//
// This diagnostic is triggered if an item name doesn't follow [Rust naming convention](https://doc.rust-lang.org/1.0.0/style/style/naming/README.html).
-pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic {
+pub(crate) fn incorrect_case(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::IncorrectCase,
+) -> Diagnostic {
let code = match d.expected_case {
CaseType::LowerSnakeCase => DiagnosticCode::RustcLint("non_snake_case"),
CaseType::UpperSnakeCase => DiagnosticCode::RustcLint("non_upper_case_globals"),
@@ -33,7 +36,7 @@ pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCas
.with_fixes(fixes(ctx, d))
}
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::IncorrectCase) -> Option<Vec<Assist>> {
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()?;
@@ -1006,18 +1009,18 @@ fn func() {
#![allow(unused_variables)]
#[warn(nonstandard_style)]
fn foo() {
- let BAR;
+ let BAR: i32;
// ^^^ 💡 warn: Variable `BAR` should have snake_case name, e.g. `bar`
#[allow(non_snake_case)]
- let FOO;
+ let FOO: i32;
}
#[warn(nonstandard_style)]
fn foo() {
- let BAR;
+ let BAR: i32;
// ^^^ 💡 warn: Variable `BAR` should have snake_case name, e.g. `bar`
#[expect(non_snake_case)]
- let FOO;
+ let FOO: i32;
#[allow(non_snake_case)]
struct qux;
// ^^^ 💡 warn: Structure `qux` should have UpperCamelCase name, e.g. `Qux`
@@ -1060,7 +1063,7 @@ mod FINE_WITH_BAD_CASE;
struct QUX;
const foo: i32 = 0;
fn BAR() {
- let BAZ;
+ let BAZ: i32;
_ = BAZ;
}
"#,
diff --git a/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs b/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
index 25220704e0..5ee02279a2 100644
--- a/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
+++ b/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
@@ -5,7 +5,7 @@ use hir::IncorrectGenericsLenKind;
//
// This diagnostic is triggered if the number of generic arguments does not match their declaration.
pub(crate) fn incorrect_generics_len(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::IncorrectGenericsLen,
) -> Diagnostic {
let owner_description = d.def.description();
diff --git a/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs b/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs
index b71586d6be..c2b70a204e 100644
--- a/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs
+++ b/crates/ide-diagnostics/src/handlers/incorrect_generics_order.rs
@@ -6,7 +6,7 @@ use syntax::SyntaxKind;
//
// This diagnostic is triggered the order of provided generic arguments does not match their declaration.
pub(crate) fn incorrect_generics_order(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::IncorrectGenericsOrder,
) -> Diagnostic {
let provided_description = match d.provided_arg.value.kind() {
diff --git a/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/crates/ide-diagnostics/src/handlers/invalid_cast.rs
index 405d8df685..bd8fa69e28 100644
--- a/crates/ide-diagnostics/src/handlers/invalid_cast.rs
+++ b/crates/ide-diagnostics/src/handlers/invalid_cast.rs
@@ -18,7 +18,10 @@ macro_rules! format_ty {
// Diagnostic: invalid-cast
//
// This diagnostic is triggered if the code contains an illegal cast
-pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_>) -> Diagnostic {
+pub(crate) fn invalid_cast(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::InvalidCast<'_>,
+) -> Diagnostic {
let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into()));
let (code, message) = match d.error {
CastError::CastToBool => (
@@ -111,7 +114,7 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_
//
// This diagnostic is triggered when casting to an unsized type
pub(crate) fn cast_to_unsized(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::CastToUnsized<'_>,
) -> Diagnostic {
let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into()));
@@ -387,7 +390,7 @@ struct Bar;
impl Foo for Bar {}
-fn to_raw<T>(_: *mut T) -> *mut () {
+fn to_raw<T: ?Sized>(_: *mut T) -> *mut () {
loop {}
}
@@ -987,7 +990,7 @@ fn main() {
fn rustc_issue_106883() {
check_diagnostics_with_disabled(
r#"
-//- minicore: sized, deref
+//- minicore: sized, deref, coerce_unsized, unsize
use core::ops::Deref;
struct Foo;
diff --git a/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs b/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs
index 9aa7aed169..8522041b52 100644
--- a/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs
+++ b/crates/ide-diagnostics/src/handlers/invalid_derive_target.rs
@@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// This diagnostic is shown when the derive attribute is used on an item other than a `struct`,
// `enum` or `union`.
pub(crate) fn invalid_derive_target(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::InvalidDeriveTarget,
) -> Diagnostic {
let display_range = ctx.sema.diagnostics_display_range_for_range(d.range);
diff --git a/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs b/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs
new file mode 100644
index 0000000000..225d3e0b46
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs
@@ -0,0 +1,90 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: invalid-lhs-of-assignment
+//
+// This diagnostic is triggered if the left-hand side of an assignment can't be assigned to.
+pub(crate) fn invalid_lhs_of_assignment(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::InvalidLhsOfAssignment,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0067"),
+ "invalid left-hand side of assignment",
+ d.lhs.map(Into::into),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn unit_struct_literal() {
+ check_diagnostics(
+ r#"
+//- minicore: add
+struct Struct;
+impl core::ops::AddAssign for Struct {
+ fn add_assign(&mut self, _other: Self) {}
+}
+fn test() {
+ Struct += Struct;
+ // ^^^^^^ error: invalid left-hand side of assignment
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn struct_literal() {
+ check_diagnostics(
+ r#"
+//- minicore: add
+struct Struct { foo: i32, bar: i32 }
+impl core::ops::AddAssign for Struct {
+ fn add_assign(&mut self, _other: Self) {}
+}
+fn test() {
+ Struct { foo: 0, bar: 0 } += Struct { foo: 1, bar: 2 };
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid left-hand side of assignment
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn destructuring_assignment() {
+ // no diagnostic, as `=` is not a _compound_ assignment
+ check_diagnostics(
+ r#"
+//- minicore: add
+struct Struct { foo: i32, bar: i32 }
+impl core::ops::AddAssign for Struct {
+ fn add_assign(&mut self, _other: Self) {}
+}
+fn test(mut foo: i32, mut bar: i32) {
+ Struct { foo, bar } = Struct { foo: 1, bar: 2 };
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn destructuring_compound_assignment() {
+ check_diagnostics(
+ r#"
+//- minicore: add
+struct Struct { foo: i32, bar: i32 }
+impl core::ops::AddAssign for Struct {
+ fn add_assign(&mut self, _other: Self) {}
+}
+fn test(foo: i32, bar: i32) {
+ Struct { foo, bar } += Struct { foo: 1, bar: 2 };
+ // ^^^^^^^^^^^^^^^^^^^ error: invalid left-hand side of assignment
+}
+ "#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
index 20bfcc2dee..24f1e3ad83 100644
--- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
+++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
@@ -2,12 +2,11 @@
//! example.
use hir::{FindPathConfig, PathResolution, Semantics};
+use ide_db::imports::insert_use::insert_uses_with_editor;
use ide_db::text_edit::TextEdit;
use ide_db::{
- EditionedFileId, FileRange, FxHashMap, RootDatabase,
- helpers::mod_path_to_ast,
- imports::insert_use::{ImportScope, insert_use},
- source_change::SourceChangeBuilder,
+ EditionedFileId, FileRange, FxHashMap, RootDatabase, helpers::mod_path_to_ast,
+ imports::insert_use::ImportScope, source_change::SourceChangeBuilder,
};
use itertools::Itertools;
use stdx::{format_to, never};
@@ -138,7 +137,7 @@ pub(crate) fn json_in_items(
.stable()
.with_fixes(Some(vec![{
let mut scb = SourceChangeBuilder::new(vfs_file_id);
- let scope = scb.make_import_scope_mut(import_scope);
+ let editor = scb.make_editor(import_scope.as_syntax_node());
let current_module = semantics_scope.module();
let cfg = FindPathConfig {
@@ -148,6 +147,7 @@ pub(crate) fn json_in_items(
allow_unstable: true,
};
+ let mut imports_to_insert = Vec::new();
if !scope_has("Serialize")
&& let Some(PathResolution::Def(it)) = serialize_resolved
&& let Some(it) = current_module.find_use_path(
@@ -157,7 +157,7 @@ pub(crate) fn json_in_items(
cfg,
)
{
- insert_use(&scope, mod_path_to_ast(&it, edition), &config.insert_use);
+ imports_to_insert.push(mod_path_to_ast(&it, edition));
}
if !scope_has("Deserialize")
&& let Some(PathResolution::Def(it)) = deserialize_resolved
@@ -168,8 +168,16 @@ pub(crate) fn json_in_items(
cfg,
)
{
- insert_use(&scope, mod_path_to_ast(&it, edition), &config.insert_use);
+ imports_to_insert.push(mod_path_to_ast(&it, edition));
}
+
+ insert_uses_with_editor(
+ &import_scope,
+ imports_to_insert,
+ &config.insert_use,
+ &editor,
+ );
+ scb.add_file_edits(vfs_file_id, editor);
let mut sc = scb.finish();
sc.insert_source_edit(vfs_file_id, edit.finish());
fix("convert_json_to_struct", "Convert JSON to struct", sc, range)
diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs
index a44b043f43..b6571e02ef 100644
--- a/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -11,7 +11,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
// Diagnostic: proc-macro-disabled
//
// This diagnostic is shown for proc macros that have been specifically disabled via `rust-analyzer.procMacro.ignored`.
-pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic {
+pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MacroError) -> Diagnostic {
// Use more accurate position if available.
let display_range = ctx.sema.diagnostics_display_range_for_range(d.range);
Diagnostic::new(
@@ -25,7 +25,10 @@ pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) ->
// Diagnostic: macro-def-error
//
// This diagnostic is shown for macro expansion errors.
-pub(crate) fn macro_def_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroDefError) -> Diagnostic {
+pub(crate) fn macro_def_error(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::MacroDefError,
+) -> Diagnostic {
// Use more accurate position if available.
let display_range = match d.name {
Some(name) => ctx.sema.diagnostics_display_range_for_range(d.node.with_value(name)),
diff --git a/crates/ide-diagnostics/src/handlers/malformed_derive.rs b/crates/ide-diagnostics/src/handlers/malformed_derive.rs
index 7d0c71f4fa..c7d8991f45 100644
--- a/crates/ide-diagnostics/src/handlers/malformed_derive.rs
+++ b/crates/ide-diagnostics/src/handlers/malformed_derive.rs
@@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is shown when the derive attribute has invalid input.
pub(crate) fn malformed_derive(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::MalformedDerive,
) -> Diagnostic {
let display_range = ctx.sema.diagnostics_display_range_for_range(d.range);
diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index 4c0985c7ae..f6293e35d0 100644
--- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -12,7 +12,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran
//
// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
pub(crate) fn mismatched_tuple_struct_pat_arg_count(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::MismatchedTupleStructPatArgCount,
) -> Diagnostic {
let s = if d.found == 1 { "" } else { "s" };
@@ -33,7 +33,7 @@ pub(crate) fn mismatched_tuple_struct_pat_arg_count(
//
// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
pub(crate) fn mismatched_arg_count(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::MismatchedArgCount,
) -> Diagnostic {
let s = if d.expected == 1 { "" } else { "s" };
@@ -47,7 +47,7 @@ pub(crate) fn mismatched_arg_count(
}
fn invalid_args_range(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
source: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
expected: usize,
found: usize,
@@ -205,6 +205,7 @@ trait Foo { fn method(&self, _arg: usize) {} }
fn f() {
let x;
+ // ^ error: type annotations needed
x.method();
}
"#,
@@ -453,6 +454,8 @@ fn g() {
b::<1, 3>(0, 2);
b(0, 1, 2);
+ // ^ error: type annotations needed
+ // | full type: `fn b<_, _>(u8, u8)`
//^ error: expected 4 arguments, found 3
}
"#,
diff --git a/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs b/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs
new file mode 100644
index 0000000000..8cae405c92
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/mismatched_array_pat_len.rs
@@ -0,0 +1,114 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: mismatched-array-pat-len
+//
+// This diagnostic is triggered when an array pattern's element count does not
+// match the array's declared length.
+pub(crate) fn mismatched_array_pat_len(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::MismatchedArrayPatLen,
+) -> Diagnostic {
+ let (code, message) = if d.has_rest {
+ (
+ "E0528",
+ format!(
+ "pattern requires at least {} element{} but array has {}",
+ d.found,
+ if d.found == 1 { "" } else { "s" },
+ d.expected,
+ ),
+ )
+ } else {
+ (
+ "E0527",
+ format!(
+ "pattern requires {} element{} but array has {}",
+ d.found,
+ if d.found == 1 { "" } else { "s" },
+ d.expected,
+ ),
+ )
+ };
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError(code),
+ message,
+ d.pat.map(Into::into),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn array_pattern_too_few_elements() {
+ check_diagnostics(
+ r#"
+fn f(arr: [i32; 3]) {
+ let [_a, _b] = arr;
+ //^^^^^^^^ error: pattern requires 2 elements but array has 3
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn array_pattern_too_many_elements() {
+ check_diagnostics(
+ r#"
+fn f(arr: [i32; 2]) {
+ let [_a, _b, _c] = arr;
+ //^^^^^^^^^^^^ error: pattern requires 3 elements but array has 2
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn array_pattern_with_rest_too_short() {
+ check_diagnostics(
+ r#"
+fn f(arr: [i32; 2]) {
+ let [_a, _b, _c, ..] = arr;
+ //^^^^^^^^^^^^^^^^ error: pattern requires at least 3 elements but array has 2
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn array_pattern_with_rest_ok() {
+ check_diagnostics(
+ r#"
+fn f(arr: [i32; 5]) {
+ let [_a, _b, ..] = arr;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn array_pattern_exact_length_ok() {
+ check_diagnostics(
+ r#"
+fn f(arr: [i32; 3]) {
+ let [_a, _b, _c] = arr;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn array_pattern_singular_element_uses_singular() {
+ check_diagnostics(
+ r#"
+fn f(arr: [i32; 3]) {
+ let [_a] = arr;
+ //^^^^ error: pattern requires 1 element but array has 3
+}
+"#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 85368cc09f..607f0cbd23 100644
--- a/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -18,6 +18,7 @@ use stdx::format_to;
use syntax::{
AstNode, Edition, SyntaxNode, SyntaxNodePtr, ToSmolStr,
ast::{self, make},
+ syntax_editor::SyntaxEditor,
};
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
@@ -33,7 +34,10 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
//
// let a = A { a: 10 };
// ```
-pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Diagnostic {
+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.display(ctx.sema.db, ctx.edition));
@@ -51,7 +55,7 @@ pub(crate) fn missing_fields(ctx: &DiagnosticsContext<'_>, d: &hir::MissingField
.with_fixes(fixes(ctx, d))
}
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingFields) -> Option<Vec<Assist>> {
// Note that although we could add a diagnostics to
// fill the missing tuple field, e.g :
// `struct A(usize);`
@@ -112,16 +116,20 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
}
});
+ let old_field_list = field_list_parent.record_expr_field_list()?;
+ let root = old_field_list.syntax().ancestors().last()?;
+ let (editor, _) = SyntaxEditor::new(root);
+ let make = editor.make();
+
let generate_fill_expr = |ty: &Type<'_>| match ctx.config.expr_fill_default {
- ExprFillDefaultMode::Todo => make::ext::expr_todo(),
- ExprFillDefaultMode::Underscore => make::ext::expr_underscore(),
+ ExprFillDefaultMode::Todo => make.expr_todo(),
+ ExprFillDefaultMode::Underscore => make.expr_underscore().into(),
ExprFillDefaultMode::Default => {
- get_default_constructor(ctx, d, ty).unwrap_or_else(make::ext::expr_todo)
+ get_default_constructor(ctx, d, ty).unwrap_or_else(|| make.expr_todo())
}
};
- let old_field_list = field_list_parent.record_expr_field_list()?;
- let new_field_list = old_field_list.clone_for_update();
+ let mut new_fields = Vec::new();
for (f, ty) in missing_fields.iter() {
let field_expr = if let Some(local_candidate) = locals.get(&f.name(ctx.sema.db)) {
cov_mark::hit!(field_shorthand);
@@ -156,31 +164,39 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
if expr.is_some() { expr } else { Some(generate_fill_expr(ty)) }
};
- let field = make::record_expr_field(
- make::name_ref(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()),
+ let field = make.record_expr_field(
+ make.name_ref(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()),
field_expr,
);
- new_field_list.add_field(field.clone_for_update());
+ new_fields.push(field);
}
- build_text_edit(new_field_list.syntax(), old_field_list.syntax())
+ old_field_list.add_fields(&editor, new_fields);
+ let new_field_list = editor.finish().find_element(old_field_list.syntax())?;
+ build_text_edit(&new_field_list, old_field_list.syntax())
}
Either::Right(field_list_parent) => {
let missing_fields = ctx.sema.record_pattern_missing_fields(field_list_parent);
let old_field_list = field_list_parent.record_pat_field_list()?;
- let new_field_list = old_field_list.clone_for_update();
+ let root = old_field_list.syntax().ancestors().last()?;
+ let (editor, _) = SyntaxEditor::new(root);
+ let make = editor.make();
+
+ let mut new_fields = Vec::new();
for (f, _) in missing_fields.iter() {
- let field = make::record_pat_field_shorthand(
- make::ident_pat(
+ let field = make.record_pat_field_shorthand(
+ make.ident_pat(
false,
false,
- make::name(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()),
+ make.name(&f.name(ctx.sema.db).display_no_db(ctx.edition).to_smolstr()),
)
.into(),
);
- new_field_list.add_field(field.clone_for_update());
+ new_fields.push(field);
}
- build_text_edit(new_field_list.syntax(), old_field_list.syntax())
+ old_field_list.add_fields(&editor, new_fields);
+ let new_field_list = editor.finish().find_element(old_field_list.syntax())?;
+ build_text_edit(&new_field_list, old_field_list.syntax())
}
}
}
@@ -202,7 +218,7 @@ fn make_ty(
}
fn get_default_constructor(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::MissingFields,
ty: &Type<'_>,
) -> Option<ast::Expr> {
@@ -293,12 +309,15 @@ fn baz(s: S) -> i32 {
#[test]
fn missing_record_pat_field_box() {
check_diagnostics(
- r"
+ r#"
+#![feature(lang_items)]
+#[lang = "owned_box"]
+struct Box<T>(T);
struct S { s: Box<u32> }
fn x(a: S) {
let S { box s } = a;
}
-",
+"#,
)
}
diff --git a/crates/ide-diagnostics/src/handlers/missing_lifetime.rs b/crates/ide-diagnostics/src/handlers/missing_lifetime.rs
index b10cdaa14e..760bb7309d 100644
--- a/crates/ide-diagnostics/src/handlers/missing_lifetime.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_lifetime.rs
@@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is triggered when a lifetime argument is missing.
pub(crate) fn missing_lifetime(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::MissingLifetime,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index d52fc73870..7bc7955c4e 100644
--- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is triggered if `match` block is missing one or more match arms.
pub(crate) fn missing_match_arms(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::MissingMatchArms,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
@@ -300,7 +300,7 @@ fn main() {
}
match (true, false) {
(true, false, true) => (),
- //^^^^^^^^^^^^^^^^^^^ error: expected (bool, bool), found (bool, bool, bool)
+ //^^^^^^^^^^^^^^^^^^^ error: expected (bool, bool), found (bool, bool, {unknown})
(true) => (),
// ^^^^ error: expected (bool, bool), found bool
}
@@ -1198,4 +1198,20 @@ fn main() {
);
}
}
+
+ #[test]
+ fn no_overloaded_deref_is_not_projection() {
+ check_diagnostics(
+ r#"
+const FOO: &str = "";
+
+fn foo() {
+ match "" {
+ FOO => {}
+ _ => {}
+ }
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 6a37702fc5..b4ddb239c8 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -10,7 +10,10 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
// Diagnostic: missing-unsafe
//
// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
-pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic {
+pub(crate) fn missing_unsafe(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::MissingUnsafe,
+) -> Diagnostic {
let code = match d.lint {
UnsafeLint::HardError => DiagnosticCode::RustcHardError("E0133"),
UnsafeLint::UnsafeOpInUnsafeFn => DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn"),
@@ -38,7 +41,7 @@ fn display_unsafety_reason(reason: UnsafetyReason) -> &'static str {
}
}
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingUnsafe) -> Option<Vec<Assist>> {
// The fixit will not work correctly for macro expansions, so we don't offer it in that case.
if d.node.file_id.is_macro() {
return None;
@@ -271,25 +274,6 @@ fn main() {
}
#[test]
- fn no_missing_unsafe_diagnostic_with_legacy_safe_intrinsic() {
- check_diagnostics(
- r#"
-extern "rust-intrinsic" {
- #[rustc_safe_intrinsic]
- pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
- pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
-}
-
-fn main() {
- let _ = bitreverse(12);
- let _ = floorf32(12.0);
- //^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
-}
-"#,
- );
- }
-
- #[test]
fn no_missing_unsafe_diagnostic_with_deprecated_safe_2024() {
check_diagnostics(
r#"
@@ -414,30 +398,6 @@ fn main() {
}
#[test]
- fn add_unsafe_block_when_calling_unsafe_intrinsic() {
- check_fix(
- r#"
-extern "rust-intrinsic" {
- pub fn floorf32(x: f32) -> f32;
-}
-
-fn main() {
- let _ = floorf32$0(12.0);
-}
-"#,
- r#"
-extern "rust-intrinsic" {
- pub fn floorf32(x: f32) -> f32;
-}
-
-fn main() {
- let _ = unsafe { floorf32(12.0) };
-}
-"#,
- )
- }
-
- #[test]
fn unsafe_expr_as_a_receiver_of_a_method_call() {
check_fix(
r#"
@@ -485,7 +445,7 @@ fn main() {
let b = &raw const x.a;
- let tmp = Vec::from([1, 2, 3]);
+ let tmp = [1, 2, 3];
let c = &raw const tmp[x.a];
// ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block
@@ -1059,7 +1019,7 @@ impl FooTrait for S2 {
fn no_false_positive_on_format_args_since_1_89_0() {
check_diagnostics(
r#"
-//- minicore: fmt
+//- minicore: fmt, builtin_impls
fn test() {
let foo = 10;
let bar = true;
diff --git a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
index 6331090d9c..e61719acf5 100644
--- a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
+++ b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -5,7 +5,7 @@ use hir::HirDisplay;
//
// This diagnostic is triggered on moving non copy things out of references.
pub(crate) fn moved_out_of_ref(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::MovedOutOfRef<'_>,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index 18280a4add..31becd1d74 100644
--- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -8,7 +8,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
// Diagnostic: need-mut
//
// This diagnostic is triggered on mutating an immutable variable.
-pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option<Diagnostic> {
+pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NeedMut) -> Option<Diagnostic> {
let root = ctx.sema.db.parse_or_expand(d.span.file_id);
let node = d.span.value.to_node(&root);
let mut span = d.span;
@@ -63,7 +63,10 @@ pub(crate) fn need_mut(ctx: &DiagnosticsContext<'_>, d: &hir::NeedMut) -> Option
// Diagnostic: unused-mut
//
// This diagnostic is triggered when a mutable variable isn't actually mutated.
-pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Option<Diagnostic> {
+pub(crate) fn unused_mut(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::UnusedMut,
+) -> Option<Diagnostic> {
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
let fixes = (|| {
let file_id = ast.file_id.file_id()?;
@@ -87,7 +90,6 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Op
use_range,
)])
})();
- let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
Some(
Diagnostic::new_with_syntax_node_ptr(
ctx,
diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs
index 944622bb1d..7959fddc75 100644
--- a/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -15,7 +15,7 @@ use crate::{
// Diagnostic: no-such-field
//
// This diagnostic is triggered if created structure does not have field provided in record.
-pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic {
+pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NoSuchField) -> Diagnostic {
let (code, message) = if d.private.is_some() {
("E0451", "field is private")
} else if let VariantId::EnumVariantId(_) = d.variant {
@@ -30,7 +30,7 @@ pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField)
.with_fixes(fixes(ctx, d))
}
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> {
// FIXME: quickfix for pattern
let root = ctx.sema.db.parse_or_expand(d.field.file_id);
match &d.field.value.to_node(&root) {
diff --git a/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
index bc10e82854..ee2f6bf319 100644
--- a/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
+++ b/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs
@@ -12,7 +12,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
// This diagnostic is triggered if a `let` statement without an `else` branch has a non-exhaustive
// pattern.
pub(crate) fn non_exhaustive_let(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::NonExhaustiveLet,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
diff --git a/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs b/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs
new file mode 100644
index 0000000000..be9c07b1ac
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/non_exhaustive_record_expr.rs
@@ -0,0 +1,46 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: non-exhaustive-record-expr
+//
+// This diagnostic is triggered if a struct expression constructs a `#[non_exhaustive]`
+// struct from another crate.
+pub(crate) fn non_exhaustive_record_expr(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::NonExhaustiveRecordExpr,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0639"),
+ "cannot create non-exhaustive struct using struct expression",
+ d.expr.map(|it| it.into()),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn reports_external_non_exhaustive_struct_literal() {
+ check_diagnostics(
+ r#"
+//- /lib.rs crate:lib
+#[non_exhaustive]
+pub struct S {
+ pub field: u32,
+}
+
+fn local_ok() {
+ let _ = S { field: 0 };
+}
+
+//- /main.rs crate:main deps:lib
+fn main() {
+ let _ = lib::S { field: 0 };
+ //^^^^^^^^^^^^^^^^^^^ error: cannot create non-exhaustive struct using struct expression
+}
+"#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs b/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs
index 68f2b19657..44fc9f482b 100644
--- a/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs
+++ b/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs
@@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// This diagnostic is shown when a `Fn`-trait-style generic parameters (`Trait(A, B) -> C`)
// was used on non-`Fn` trait/type.
pub(crate) fn parenthesized_generic_args_without_fn_trait(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::ParenthesizedGenericArgsWithoutFnTrait,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
diff --git a/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs b/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs
new file mode 100644
index 0000000000..459ec175b1
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/pattern_arg_in_extern_fn.rs
@@ -0,0 +1,86 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: pattern-arg-in-extern-fn
+//
+// This diagnostic is triggered if a pattern was declared as an argument in a foreign function declaration.
+pub(crate) fn pattern_arg_in_extern_fn(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::PatternArgInExternFn,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0130"),
+ "patterns aren't allowed in foreign function declarations",
+ d.node.map(Into::into),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn tuple_pattern() {
+ check_diagnostics(
+ r#"
+unsafe extern { fn foo((a, b): (u32, u32)); }
+ // ^^^^^^ error: patterns aren't allowed in foreign function declarations
+ "#,
+ );
+ }
+
+ #[test]
+ fn struct_pattern() {
+ check_diagnostics(
+ r#"
+struct Foo(u32, u32);
+unsafe extern { fn foo(Foo(a, b): Foo); }
+ // ^^^^^^^^^ error: patterns aren't allowed in foreign function declarations
+ "#,
+ );
+
+ check_diagnostics(
+ r#"
+struct Foo{ bar: u32, baz: u32 }
+unsafe extern { fn foo(Foo { bar, baz }: Foo); }
+ // ^^^^^^^^^^^^^^^^ error: patterns aren't allowed in foreign function declarations
+ "#,
+ );
+ }
+
+ #[test]
+ fn pattern_is_second_arg() {
+ check_diagnostics(
+ r#"
+struct Foo(u32, u32);
+unsafe extern { fn foo(okay: u32, Foo(a, b): Foo); }
+ // ^^^^^^^^^ error: patterns aren't allowed in foreign function declarations
+ "#,
+ );
+ }
+
+ #[test]
+ fn non_simple_ident() {
+ check_diagnostics(
+ r#"
+unsafe extern { fn foo(ref a: u32); }
+ // ^^^^^ error: patterns aren't allowed in foreign function declarations
+ "#,
+ );
+
+ check_diagnostics(
+ r#"
+unsafe extern { fn foo(mut a: u32); }
+ // ^^^^^ error: patterns aren't allowed in foreign function declarations
+ "#,
+ );
+
+ check_diagnostics(
+ r#"
+unsafe extern { fn foo(a @ _: u32); }
+ // ^^^^^ error: patterns aren't allowed in foreign function declarations
+ "#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
index 6d33ae0cf9..92f3c6961e 100644
--- a/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
+++ b/crates/ide-diagnostics/src/handlers/private_assoc_item.rs
@@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// This diagnostic is triggered if the referenced associated item is not visible from the current
// module.
pub(crate) fn private_assoc_item(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::PrivateAssocItem,
) -> Diagnostic {
// FIXME: add quickfix
diff --git a/crates/ide-diagnostics/src/handlers/private_field.rs b/crates/ide-diagnostics/src/handlers/private_field.rs
index 90c27bdcef..9515afed76 100644
--- a/crates/ide-diagnostics/src/handlers/private_field.rs
+++ b/crates/ide-diagnostics/src/handlers/private_field.rs
@@ -10,7 +10,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
// Diagnostic: private-field
//
// This diagnostic is triggered if the accessed field is not visible from the current module.
-pub(crate) fn private_field(ctx: &DiagnosticsContext<'_>, d: &hir::PrivateField) -> Diagnostic {
+pub(crate) fn private_field(ctx: &DiagnosticsContext<'_, '_>, d: &hir::PrivateField) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
ctx,
DiagnosticCode::RustcHardError("E0616"),
diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
index 2ec41d0528..b5a47e508e 100644
--- a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
+++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
@@ -10,7 +10,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran
// This diagnostic is triggered when there is a redundant `return` at the end of a function
// or closure.
pub(crate) fn remove_trailing_return(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &RemoveTrailingReturn,
) -> Option<Diagnostic> {
if d.return_expr.file_id.macro_file().is_some() {
@@ -36,7 +36,7 @@ pub(crate) fn remove_trailing_return(
)
}
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveTrailingReturn) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &RemoveTrailingReturn) -> Option<Vec<Assist>> {
let root = ctx.sema.db.parse_or_expand(d.return_expr.file_id);
let return_expr = d.return_expr.value.to_node(&root);
let stmt = return_expr.syntax().parent().and_then(ast::ExprStmt::cast);
@@ -333,7 +333,7 @@ fn foo(x: usize) -> u8 {
}
}
"#,
- std::iter::once("remove-unnecessary-else".to_owned()),
+ &["remove-unnecessary-else"],
);
check_fix(
r#"
diff --git a/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs b/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs
index 04f48ae3db..aa7b57e292 100644
--- a/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs
+++ b/crates/ide-diagnostics/src/handlers/remove_unnecessary_else.rs
@@ -19,7 +19,7 @@ use crate::{
// This diagnostic is triggered when there is an `else` block for an `if` expression whose
// then branch diverges (e.g. ends with a `return`, `continue`, `break` e.t.c).
pub(crate) fn remove_unnecessary_else(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &RemoveUnnecessaryElse,
) -> Option<Diagnostic> {
if d.if_expr.file_id.macro_file().is_some() {
@@ -40,7 +40,7 @@ pub(crate) fn remove_unnecessary_else(
)
}
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &RemoveUnnecessaryElse) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &RemoveUnnecessaryElse) -> Option<Vec<Assist>> {
let root = ctx.sema.db.parse_or_expand(d.if_expr.file_id);
let if_expr = d.if_expr.value.to_node(&root);
let if_expr = ctx.sema.original_ast_node(if_expr)?;
diff --git a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
index 37ce5f583f..f974c55023 100644
--- a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
+++ b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs
@@ -12,7 +12,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
//
// This diagnostic is triggered when `.filter_map(..).next()` is used, rather than the more concise `.find_map(..)`.
pub(crate) fn replace_filter_map_next_with_find_map(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::ReplaceFilterMapNextWithFindMap,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
@@ -26,7 +26,7 @@ pub(crate) fn replace_filter_map_next_with_find_map(
}
fn fixes(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::ReplaceFilterMapNextWithFindMap,
) -> Option<Vec<Assist>> {
let root = ctx.sema.db.parse_or_expand(d.file);
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs b/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs
index c5b2f499d3..9e7393c89c 100644
--- a/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs
+++ b/crates/ide-diagnostics/src/handlers/trait_impl_incorrect_safety.rs
@@ -7,7 +7,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, adjusted_d
//
// Diagnoses incorrect safety annotations of trait impls.
pub(crate) fn trait_impl_incorrect_safety(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::TraitImplIncorrectSafety,
) -> Diagnostic {
Diagnostic::new(
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
index 2c05544701..5f5e155bd7 100644
--- a/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
+++ b/crates/ide-diagnostics/src/handlers/trait_impl_missing_assoc_item.rs
@@ -8,7 +8,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran
//
// Diagnoses missing trait items in a trait impl.
pub(crate) fn trait_impl_missing_assoc_item(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::TraitImplMissingAssocItems,
) -> Diagnostic {
let missing = d.missing.iter().format_with(", ", |(name, item), f| {
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs b/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs
index 96911d4781..a9dc0d5d72 100644
--- a/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs
+++ b/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs
@@ -6,7 +6,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// Only traits defined in the current crate can be implemented for arbitrary types
pub(crate) fn trait_impl_orphan(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::TraitImplOrphan,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
index 6a380481d4..ee972f2d1d 100644
--- a/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
+++ b/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
@@ -16,7 +16,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// Diagnoses redundant trait items in a trait impl.
pub(crate) fn trait_impl_redundant_assoc_item(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::TraitImplRedundantAssocItems,
) -> Diagnostic {
let db = ctx.sema.db;
@@ -74,7 +74,7 @@ pub(crate) fn trait_impl_redundant_assoc_item(
/// add assoc item into the trait def body
fn quickfix_for_redundant_assoc_item(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::TraitImplRedundantAssocItems,
redundant_item_def: String,
range: TextRange,
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 98a4474ef1..250c692d16 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -20,7 +20,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_dis
// This diagnostic is triggered when the type of an expression or pattern does not match
// the expected type.
pub(crate) fn type_mismatch(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::TypeMismatch<'_>,
) -> Option<Diagnostic> {
if d.expected.is_unknown() || d.actual.is_unknown() {
@@ -64,7 +64,7 @@ pub(crate) fn type_mismatch(
)
}
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<'_>) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option<Vec<Assist>> {
let mut fixes = Vec::new();
if let Some(expr_ptr) = d.expr_or_pat.value.cast::<ast::Expr>() {
@@ -80,7 +80,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<'_>) -> Option<Vec<
}
fn add_reference(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::TypeMismatch<'_>,
expr_ptr: &InFile<AstPtr<ast::Expr>>,
acc: &mut Vec<Assist>,
@@ -102,7 +102,7 @@ fn add_reference(
}
fn add_missing_ok_or_some(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::TypeMismatch<'_>,
expr_ptr: &InFile<AstPtr<ast::Expr>>,
acc: &mut Vec<Assist>,
@@ -197,7 +197,7 @@ fn add_missing_ok_or_some(
}
fn remove_unnecessary_wrapper(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::TypeMismatch<'_>,
expr_ptr: &InFile<AstPtr<ast::Expr>>,
acc: &mut Vec<Assist>,
@@ -279,7 +279,7 @@ fn remove_unnecessary_wrapper(
}
fn remove_semicolon(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::TypeMismatch<'_>,
expr_ptr: &InFile<AstPtr<ast::Expr>>,
acc: &mut Vec<Assist>,
@@ -310,7 +310,7 @@ fn remove_semicolon(
}
fn str_ref_to_owned(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::TypeMismatch<'_>,
expr_ptr: &InFile<AstPtr<ast::Expr>>,
acc: &mut Vec<Assist>,
@@ -338,7 +338,8 @@ fn str_ref_to_owned(
#[cfg(test)]
mod tests {
use crate::tests::{
- check_diagnostics, check_diagnostics_with_disabled, check_fix, check_has_fix, check_no_fix,
+ check_diagnostics, check_diagnostics_with_disabled, check_fix, check_fix_with_disabled,
+ check_has_fix, check_no_fix,
};
#[test]
@@ -739,7 +740,7 @@ fn foo() -> Result<(), ()> {
check_fix(
r#"
-//- minicore: result
+//- minicore: result, iterator
fn foo() -> Result<(), ()> {
for _ in 0..5 {}$0
}
@@ -755,7 +756,7 @@ fn foo() -> Result<(), ()> {
#[test]
fn wrapped_unit_as_return_expr() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: result
fn foo(b: bool) -> Result<(), String> {
@@ -773,6 +774,7 @@ fn foo(b: bool) -> Result<(), String> {
Err("oh dear".to_owned())
}"#,
+ &["E0599"],
);
}
@@ -822,7 +824,7 @@ fn foo() -> SomeOtherEnum { 0$0 }
#[test]
fn unwrap_return_type() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: option, result
fn div(x: i32, y: i32) -> i32 {
@@ -840,6 +842,7 @@ fn div(x: i32, y: i32) -> i32 {
x / y
}
"#,
+ &["E0282"],
);
}
@@ -897,7 +900,7 @@ fn div(x: i32, y: i32) -> i32 {
#[test]
fn unwrap_return_type_option_tail_unit() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: option, result
fn div(x: i32, y: i32) {
@@ -915,12 +918,13 @@ fn div(x: i32, y: i32) {
}
}
"#,
+ &["E0282"],
);
}
#[test]
fn unwrap_return_type_handles_generic_functions() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: option, result
fn div<T>(x: T) -> T {
@@ -938,12 +942,13 @@ fn div<T>(x: T) -> T {
x
}
"#,
+ &["E0282"],
);
}
#[test]
fn unwrap_return_type_handles_type_aliases() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: option, result
type MyResult<T> = T;
@@ -965,12 +970,13 @@ fn div(x: i32, y: i32) -> MyResult<i32> {
x / y
}
"#,
+ &["E0282"],
);
}
#[test]
fn unwrap_tail_expr() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: result
fn foo() -> () {
@@ -983,12 +989,13 @@ fn foo() -> () {
println!("Hello, world!");
}
"#,
+ &["E0282"],
);
}
#[test]
fn unwrap_to_empty_block() {
- check_fix(
+ check_fix_with_disabled(
r#"
//- minicore: result
fn foo() -> () {
@@ -998,6 +1005,7 @@ fn foo() -> () {
r#"
fn foo() -> () {}
"#,
+ &["E0282"],
);
}
@@ -1190,9 +1198,7 @@ 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
}
}
@@ -1342,6 +1348,8 @@ pub fn foo<T: Foo>(_: T) -> (T::Out,) { loop { } }
fn main() {
let _x = foo(2);
+ // ^^ error: type annotations needed
+ // ^^^ error: the trait bound `i32: Foo` is not satisfied
}
"#,
);
diff --git a/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
new file mode 100644
index 0000000000..a03352fe31
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
@@ -0,0 +1,162 @@
+use either::Either;
+use hir::{HirDisplay, SpanAst};
+use stdx::format_to;
+use syntax::{AstNode, SyntaxNodePtr, ast};
+
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: type-must-be-known
+//
+// This diagnostic is triggered when rust-analyzer cannot infer some type.
+pub(crate) fn type_must_be_known<'db>(
+ ctx: &DiagnosticsContext<'db, '_>,
+ d: &hir::TypeMustBeKnown<'db>,
+) -> Diagnostic {
+ let mut at_point = d.at_point.map(|it| it.syntax_node_ptr());
+ let mut top_term = d.top_term.clone();
+
+ // Do some adjustments to the node: FIXME: We should probably do that at the emitting site.
+ let node = ctx.sema.to_node(d.at_point);
+ if let SpanAst::Expr(expr) = &node
+ && let Some(Either::Left(top_ty)) = &d.top_term
+ && let Some(expr_ty) = ctx.sema.type_of_expr(expr)
+ && expr_ty.original == *top_ty
+ && !top_ty.is_unknown()
+ && let Some(parent) = expr.syntax().parent().and_then(ast::CallExpr::cast)
+ && let Some(callable) = top_ty.as_callable(ctx.db())
+ && let ret_ty = callable.return_type()
+ && ret_ty.contains_unknown()
+ {
+ top_term = Some(Either::Left(ret_ty));
+ at_point.value = SyntaxNodePtr::new(parent.syntax());
+ }
+
+ let message = match &top_term {
+ Some(top_term) if !matches!(top_term, Either::Left(ty) if ty.is_unknown()) => {
+ let mut message = "type annotations needed\nfull type: `".to_owned();
+ match top_term {
+ Either::Left(ty) => {
+ format_to!(message, "{}", ty.display(ctx.db(), ctx.display_target))
+ }
+ Either::Right(konst) => message.push_str(konst),
+ }
+ message.push_str("`\n");
+ message
+ }
+ Some(_) => "type annotations needed".to_owned(),
+ None => "type annotations needed; type must be known at this point".to_owned(),
+ };
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0282"),
+ message,
+ at_point,
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn some_expressions_require_knowing_type() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ let var = loop {};
+ // ^^^ 💡 warn: unused variable
+ var();
+ // ^^^ error: type annotations needed; type must be known at this point
+ let var = loop {};
+ // ^^^ 💡 warn: unused variable
+ var[0];
+ // ^^^ error: type annotations needed; type must be known at this point
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn binding_without_type() {
+ check_diagnostics(
+ r#"
+fn any<T>() -> T { loop {} }
+fn foo() {
+ let _x = any();
+ // ^^^^^ error: type annotations needed
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn struct_with_generic() {
+ check_diagnostics(
+ r#"
+struct X<T>(T);
+fn any<T>() -> X<T> { loop {} }
+fn foo() {
+ let _x = any();
+ // ^^^^^ error: type annotations needed
+ // | full type: `X<{unknown}>`
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn const_block_does_not_cause_error() {
+ check_diagnostics(
+ r#"
+fn bar<T>(_inner: fn() -> *const T) {}
+
+fn foo() {
+ bar(const { || 0 as *const i32 })
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn async_closure_does_not_trigger() {
+ check_diagnostics(
+ r#"
+//- minicore: async_fn
+struct Task<R>(R);
+fn spawn_in<AsyncFn, R>(_f: AsyncFn) -> Task<R>
+where
+ R: 'static,
+ AsyncFn: AsyncFnOnce(&()) -> R + 'static,
+{
+ loop {}
+}
+
+fn foo() {
+ spawn_in(async move |cx| {});
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn regression_22263() {
+ check_diagnostics(
+ r#"
+trait From<T> {}
+impl<T> From<T> for T {}
+#[rustc_reservation_impl = "blah blah"]
+impl<T> From<!> for T {}
+
+fn any<T>() -> T {
+ loop {}
+}
+fn foo<T, U: From<T>>(_: T) -> U {
+ loop {}
+}
+fn bar() {
+ let _: () = foo(any());
+}
+ "#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs
index fd1674e2a4..e000d6388a 100644
--- a/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -20,7 +20,10 @@ use syntax::AstNode;
// 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 {
+pub(crate) fn typed_hole<'db>(
+ ctx: &DiagnosticsContext<'_, 'db>,
+ d: &hir::TypedHole<'db>,
+) -> Diagnostic {
let display_range = ctx.sema.diagnostics_display_range(d.expr.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)
@@ -41,7 +44,7 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole<'_>) -
.with_fixes(fixes)
}
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole<'_>) -> Option<Vec<Assist>> {
+fn fixes<'db>(ctx: &DiagnosticsContext<'_, 'db>, d: &hir::TypedHole<'db>) -> Option<Vec<Assist>> {
let db = ctx.sema.db;
let root = db.parse_or_expand(d.expr.file_id);
let (original_range, _) =
@@ -166,6 +169,8 @@ fn t<T>() -> T { loop {} }
r#"
fn main() {
let _x = [(); _];
+ // ^ error: type annotations needed
+ // | full type: `[(); _]`
// FIXME: This should trigger error
// let _y: [(); 10] = [(); _];
_ = 0;
diff --git a/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/crates/ide-diagnostics/src/handlers/undeclared_label.rs
index f81d34377d..7efc8a7136 100644
--- a/crates/ide-diagnostics/src/handlers/undeclared_label.rs
+++ b/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -2,7 +2,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: undeclared-label
pub(crate) fn undeclared_label(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UndeclaredLabel,
) -> Diagnostic {
let name = &d.name;
@@ -86,16 +86,18 @@ fn foo() {
check_diagnostics(
r#"
//- minicore: option, try
-fn foo() {
+fn foo() -> Option<()> {
None?;
+ None
}
"#,
);
check_diagnostics(
r#"
//- minicore: option, try, future
-async fn foo() {
+async fn foo() -> Option<()> {
None?;
+ None
}
"#,
);
@@ -103,7 +105,7 @@ async fn foo() {
r#"
//- minicore: option, try, future, fn
async fn foo() {
- || None?;
+ || { None?; Some(()) };
}
"#,
);
diff --git a/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs b/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs
index 5627393f31..b652456c09 100644
--- a/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs
+++ b/crates/ide-diagnostics/src/handlers/unimplemented_builtin_macro.rs
@@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, Severity};
//
// This diagnostic is shown for builtin macros which are not yet implemented by rust-analyzer
pub(crate) fn unimplemented_builtin_macro(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnimplementedBuiltinMacro,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
diff --git a/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs b/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs
new file mode 100644
index 0000000000..d94ceef642
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/unimplemented_trait.rs
@@ -0,0 +1,72 @@
+use hir::HirDisplay;
+
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: unimplemented-trait
+//
+// This diagnostic is triggered when rust-analyzer cannot infer some type.
+pub(crate) fn unimplemented_trait<'db>(
+ ctx: &DiagnosticsContext<'_, 'db>,
+ d: &hir::UnimplementedTrait<'db>,
+) -> Diagnostic {
+ let message = match &d.root_trait_predicate {
+ Some(root_predicate) if *root_predicate != d.trait_predicate => format!(
+ "the trait bound `{}` is not satisfied\n\
+ required by the bound `{}`\n",
+ d.trait_predicate.display(ctx.db(), ctx.display_target),
+ root_predicate.display(ctx.db(), ctx.display_target),
+ ),
+ _ => format!(
+ "the trait bound `{}` is not satisfied",
+ d.trait_predicate.display(ctx.db(), ctx.display_target),
+ ),
+ };
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0277"),
+ message,
+ d.span.map(Into::into),
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn smoke_test() {
+ check_diagnostics(
+ r#"
+trait Trait {}
+impl<T: Trait, const N: usize> Trait for [T; N] {}
+fn foo(_v: impl Trait) {}
+fn bar() {
+ foo(1);
+ // ^^^ error: the trait bound `i32: Trait` is not satisfied
+ foo([1]);
+ // ^^^ error: the trait bound `i32: Trait` is not satisfied
+ // | required by the bound `[i32; 1]: Trait`
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn async_closure_does_not_trigger() {
+ check_diagnostics(
+ r#"
+//- minicore: async_fn
+fn spawn_in<AsyncFn>(_f: AsyncFn)
+where
+ AsyncFn: AsyncFnOnce(),
+{
+}
+
+fn foo() {
+ spawn_in(async move || {});
+}
+
+ "#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs b/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs
new file mode 100644
index 0000000000..7f1b2da482
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/union_expr_must_have_exactly_one_field.rs
@@ -0,0 +1,42 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: union-expr-must-have-exactly-one-field
+//
+// A union expression does not have exactly one field.
+pub(crate) fn union_expr_must_have_exactly_one_field(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::UnionExprMustHaveExactlyOneField,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0784"),
+ "union expressions should have exactly one field",
+ d.expr.map(|it| it.into()),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn union_expr_must_have_exactly_one_field() {
+ check_diagnostics(
+ r#"
+union Bird {
+ pigeon: u8,
+ turtledove: u16,
+}
+
+fn main() {
+ let bird = Bird { pigeon: 0 };
+ let bird = Bird {};
+ // ^^^^^^^ error: union expressions should have exactly one field
+ let bird = Bird { pigeon: 0, turtledove: 1 };
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: union expressions should have exactly one field
+}
+"#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
index 570319c347..dc6ae6f08b 100644
--- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs
+++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -20,7 +20,7 @@ use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, fi
// This diagnostic is shown for files that are not included in any crate, or files that are part of
// crates rust-analyzer failed to discover. The file will not have IDE features available.
pub(crate) fn unlinked_file(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
acc: &mut Vec<Diagnostic>,
file_id: FileId,
) {
@@ -73,7 +73,7 @@ pub(crate) fn unlinked_file(
}
fn fixes(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
file_id: FileId,
trigger_range: TextRange,
) -> Option<Vec<Assist>> {
diff --git a/crates/ide-diagnostics/src/handlers/unreachable_label.rs b/crates/ide-diagnostics/src/handlers/unreachable_label.rs
index 0c9e0d6ce4..52138b7cd5 100644
--- a/crates/ide-diagnostics/src/handlers/unreachable_label.rs
+++ b/crates/ide-diagnostics/src/handlers/unreachable_label.rs
@@ -2,7 +2,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: unreachable-label
pub(crate) fn unreachable_label(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnreachableLabel,
) -> Diagnostic {
let name = &d.name;
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs b/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs
index f181021bdc..7797c665fd 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_assoc_item.rs
@@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is triggered if the referenced associated item does not exist.
pub(crate) fn unresolved_assoc_item(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnresolvedAssocItem,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs b/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs
index 7c3eacf7e3..2c1f1e7283 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_extern_crate.rs
@@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is triggered if rust-analyzer is unable to discover referred extern crate.
pub(crate) fn unresolved_extern_crate(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnresolvedExternCrate,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_field.rs b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
index 59ec259adf..78e13677cf 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_field.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_field.rs
@@ -22,7 +22,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran
//
// This diagnostic is triggered if a field does not exist on a given type.
pub(crate) fn unresolved_field(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnresolvedField<'_>,
) -> Diagnostic {
let method_suffix = if d.method_with_same_name_exists {
@@ -52,7 +52,7 @@ pub(crate) fn unresolved_field(
.with_fixes(fixes(ctx, d))
}
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedField<'_>) -> Option<Vec<Assist>> {
let mut fixes = Vec::new();
if d.method_with_same_name_exists {
fixes.extend(method_fix(ctx, &d.expr));
@@ -62,7 +62,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Option<V
}
// FIXME: Add Snippet Support
-fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Option<Assist> {
+fn field_fix(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedField<'_>) -> Option<Assist> {
// Get the FileRange of the invalid field access
let root = ctx.sema.db.parse_or_expand(d.expr.file_id);
let expr = d.expr.value.to_node(&root).left()?;
@@ -101,7 +101,7 @@ fn field_fix(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedField<'_>) -> Opti
}
fn add_variant_to_union(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
adt_union: Union,
field_name: &str,
suggested_type: Type,
@@ -129,7 +129,7 @@ fn add_variant_to_union(
}
fn add_field_to_struct_fix(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
adt_struct: Struct,
field_name: &str,
suggested_type: Type,
@@ -263,7 +263,7 @@ fn record_field_layout(
// FIXME: We should fill out the call here, move the cursor and trigger signature help
fn method_fix(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
expr_ptr: &InFile<AstPtr<Either<ast::Expr, ast::Pat>>>,
) -> Option<Assist> {
let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_ident.rs b/crates/ide-diagnostics/src/handlers/unresolved_ident.rs
index 801023dabd..6ecf0be825 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_ident.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_ident.rs
@@ -4,7 +4,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is triggered if an expr-position ident is invalid.
pub(crate) fn unresolved_ident(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnresolvedIdent,
) -> Diagnostic {
let mut range =
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_import.rs b/crates/ide-diagnostics/src/handlers/unresolved_import.rs
index 0da535d11b..f9a125de13 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_import.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_import.rs
@@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// This diagnostic is triggered if rust-analyzer is unable to resolve a path in
// a `use` declaration.
pub(crate) fn unresolved_import(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnresolvedImport,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
index 030c82ca0b..9be7ef6fe7 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs
@@ -5,7 +5,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// This diagnostic is triggered if rust-analyzer is unable to resolve the path
// to a macro in a macro invocation.
pub(crate) fn unresolved_macro_call(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnresolvedMacroCall,
) -> Diagnostic {
let display_range = ctx.sema.diagnostics_display_range_for_range(d.range);
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index bd5d134348..93caf281f0 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -17,7 +17,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_ran
//
// This diagnostic is triggered if a method does not exist on a given type.
pub(crate) fn unresolved_method(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnresolvedMethodCall<'_>,
) -> Diagnostic {
let suffix = if d.field_with_same_name.is_some() {
@@ -49,7 +49,10 @@ pub(crate) fn unresolved_method(
.with_fixes(fixes(ctx, d))
}
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall<'_>) -> Option<Vec<Assist>> {
+fn fixes(
+ ctx: &DiagnosticsContext<'_, '_>,
+ d: &hir::UnresolvedMethodCall<'_>,
+) -> Option<Vec<Assist>> {
let field_fix = if let Some(ty) = &d.field_with_same_name {
field_fix(ctx, d, ty)
} else {
@@ -71,7 +74,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedMethodCall<'_>) -> Opt
}
fn field_fix(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnresolvedMethodCall<'_>,
ty: &hir::Type<'_>,
) -> Option<Assist> {
@@ -108,7 +111,7 @@ fn field_fix(
}
fn assoc_func_fix(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnresolvedMethodCall<'_>,
) -> Option<Assist> {
if let Some(f) = d.assoc_func_with_same_name {
diff --git a/crates/ide-diagnostics/src/handlers/unresolved_module.rs b/crates/ide-diagnostics/src/handlers/unresolved_module.rs
index 1a409d7e76..1e0e9105d8 100644
--- a/crates/ide-diagnostics/src/handlers/unresolved_module.rs
+++ b/crates/ide-diagnostics/src/handlers/unresolved_module.rs
@@ -9,7 +9,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
//
// This diagnostic is triggered if rust-analyzer is unable to discover referred module.
pub(crate) fn unresolved_module(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnresolvedModule,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
@@ -32,7 +32,7 @@ pub(crate) fn unresolved_module(
.with_fixes(fixes(ctx, d))
}
-fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> {
+fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::UnresolvedModule) -> Option<Vec<Assist>> {
let root = ctx.sema.db.parse_or_expand(d.decl.file_id);
let unresolved_module = d.decl.value.to_node(&root);
Some(
diff --git a/crates/ide-diagnostics/src/handlers/unused_must_use.rs b/crates/ide-diagnostics/src/handlers/unused_must_use.rs
new file mode 100644
index 0000000000..e8d0717c91
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/unused_must_use.rs
@@ -0,0 +1,132 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: unused-must-use
+//
+// This diagnostic is triggered when a value with the `#[must_use]` attribute
+// is dropped without being used.
+pub(crate) fn unused_must_use<'db>(
+ ctx: &DiagnosticsContext<'_, 'db>,
+ d: &hir::UnusedMustUse<'db>,
+) -> Diagnostic {
+ let message = match d.message {
+ Some(message) => format!("unused return value that must be used: {message}"),
+ None => "unused return value that must be used".to_owned(),
+ };
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcLint("unused_must_use"),
+ message,
+ d.expr.map(Into::into),
+ )
+ .stable()
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn unused_must_use_function_call() {
+ check_diagnostics(
+ r#"
+#[must_use]
+fn produces() -> i32 { 0 }
+fn main() {
+ produces();
+ //^^^^^^^^^^ warn: unused return value that must be used
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unused_must_use_method_call() {
+ check_diagnostics(
+ r#"
+struct S;
+impl S {
+ #[must_use]
+ fn produces(&self) -> i32 { 0 }
+}
+fn main() {
+ let s = S;
+ s.produces();
+ //^^^^^^^^^^^^ warn: unused return value that must be used
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn with_message() {
+ check_diagnostics(
+ r#"
+struct S;
+impl S {
+ #[must_use = "custom message"]
+ fn produces(&self) -> i32 { 0 }
+}
+fn main() {
+ let s = S;
+ s.produces();
+ //^^^^^^^^^^^^ warn: unused return value that must be used: custom message
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unused_must_use_type() {
+ check_diagnostics(
+ r#"
+#[must_use]
+struct Important;
+fn produces() -> Important { Important }
+fn main() {
+ produces();
+ //^^^^^^^^^^ warn: unused return value that must be used
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_warning_when_value_used() {
+ check_diagnostics(
+ r#"
+#[must_use]
+fn produces() -> i32 { 0 }
+fn main() {
+ let _x = produces();
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_warning_when_no_must_use_attribute() {
+ check_diagnostics(
+ r#"
+fn ordinary() -> i32 { 0 }
+fn main() {
+ ordinary();
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn no_warning_when_value_assigned() {
+ check_diagnostics(
+ r#"
+#[must_use]
+fn produces() -> i32 { 0 }
+fn main() {
+ let x;
+ x = produces();
+ let _ = x;
+}
+"#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs
index 52a2f44fd0..afc74445f4 100644
--- a/crates/ide-diagnostics/src/handlers/unused_variables.rs
+++ b/crates/ide-diagnostics/src/handlers/unused_variables.rs
@@ -14,7 +14,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is triggered when a local variable is not used.
pub(crate) fn unused_variables(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
d: &hir::UnusedVariable,
) -> Option<Diagnostic> {
let ast = d.local.primary_source(ctx.sema.db).syntax_ptr();
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 09c9f8eab0..49b3234a11 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -32,9 +32,13 @@ mod handlers {
pub(crate) mod await_outside_of_async;
pub(crate) mod bad_rtn;
pub(crate) mod break_outside_of_loop;
+ pub(crate) mod duplicate_field;
pub(crate) mod elided_lifetimes_in_path;
+ pub(crate) mod expected_array_or_slice_pat;
pub(crate) mod expected_function;
+ pub(crate) mod functional_record_update_on_non_struct;
pub(crate) mod generic_args_prohibited;
+ pub(crate) mod generic_default_refers_to_self;
pub(crate) mod inactive_code;
pub(crate) mod incoherent_impl;
pub(crate) mod incorrect_case;
@@ -42,9 +46,11 @@ mod handlers {
pub(crate) mod incorrect_generics_order;
pub(crate) mod invalid_cast;
pub(crate) mod invalid_derive_target;
+ pub(crate) mod invalid_lhs_of_assignment;
pub(crate) mod macro_error;
pub(crate) mod malformed_derive;
pub(crate) mod mismatched_arg_count;
+ pub(crate) mod mismatched_array_pat_len;
pub(crate) mod missing_fields;
pub(crate) mod missing_lifetime;
pub(crate) mod missing_match_arms;
@@ -53,7 +59,9 @@ mod handlers {
pub(crate) mod mutability_errors;
pub(crate) mod no_such_field;
pub(crate) mod non_exhaustive_let;
+ pub(crate) mod non_exhaustive_record_expr;
pub(crate) mod parenthesized_generic_args_without_fn_trait;
+ pub(crate) mod pattern_arg_in_extern_fn;
pub(crate) mod private_assoc_item;
pub(crate) mod private_field;
pub(crate) mod remove_trailing_return;
@@ -64,9 +72,12 @@ mod handlers {
pub(crate) mod trait_impl_orphan;
pub(crate) mod trait_impl_redundant_assoc_item;
pub(crate) mod type_mismatch;
+ pub(crate) mod type_must_be_known;
pub(crate) mod typed_hole;
pub(crate) mod undeclared_label;
pub(crate) mod unimplemented_builtin_macro;
+ pub(crate) mod unimplemented_trait;
+ pub(crate) mod union_expr_must_have_exactly_one_field;
pub(crate) mod unreachable_label;
pub(crate) mod unresolved_assoc_item;
pub(crate) mod unresolved_extern_crate;
@@ -76,6 +87,7 @@ mod handlers {
pub(crate) mod unresolved_macro_call;
pub(crate) mod unresolved_method;
pub(crate) mod unresolved_module;
+ pub(crate) mod unused_must_use;
pub(crate) mod unused_variables;
// The handlers below are unusual, the implement the diagnostics as well.
@@ -91,7 +103,8 @@ mod tests;
use std::sync::LazyLock;
use hir::{
- Crate, DisplayTarget, InFile, Semantics, db::ExpandDatabase, diagnostics::AnyDiagnostic,
+ Crate, DisplayTarget, InFile, MacroCallIdExt, Semantics, db::ExpandDatabase,
+ diagnostics::AnyDiagnostic,
};
use ide_db::{
FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap,
@@ -190,7 +203,7 @@ impl Diagnostic {
}
fn new_with_syntax_node_ptr(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
code: DiagnosticCode,
message: impl Into<String>,
node: InFile<SyntaxNodePtr>,
@@ -218,6 +231,25 @@ impl Diagnostic {
self.unused = unused;
self
}
+
+ fn main_node(&self, sema: &Semantics<'_, RootDatabase>) -> Option<InFile<SyntaxNode>> {
+ self.main_node.map(|ptr| ptr.with_value(sema.to_node_syntax(ptr))).or_else(|| {
+ let token = sema
+ .parse_guess_edition(self.range.file_id)
+ .syntax()
+ .token_at_offset(self.range.range.start())
+ .right_biased()?;
+ sema.descend_into_macros(token).into_iter().find_map(|token| {
+ let node = sema.ancestors_with_macros(token.parent().unwrap()).find(|node| {
+ let original_range = sema.original_range(node);
+ original_range.file_id.file_id(sema.db) == self.range.file_id
+ && original_range.range.contains_range(self.range.range)
+ })?;
+ let file = sema.hir_file_for(&node);
+ Some(InFile::new(file, node))
+ })
+ })
+ }
}
#[derive(Debug, Clone)]
@@ -276,17 +308,17 @@ impl DiagnosticsConfig {
}
}
-struct DiagnosticsContext<'a> {
+struct DiagnosticsContext<'a, 'db> {
config: &'a DiagnosticsConfig,
- sema: Semantics<'a, RootDatabase>,
+ sema: Semantics<'db, RootDatabase>,
resolve: &'a AssistResolveStrategy,
edition: Edition,
display_target: DisplayTarget,
is_nightly: bool,
}
-impl<'a> DiagnosticsContext<'a> {
- fn db(&self) -> &'a RootDatabase {
+impl<'db> DiagnosticsContext<'_, 'db> {
+ fn db(&self) -> &'db RootDatabase {
self.sema.db
}
}
@@ -395,7 +427,9 @@ pub fn semantic_diagnostics(
let d = match diag {
AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d),
AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d),
+ AnyDiagnostic::ExpectedArrayOrSlicePat(d) => handlers::expected_array_or_slice_pat::expected_array_or_slice_pat(&ctx, &d),
AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
+ AnyDiagnostic::FunctionalRecordUpdateOnNonStruct(d) => handlers::functional_record_update_on_non_struct::functional_record_update_on_non_struct(&ctx, &d),
AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
Some(it) => it,
None => continue,
@@ -419,6 +453,7 @@ pub fn semantic_diagnostics(
},
AnyDiagnostic::MalformedDerive(d) => handlers::malformed_derive::malformed_derive(&ctx, &d),
AnyDiagnostic::MismatchedArgCount(d) => handlers::mismatched_arg_count::mismatched_arg_count(&ctx, &d),
+ AnyDiagnostic::MismatchedArrayPatLen(d) => handlers::mismatched_array_pat_len::mismatched_array_pat_len(&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),
@@ -428,7 +463,11 @@ pub fn semantic_diagnostics(
None => continue,
},
AnyDiagnostic::NonExhaustiveLet(d) => handlers::non_exhaustive_let::non_exhaustive_let(&ctx, &d),
+ AnyDiagnostic::NonExhaustiveRecordExpr(d) => {
+ handlers::non_exhaustive_record_expr::non_exhaustive_record_expr(&ctx, &d)
+ }
AnyDiagnostic::NoSuchField(d) => handlers::no_such_field::no_such_field(&ctx, &d),
+ AnyDiagnostic::DuplicateField(d) => handlers::duplicate_field::duplicate_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),
@@ -452,6 +491,7 @@ pub fn semantic_diagnostics(
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::UnusedMustUse(d) => handlers::unused_must_use::unused_must_use(&ctx, &d),
AnyDiagnostic::UnusedMut(d) => match handlers::mutability_errors::unused_mut(&ctx, &d) {
Some(it) => it,
None => continue,
@@ -477,6 +517,12 @@ pub fn semantic_diagnostics(
AnyDiagnostic::IncorrectGenericsOrder(d) => handlers::incorrect_generics_order::incorrect_generics_order(&ctx, &d),
AnyDiagnostic::MissingLifetime(d) => handlers::missing_lifetime::missing_lifetime(&ctx, &d),
AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d),
+ AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d),
+ AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d),
+ AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d),
+ AnyDiagnostic::PatternArgInExternFn(d) => handlers::pattern_arg_in_extern_fn::pattern_arg_in_extern_fn(&ctx, &d),
+ AnyDiagnostic::UnionExprMustHaveExactlyOneField(d) => handlers::union_expr_must_have_exactly_one_field::union_expr_must_have_exactly_one_field(&ctx, &d),
+ AnyDiagnostic::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d),
};
res.push(d)
}
@@ -489,14 +535,7 @@ pub fn semantic_diagnostics(
let mut lints = res
.iter_mut()
.filter(|it| matches!(it.code, DiagnosticCode::Clippy(_) | DiagnosticCode::RustcLint(_)))
- .filter_map(|it| {
- Some((
- it.main_node.map(|ptr| {
- ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id)))
- })?,
- it,
- ))
- })
+ .filter_map(|it| Some((it.main_node(&ctx.sema)?, it)))
.collect::<Vec<_>>();
// The edition isn't accurate (each diagnostics may have its own edition due to macros),
@@ -543,7 +582,7 @@ fn handle_diag_from_macros(
let mut spans = span_map.spans_for_range(node.text_range());
if spans.any(|span| {
span.ctx.outer_expn(sema.db).is_some_and(|expansion| {
- let macro_call = sema.db.lookup_intern_macro_call(expansion.into());
+ let macro_call = expansion.loc(sema.db);
// We don't want to show diagnostics for non-local macros at all, but proc macros authors
// seem to rely on being able to emit non-warning-free code, so we don't want to show warnings
// for them even when the proc macro comes from the same workspace (in rustc that's not a
@@ -766,7 +805,7 @@ fn unresolved_fix(id: &'static str, label: &str, target: TextRange) -> Assist {
}
fn adjusted_display_range<N: AstNode>(
- ctx: &DiagnosticsContext<'_>,
+ ctx: &DiagnosticsContext<'_, '_>,
diag_ptr: InFile<AstPtr<N>>,
adj: &dyn Fn(N) -> Option<TextRange>,
) -> FileRange {
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs
index fc49542e3c..4b9535ca06 100644
--- a/crates/ide-diagnostics/src/tests.rs
+++ b/crates/ide-diagnostics/src/tests.rs
@@ -57,11 +57,11 @@ fn check_nth_fix(
pub(crate) fn check_fix_with_disabled(
#[rust_analyzer::rust_fixture] ra_fixture_before: &str,
#[rust_analyzer::rust_fixture] ra_fixture_after: &str,
- disabled: impl Iterator<Item = String>,
+ disabled: &[&str],
) {
let mut config = DiagnosticsConfig::test_sample();
config.expr_fill_default = ExprFillDefaultMode::Default;
- config.disabled.extend(disabled);
+ config.disabled.extend(disabled.iter().map(|&disabled| disabled.to_owned()));
check_nth_fix_with_config(config, 0, ra_fixture_before, ra_fixture_after)
}
diff --git a/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs b/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
index 9883bcc84f..301613e920 100644
--- a/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
+++ b/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
@@ -2722,6 +2722,13 @@ fn foo() {
tracing::error!();
}
"#,
- &["E0432", "inactive-code", "unresolved-macro-call", "syntax-error", "macro-error"],
+ &[
+ "E0432",
+ "E0282",
+ "inactive-code",
+ "unresolved-macro-call",
+ "syntax-error",
+ "macro-error",
+ ],
);
}
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index fb885c2ad1..c2b3a3d8d6 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -6,7 +6,8 @@ use ide_db::{
};
use span::{SpanMap, TextRange, TextSize};
use stdx::format_to;
-use syntax::{AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, ast, ted};
+use syntax::syntax_editor::SyntaxEditor;
+use syntax::{AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, ast};
use crate::FilePosition;
@@ -72,7 +73,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
SyntaxKind::MACRO_ITEMS,
position.file_id,
expansion,
- &expansion_span_map,
+ expansion_span_map,
krate,
);
if let Some(err) = err {
@@ -153,7 +154,6 @@ fn expand_macro_recur(
.or_else(|| sema.expand_allowed_builtins(macro_call))?,
item => sema.expand_attr_macro(item)?.map(|it| it.value),
};
- let expanded = expanded.clone_for_update();
if let Some(err) = err {
format_to!(error, "\n{}", err.render_to_string(sema.db));
}
@@ -163,7 +163,7 @@ fn expand_macro_recur(
result_span_map.merge(
TextRange::at(offset_in_original_node, macro_call.syntax().text_range().len()),
expanded.text_range().len(),
- &expansion_span_map,
+ expansion_span_map,
);
Some(expand(sema, expanded, error, result_span_map, u32::from(offset_in_original_node) as i32))
}
@@ -175,6 +175,7 @@ fn expand(
result_span_map: &mut SpanMap,
mut offset_in_original_node: i32,
) -> SyntaxNode {
+ let (editor, expanded) = SyntaxEditor::new(expanded);
let children = expanded.descendants().filter_map(ast::Item::cast);
let mut replacements = Vec::new();
@@ -200,8 +201,8 @@ fn expand(
}
}
- replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
- expanded
+ replacements.into_iter().rev().for_each(|(old, new)| editor.replace(old.syntax(), new));
+ editor.finish().new_root().clone()
}
fn format(
@@ -357,7 +358,7 @@ fn main() {
"#,
expect![[r#"
bar!
- for _ in 0..42{}"#]],
+ for _ in 0..42 {}"#]],
);
}
@@ -433,9 +434,9 @@ 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 {
+ if let Some(it) = ast::TraitDef::cast(container.clone()){
+ }else if let Some(it) = ast::ImplDef::cast(container.clone()){
+ }else {
{
continue
}
@@ -448,6 +449,7 @@ fn main() {
fn macro_expand_match_ast_inside_let_statement() {
check(
r#"
+//- minicore: try
macro_rules! match_ast {
(match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
(match ($node:expr) {}) => {{}};
@@ -593,7 +595,7 @@ struct Foo {}
"#,
expect![[r#"
proc_macros::DeriveIdentity
- struct Foo{}"#]],
+ struct Foo {}"#]],
);
}
@@ -611,7 +613,7 @@ struct Foo {}
expect![[r#"
proc_macros::DeriveIdentity
#[derive(proc_macros::DeriveIdentity)]
- struct Foo{}"#]],
+ struct Foo {}"#]],
);
}
@@ -627,7 +629,7 @@ struct Foo {}
"#,
expect![[r#"
proc_macros::DeriveIdentity
- struct Foo{}"#]],
+ struct Foo {}"#]],
);
check(
r#"
@@ -639,7 +641,7 @@ struct Foo {}
"#,
expect![[r#"
proc_macros::DeriveIdentity
- struct Foo{}"#]],
+ struct Foo {}"#]],
);
}
@@ -782,7 +784,6 @@ foo();
macro_rules! foo {
() => {
fn item(){}
-
};
}
foo();"#]],
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 4cdf0eac75..4890badcbf 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -95,6 +95,11 @@ pub(crate) fn goto_definition(
continue;
}
+ if let Some(n) = find_definition_for_comparison_operators(sema, &token.value) {
+ navs.extend(n);
+ continue;
+ }
+
let parent = token.value.parent()?;
if let Some(question_mark_conversion) = goto_question_mark_conversions(sema, &parent) {
@@ -264,6 +269,62 @@ fn find_definition_for_known_blanket_dual_impls(
Some(def_to_nav(sema, def))
}
+// If the token is a comparison operator (!=, <, <=, >, >=) that resolves to a default trait method, navigate to the corresponding primary method (eq for ne, partial_cmp for the others).
+fn find_definition_for_comparison_operators(
+ sema: &Semantics<'_, RootDatabase>,
+ original_token: &SyntaxToken,
+) -> Option<Vec<NavigationTarget>> {
+ let bin_expr = ast::BinExpr::cast(original_token.parent()?)?;
+
+ let f = sema.resolve_bin_expr(&bin_expr)?;
+ let assoc = f.as_assoc_item(sema.db)?;
+
+ let lhs_type = sema.type_of_expr(&bin_expr.lhs()?)?.original;
+ let rhs_type = sema.type_of_expr(&bin_expr.rhs()?)?.original;
+
+ let t = match assoc.container(sema.db) {
+ hir::AssocItemContainer::Trait(t) => t,
+ hir::AssocItemContainer::Impl(_) => return None, // Already implemented by the type
+ };
+
+ let fn_name = f.name(sema.db);
+ let fn_name_str = fn_name.as_str();
+
+ let trait_name = t.name(sema.db);
+ let trait_name_str = trait_name.as_str();
+
+ let (target_fn_name, expected_trait) = match fn_name_str {
+ "ne" => ("eq", "PartialEq"),
+ "lt" | "le" | "gt" | "ge" => ("partial_cmp", "PartialOrd"),
+ _ => return None,
+ };
+
+ if trait_name_str != expected_trait {
+ return None;
+ }
+
+ let primary_f = t.items(sema.db).into_iter().find_map(|item| {
+ if let hir::AssocItem::Function(func) = item
+ && func.name(sema.db).as_str() == target_fn_name
+ {
+ return Some(func);
+ }
+ None
+ })?;
+
+ // Chalk requires ALL trait substitutions, including `Self`!
+ // We must pass [Self, Rhs]
+ let resolved_f = sema.resolve_trait_impl_method(
+ lhs_type.clone(),
+ t,
+ primary_f,
+ [lhs_type.clone(), rhs_type.clone()],
+ )?;
+
+ let def = Definition::from(resolved_f);
+
+ Some(def_to_nav(sema, def))
+}
fn try_lookup_include_path(
sema: &Semantics<'_, RootDatabase>,
token: InFile<ast::String>,
@@ -4099,4 +4160,24 @@ fn foo() -> Result<(), Bar> {
"#,
);
}
+
+ #[test]
+ fn goto_definition_for_comparison_operators() {
+ check(
+ r#"
+//- minicore: eq, ord
+struct Foo;
+impl PartialEq for Foo {
+ fn eq(&self, other: &Self) -> bool { true }
+ //^^
+}
+
+fn main() {
+ let a = Foo;
+ let b = Foo;
+ let _ = a !=$0 b;
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index e6ef7b8949..12ce457ea6 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -695,14 +695,14 @@ fn merge_map(res: &mut HighlightMap, new: Option<HighlightMap>) {
/// Preorder walk all the expression's child expressions.
/// For macro calls, the callback will be called on the expanded expressions after
/// visiting the macro call itself.
-struct WalkExpandedExprCtx<'a> {
- sema: &'a Semantics<'a, RootDatabase>,
+struct WalkExpandedExprCtx<'a, 'db> {
+ sema: &'a Semantics<'db, RootDatabase>,
depth: usize,
check_ctx: &'static dyn Fn(&ast::Expr) -> bool,
}
-impl<'a> WalkExpandedExprCtx<'a> {
- fn new(sema: &'a Semantics<'a, RootDatabase>) -> Self {
+impl<'a, 'db> WalkExpandedExprCtx<'a, 'db> {
+ fn new(sema: &'a Semantics<'db, RootDatabase>) -> Self {
Self { sema, depth: 0, check_ctx: &is_closure_or_blk_with_modif }
}
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 4d712bf0f0..e08bbc5c21 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -544,7 +544,7 @@ pub(super) fn definition(
let mut body = source.value.body()?.syntax().clone();
if let Some(macro_file) = source.file_id.macro_file() {
let span_map = db.expansion_span_map(macro_file);
- body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into());
+ body = prettify_macro_expansion(db, body, span_map, it.krate(db).into());
}
if env::var_os("RA_DEV").is_some() {
format!("{body}\n{}", render_const_eval_error(db, err, display_target))
@@ -576,7 +576,7 @@ pub(super) fn definition(
let mut body = source.value.body()?.syntax().clone();
if let Some(macro_file) = source.file_id.macro_file() {
let span_map = db.expansion_span_map(macro_file);
- body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into());
+ body = prettify_macro_expansion(db, body, span_map, it.krate(db).into());
}
if env::var_os("RA_DEV").is_some() {
format!("{body}\n{}", render_const_eval_error(db, err, display_target))
@@ -1136,12 +1136,12 @@ fn markup(
}
}
-fn render_memory_layout(
+fn render_memory_layout<'db>(
config: Option<MemoryLayoutHoverConfig>,
- layout: impl FnOnce() -> Result<Layout, LayoutError>,
- offset: impl FnOnce(&Layout) -> Option<u64>,
- padding: impl FnOnce(&Layout) -> Option<(&str, u64)>,
- tag: impl FnOnce(&Layout) -> Option<usize>,
+ layout: impl FnOnce() -> Result<Layout<'db>, LayoutError>,
+ offset: impl FnOnce(&Layout<'db>) -> Option<u64>,
+ padding: impl for<'a> FnOnce(&'a Layout<'db>) -> Option<(&'a str, u64)>,
+ tag: impl FnOnce(&Layout<'db>) -> Option<usize>,
) -> Option<String> {
let config = config?;
let layout = layout().ok()?;
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 491471428f..bf5e0be374 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -3392,7 +3392,7 @@ fn main() { let foo_test = unsafe { fo$0o(1, 2, 3); } }
```
```rust
- pub unsafe fn foo(bar: i32, ...) -> i32
+ pub unsafe extern "C" fn foo(bar: i32, ...) -> i32
```
"#]],
);
@@ -5972,9 +5972,8 @@ const FOO$0: f64 = 1.0f64;
fn hover_const_eval_floating_point() {
check(
r#"
-extern "rust-intrinsic" {
- pub fn expf64(x: f64) -> f64;
-}
+#[rustc_intrinsic]
+pub fn expf64(x: f64) -> f64;
const FOO$0: f64 = expf64(1.2);
"#,
@@ -7152,6 +7151,7 @@ fn f() { let expr = [1, 2, $03$0, 4] }
fn hover_range_functions() {
check_hover_range(
r#"
+//- minicore: unsize, coerce_unsized
fn f<T>(a: &[T]) { }
fn b() { $0f$0(&[1, 2, 3, 4, 5]); }
"#,
@@ -9197,7 +9197,7 @@ extern "C" {
```
```rust
- unsafe fn fun()
+ unsafe extern "C" fn fun()
```
"#]],
);
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 0d2239c71f..a15366fea9 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -687,21 +687,21 @@ impl fmt::Debug for InlayHintLabelPart {
}
#[derive(Debug)]
-struct InlayHintLabelBuilder<'a> {
- sema: &'a Semantics<'a, RootDatabase>,
+struct InlayHintLabelBuilder<'a, 'db> {
+ sema: &'a Semantics<'db, RootDatabase>,
result: InlayHintLabel,
last_part: String,
resolve: bool,
location: Option<LazyProperty<FileRange>>,
}
-impl fmt::Write for InlayHintLabelBuilder<'_> {
+impl fmt::Write for InlayHintLabelBuilder<'_, '_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.last_part.write_str(s)
}
}
-impl HirWrite for InlayHintLabelBuilder<'_> {
+impl HirWrite for InlayHintLabelBuilder<'_, '_> {
fn start_location_link(&mut self, def: ModuleDefId) {
never!(self.location.is_some(), "location link is already started");
self.make_new_part();
@@ -737,7 +737,7 @@ impl HirWrite for InlayHintLabelBuilder<'_> {
}
}
-impl InlayHintLabelBuilder<'_> {
+impl InlayHintLabelBuilder<'_, '_> {
fn make_new_part(&mut self) {
let text = take(&mut self.last_part);
if !text.is_empty() {
@@ -755,18 +755,18 @@ impl InlayHintLabelBuilder<'_> {
}
}
-fn label_of_ty(
- famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
+fn label_of_ty<'db>(
+ famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, 'db>,
config: &InlayHintsConfig<'_>,
- ty: &hir::Type<'_>,
+ ty: &hir::Type<'db>,
display_target: DisplayTarget,
) -> Option<InlayHintLabel> {
- fn rec(
- sema: &Semantics<'_, RootDatabase>,
- famous_defs: &FamousDefs<'_, '_>,
+ fn rec<'db>(
+ sema: &Semantics<'db, RootDatabase>,
+ famous_defs: &FamousDefs<'_, 'db>,
mut max_length: Option<usize>,
- ty: &hir::Type<'_>,
- label_builder: &mut InlayHintLabelBuilder<'_>,
+ ty: &hir::Type<'db>,
+ label_builder: &mut InlayHintLabelBuilder<'_, '_>,
config: &InlayHintsConfig<'_>,
display_target: DisplayTarget,
) -> Result<(), HirDisplayError> {
@@ -790,7 +790,7 @@ fn label_of_ty(
)
});
- let module_def_location = |label_builder: &mut InlayHintLabelBuilder<'_>,
+ let module_def_location = |label_builder: &mut InlayHintLabelBuilder<'_, '_>,
def: ModuleDef,
name| {
let def = def.try_into();
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index f194bb183e..57b723cbd8 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -1336,7 +1336,7 @@ where
{
fn f(&self) {
let x = self.field.foo();
- //^ impl Baz<<<T as Foo>::Assoc as Bar>::Target> + Bar
+ //^ impl Baz<<<… as Foo>::Assoc as Bar>::Target> + Bar
}
}
"#,
diff --git a/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs
index e8d305afb3..63a83ea335 100644
--- a/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/crates/ide/src/inlay_hints/binding_mode.rs
@@ -169,13 +169,14 @@ fn __(
}
match &(0,) {
(x,) | (x,) => (),
- //^^^^^^^^^^^)
- //^^^^^^^^^^^&(
+ //^^^^&
//^ ref
//^ ref
+ //^^^^&
((x,) | (x,)) => (),
- //^^^^^^^^^^^^^&
+ //^^^^&
//^ ref
+ //^^^^&
//^ ref
}
match &mut (0,) {
@@ -183,7 +184,8 @@ fn __(
//^^^^ &mut
//^ ref mut
}
-}"#,
+}
+"#,
);
}
@@ -217,8 +219,8 @@ fn main() {
expect![[r#"
fn main() {
match &(0,) {
- &(&((ref x,) | (ref x,))) => (),
- &((ref x,) | (ref x,)) => (),
+ &(ref x,) | &(ref x,) => (),
+ (&(ref x,) | &(ref x,)) => (),
}
}
"#]],
diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs
index f4ac9c42f4..df2c42a68c 100644
--- a/crates/ide/src/inlay_hints/closure_captures.rs
+++ b/crates/ide/src/inlay_hints/closure_captures.rs
@@ -2,7 +2,6 @@
//!
//! Tests live in [`bind_pat`][super::bind_pat] module.
use ide_db::famous_defs::FamousDefs;
-use ide_db::text_edit::{TextRange, TextSize};
use span::Edition;
use stdx::{TupleExt, never};
use syntax::ast::{self, AstNode};
@@ -29,14 +28,11 @@ pub(super) fn hints(
return None;
}
- let (range, label) = match closure.move_token() {
- Some(t) => (t.text_range(), InlayHintLabel::default()),
+ let (range, label, position, pad_right) = match closure.move_token() {
+ Some(t) => (t.text_range(), InlayHintLabel::default(), InlayHintPosition::After, false),
None => {
- let prev_token = closure.syntax().first_token()?.prev_token()?.text_range();
- (
- TextRange::new(prev_token.end() - TextSize::from(1), prev_token.end()),
- InlayHintLabel::from("move"),
- )
+ let l_pipe = closure.param_list()?.pipe_token()?.text_range();
+ (l_pipe, InlayHintLabel::from("move"), InlayHintPosition::Before, true)
}
};
let mut hint = InlayHint {
@@ -44,9 +40,9 @@ pub(super) fn hints(
kind: InlayKind::ClosureCapture,
label,
text_edit: None,
- position: InlayHintPosition::After,
+ position,
pad_left: false,
- pad_right: true,
+ pad_right,
resolve_parent: Some(closure.syntax().text_range()),
};
hint.label.append_str("(");
@@ -107,7 +103,7 @@ mod tests {
check_with_config(
InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
r#"
-//- minicore: copy, derive
+//- minicore: copy, derive, fn
#[derive(Copy, Clone)]
@@ -121,29 +117,75 @@ fn main() {
let mut baz = NonCopy;
let qux = &mut NonCopy;
|| {
-// ^ move(&foo, bar, baz, qux)
+ // ^ move(&foo, bar, baz, qux)
foo;
bar;
baz;
qux;
};
|| {
-// ^ move(&foo, &bar, &baz, &qux)
+ // ^ move(&foo, &bar, &baz, &qux)
&foo;
&bar;
&baz;
&qux;
};
|| {
-// ^ move(&mut baz)
+ // ^ move(&mut baz)
&mut baz;
};
|| {
-// ^ move(&mut baz, &mut *qux)
+ // ^ move(&mut baz, &mut *qux)
+ baz = NonCopy;
+ *qux = NonCopy;
+ };
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn all_capture_kinds_async_closure() {
+ check_with_config(
+ InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
+ r#"
+//- minicore: copy, derive, fn, future, async_fn
+
+#[derive(Copy, Clone)]
+struct Copy;
+
+struct NonCopy;
+
+fn main() {
+ let foo = Copy;
+ let bar = NonCopy;
+ let mut baz = NonCopy;
+ let qux = &mut NonCopy;
+ async || {
+ // ^ move(&foo, bar, baz, qux)
+ foo;
+ bar;
+ baz;
+ qux;
+ };
+ async || {
+ // ^ move(&foo, &bar, &baz, &qux)
+ &foo;
+ &bar;
+ &baz;
+ &qux;
+ };
+ async || {
+ // ^ move(&mut baz)
+ &mut baz;
+ };
+ async || {
+ // ^ move(&mut baz, &mut *qux)
baz = NonCopy;
*qux = NonCopy;
};
}
+
"#,
);
}
@@ -163,5 +205,18 @@ fn main() {
}
"#,
);
+ check_with_config(
+ InlayHintsConfig { closure_capture_hints: true, ..DISABLED_CONFIG },
+ r#"
+//- minicore: copy, derive
+fn main() {
+ let foo = u32;
+ async move || {
+ // ^^^^ (foo)
+ foo;
+ };
+}
+"#,
+ );
}
}
diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs
index 3af529e8c5..57aba51b4e 100644
--- a/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -37,7 +37,7 @@ pub(super) fn hints(
let def = def.try_into().ok()?;
let (hir, source_map) = hir::Body::with_source_map(sema.db, def);
- let mir = sema.db.mir_body(def).ok()?;
+ let mir = sema.db.mir_body(def.into()).ok()?;
let local_to_binding = mir.local_to_binding_map();
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 0af2a1f820..e131e7bdd1 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -70,7 +70,7 @@ use ide_db::ra_fixture::RaFixtureAnalysis;
use ide_db::{
FxHashMap, FxIndexSet,
base_db::{
- CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath,
+ AbsPathBuf, CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath,
salsa::{Cancelled, Database},
},
prime_caches, symbol_index,
@@ -253,7 +253,7 @@ impl Analysis {
// Creates an analysis instance for a single file, without any external
// dependencies, stdlib support or ability to apply changes. See
// `AnalysisHost` for creating a fully-featured analysis.
- pub fn from_single_file(text: String) -> (Analysis, FileId) {
+ pub fn from_single_file(text: String, proc_macro_cwd: Arc<AbsPathBuf>) -> (Analysis, FileId) {
let mut host = AnalysisHost::default();
let file_id = FileId::from_raw(0);
let mut file_set = FileSet::default();
@@ -267,11 +267,6 @@ impl Analysis {
// Default to enable test for single file.
let mut cfg_options = CfgOptions::default();
- // FIXME: This is less than ideal
- let proc_macro_cwd = Arc::new(
- TryFrom::try_from(&*std::env::current_dir().unwrap().as_path().to_string_lossy())
- .unwrap(),
- );
let crate_attrs = Vec::new();
cfg_options.insert_atom(sym::test);
crate_graph.add_crate_root(
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 99f8634bcb..f70bb3353f 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -6,8 +6,7 @@ use arrayvec::ArrayVec;
use either::Either;
use hir::{
AssocItem, Crate, FieldSource, HasContainer, HasCrate, HasSource, HirDisplay, HirFileId,
- InFile, LocalSource, ModuleSource, Name, Semantics, Symbol, db::ExpandDatabase, sym,
- symbols::FileSymbol,
+ InFile, LocalSource, ModuleSource, Name, Semantics, Symbol, sym, symbols::FileSymbol,
};
use ide_db::{
FileId, FileRange, RootDatabase, SymbolKind,
@@ -580,7 +579,7 @@ impl TryToNav for hir::Field {
|(FileRange { file_id, range: full_range }, focus_range)| {
NavigationTarget::from_syntax(
file_id,
- Symbol::integer(self.index()),
+ sym::Integer::get(self.index()),
focus_range,
full_range,
SymbolKind::Field,
@@ -939,10 +938,9 @@ pub(crate) fn orig_range_with_focus_r(
) -> UpmappingResult<(FileRange, Option<TextRange>)> {
let Some(name) = focus_range else { return orig_range_r(db, hir_file, value) };
- let call = || db.lookup_intern_macro_call(hir_file.macro_file().unwrap());
+ let call = || hir_file.macro_file().unwrap().loc(db);
- let def_range =
- || db.lookup_intern_macro_call(hir_file.macro_file().unwrap()).def.definition_range(db);
+ let def_range = || hir_file.macro_file().unwrap().loc(db).def.definition_range(db);
// FIXME: Also make use of the syntax context to determine which site we are at?
let value_range = InFile::new(hir_file, value).original_node_file_range_opt(db);
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 4ed3d1c7d7..3eb7867a3a 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -580,6 +580,7 @@ pub fn also_calls_foo() {
"#,
false,
false,
+ // FIXME: The ranges here are volatile when minicore changes, that's not good.
expect![[r#"
foo Function FileId(1) 0..15 7..10
@@ -599,7 +600,7 @@ fn main() {
false,
false,
expect![[r#"
- Some Variant FileId(1) 5999..6031 6024..6028
+ Some Variant FileId(1) 6737..6769 6762..6766
FileId(0) 46..50
"#]],
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index 900a885a64..b664187932 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -16,7 +16,7 @@ use std::fmt::Write;
use stdx::{always, format_to, never};
use syntax::{
AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize,
- ast::{self, HasArgList, prec::ExprPrecedence},
+ ast::{self, HasArgList, make, prec::ExprPrecedence},
};
use ide_db::text_edit::TextEdit;
@@ -79,7 +79,10 @@ pub(crate) fn prepare_rename(
let sema = Semantics::new(db);
let source_file = sema.parse_guess_edition(position.file_id);
let syntax = source_file.syntax();
-
+ if let Some(lifetime_token) = syntax.token_at_offset(position.offset).find(|t| t.text() == "'_")
+ {
+ return Ok(RangeInfo::new(lifetime_token.text_range(), ()));
+ }
let res = find_definitions(&sema, syntax, position, &Name::new_symbol_root(sym::underscore))?
.filter(|(_, _, def, _, _)| def.range_for_rename(&sema).is_some())
.map(|(frange, kind, _, _, _)| {
@@ -133,6 +136,13 @@ pub(crate) fn rename(
let edition = file_id.edition(db);
let (new_name, kind) = IdentifierKind::classify(edition, new_name)?;
+ if kind == IdentifierKind::Lifetime
+ && let Some(lifetime_token) =
+ syntax.token_at_offset(position.offset).find(|t| t.text() == "'_")
+ {
+ let new_name_str = new_name.display(db, edition).to_string();
+ return rename_elided_lifetime(position, lifetime_token, &new_name_str);
+ }
let defs = find_definitions(&sema, syntax, position, &new_name)?;
let alias_fallback =
@@ -797,6 +807,30 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: String) -> O
Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
}
+fn rename_elided_lifetime(
+ position: FilePosition,
+ lifetime_token: syntax::SyntaxToken,
+ new_name: &str,
+) -> RenameResult<SourceChange> {
+ let parent = lifetime_token.parent().unwrap();
+ let root = parent.ancestors().last().unwrap();
+
+ let mut builder = SourceChangeBuilder::new(position.file_id);
+
+ let editor = builder.make_editor(&root);
+
+ editor.replace(lifetime_token, make::lifetime(new_name).syntax().clone());
+
+ if let Some(has_generic_params) = parent.ancestors().find_map(ast::AnyHasGenericParams::cast) {
+ let lifetime_param = make::lifetime_param(make::lifetime(new_name));
+ editor.add_generic_param(&has_generic_params, lifetime_param.into());
+ }
+
+ builder.add_file_edits(position.file_id, editor);
+
+ Ok(builder.finish())
+}
+
#[cfg(test)]
mod tests {
use expect_test::{Expect, expect};
@@ -1326,8 +1360,8 @@ impl Foo {
struct Foo { foo$0: i32 }
impl Foo {
- fn new(foo: i32) -> Self {
- Self { foo }
+ fn foo(foo: i32) {
+ Self { foo };
}
}
"#,
@@ -1335,8 +1369,8 @@ impl Foo {
struct Foo { field: i32 }
impl Foo {
- fn new(foo: i32) -> Self {
- Self { field: foo }
+ fn foo(foo: i32) {
+ Self { field: foo };
}
}
"#,
@@ -3930,4 +3964,198 @@ fn bar() {
"#,
);
}
+
+ #[test]
+ fn rename_constructor_locals() {
+ check(
+ "field",
+ r#"
+struct Struct {
+ struct_field$0: String,
+}
+
+impl Struct {
+ fn new(struct_field: String) -> Self {
+ if false {
+ return Self { struct_field };
+ }
+ Self { struct_field }
+ }
+}
+
+mod foo {
+ macro_rules! m {
+ ($it:expr) => { return $it };
+ }
+
+ impl crate::Struct {
+ fn with_foo(struct_field: String) -> crate::Struct {
+ m!(crate::Struct { struct_field });
+ }
+ }
+}
+ "#,
+ r#"
+struct Struct {
+ field: String,
+}
+
+impl Struct {
+ fn new(field: String) -> Self {
+ if false {
+ return Self { field };
+ }
+ Self { field }
+ }
+}
+
+mod foo {
+ macro_rules! m {
+ ($it:expr) => { return $it };
+ }
+
+ impl crate::Struct {
+ fn with_foo(field: String) -> crate::Struct {
+ m!(crate::Struct { field });
+ }
+ }
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn test_rename_elided_lifetime_fn_no_generics() {
+ check(
+ "'a",
+ r#"
+fn foo(x: &'_$0 str) {}
+"#,
+ r#"
+fn foo<'a>(x: &'a str) {}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_rename_elided_lifetime_fn_with_generics() {
+ check(
+ "'a",
+ r#"
+fn foo<T>(x: &'_$0 str, y: T) {}
+"#,
+ r#"
+fn foo<'a, T>(x: &'a str, y: T) {}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_rename_elided_lifetime_impl_no_generics() {
+ check(
+ "'a",
+ r#"
+struct Foo<'a>(&'a str);
+impl Foo<'_$0> {}
+"#,
+ r#"
+struct Foo<'a>(&'a str);
+impl<'a> Foo<'a> {}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_rename_elided_lifetime_impl_with_generics() {
+ check(
+ "'a",
+ r#"
+struct Foo<'a, T>(&'a str, T);
+impl<T> Foo<'_$0, T> {}
+"#,
+ r#"
+struct Foo<'a, T>(&'a str, T);
+impl<'a, T> Foo<'a, T> {}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_rename_mut_pattern_with_macro() {
+ check(
+ "new",
+ r#"
+//- minicore: option
+macro_rules! pat_macro {
+ ($pat:pat) => {
+ $pat
+ };
+}
+
+pub fn main() {
+ match None {
+ pat_macro!(Some(mut old$0)) => {
+ old += 1,
+ }
+ None => {}
+ }
+}
+"#,
+ r#"
+macro_rules! pat_macro {
+ ($pat:pat) => {
+ $pat
+ };
+}
+
+pub fn main() {
+ match None {
+ pat_macro!(Some(mut new)) => {
+ new += 1,
+ }
+ None => {}
+ }
+}
+"#,
+ );
+ }
+ #[test]
+ fn test_rename_ref_pattern_with_macro() {
+ check(
+ "new",
+ r#"
+//- minicore: option
+macro_rules! pat_macro {
+ ($pat:pat) => {
+ $pat
+ };
+}
+
+pub fn main() {
+ match None {
+ pat_macro!(Some(ref old$0)) => {
+ old += 1,
+ }
+ None => {}
+ }
+}
+"#,
+ r#"
+macro_rules! pat_macro {
+ ($pat:pat) => {
+ $pat
+ };
+}
+
+pub fn main() {
+ match None {
+ pat_macro!(Some(ref new)) => {
+ new += 1,
+ }
+ None => {}
+ }
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index cf796b2715..edcf0dc22b 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -381,8 +381,7 @@ fn signature_help_for_generics(
}
}
GenericParam::ConstParam(param) => {
- if let Some(expr) = param.default(db, display_target).and_then(|konst| konst.expr())
- {
+ if let Some(expr) = param.default(db, display_target) {
format_to!(buf, " = {}", expr);
}
}
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 6afe5681a9..76bb06328b 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -9,6 +9,7 @@ use syntax::{
SyntaxNode, TextRange, TextSize,
ast::{self, IsString},
};
+use triomphe::Arc;
use crate::{
Analysis, HlMod, HlRange, HlTag, RootDatabase,
@@ -92,6 +93,7 @@ pub(super) fn doc_comment(
Some(it) => it,
None => return,
};
+ let vfs_file_id = src_file_id.file_id(sema.db);
let src_file_id: HirFileId = src_file_id.into();
let Some(docs) = attributes.hir_docs(sema.db) else { return };
@@ -171,7 +173,16 @@ pub(super) fn doc_comment(
inj.add_unmapped("\n}");
- let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text());
+ let proc_macro_cwd = {
+ match sema.first_crate(vfs_file_id) {
+ Some(krate) => krate.base().data(sema.db).proc_macro_cwd.clone(),
+ None => {
+ // Arbitrarily pick /, since from_single_file treats this file as as /main.rs anyway.
+ Arc::new(ide_db::base_db::AbsPathBuf::try_from("/").unwrap())
+ }
+ }
+ };
+ let (analysis, tmp_file_id) = Analysis::from_single_file(inj.take_text(), proc_macro_cwd);
if let Ok(ranges) = analysis.with_db(|db| {
super::highlight(
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
index fa7f7b1cba..6b63edf563 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html
@@ -54,7 +54,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="comment">// This is another normal comment</span>
<span class="attribute_bracket attribute macro proc_macro">#</span><span class="attribute_bracket attribute macro proc_macro">[</span><span class="attribute attribute default_library library macro proc_macro">derive</span><span class="parenthesis attribute macro proc_macro">(</span><span class="derive attribute default_library library macro">Copy</span><span class="comma attribute macro proc_macro">,</span> <span class="unresolved_reference attribute macro">Unresolved</span><span class="parenthesis attribute macro proc_macro">)</span><span class="attribute_bracket attribute macro proc_macro">]</span>
<span class="comment">// The reason for these being here is to test AttrIds</span>
+<span class="attribute_bracket attribute macro proc_macro">#</span><span class="attribute_bracket attribute macro proc_macro">[</span><span class="derive_helper attribute default_library library macro proc_macro">default</span><span class="attribute_bracket attribute macro proc_macro">]</span>
<span class="keyword macro proc_macro">enum</span> <span class="enum declaration macro proc_macro">Foo</span> <span class="brace macro proc_macro">{</span>
<span class="attribute_bracket attribute macro proc_macro">#</span><span class="attribute_bracket attribute macro proc_macro">[</span><span class="derive_helper attribute default_library library macro proc_macro">default</span><span class="attribute_bracket attribute macro proc_macro">]</span>
- <span class="enum_variant declaration macro proc_macro">Bar</span>
-<span class="brace macro proc_macro">}</span></code></pre> \ No newline at end of file
+ <span class="enum_variant declaration macro proc_macro">Bar</span> <span class="brace macro proc_macro">{</span>
+ <span class="attribute_bracket attribute macro proc_macro">#</span><span class="attribute_bracket attribute macro proc_macro">[</span><span class="derive_helper attribute default_library library macro proc_macro">default</span><span class="attribute_bracket attribute macro proc_macro">]</span>
+ <span class="field declaration macro proc_macro">field</span><span class="colon macro proc_macro">:</span> <span class="builtin_type macro proc_macro">i32</span>
+ <span class="brace macro proc_macro">}</span>
+<span class="brace macro proc_macro">}</span>
+
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="attribute attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="derive attribute default_library library macro">Default</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="derive_helper attribute default_library library">default</span><span class="attribute_bracket attribute">]</span>
+<span class="keyword">struct</span> <span class="struct declaration">Bar</span><span class="parenthesis">(</span><span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="derive_helper attribute default_library library">default</span><span class="attribute_bracket attribute">]</span> <span class="builtin_type">i32</span><span class="parenthesis">)</span><span class="semicolon">;</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index d687cb40a9..ab69578ed9 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -39,10 +39,18 @@ fn attributes() {
// This is another normal comment
#[derive(Copy, Unresolved)]
// The reason for these being here is to test AttrIds
+#[default]
enum Foo {
#[default]
- Bar
+ Bar {
+ #[default]
+ field: i32
+ }
}
+
+#[derive(Default)]
+#[default]
+struct Bar(#[default] i32);
"#,
expect_file!["./test_data/highlight_attributes.html"],
false,
@@ -1348,7 +1356,7 @@ fn benchmark_syntax_highlighting_parser() {
})
.count()
};
- assert_eq!(hash, 1631);
+ assert_eq!(hash, 1644);
}
#[test]
diff --git a/crates/ide/src/view_memory_layout.rs b/crates/ide/src/view_memory_layout.rs
index 47ca616f31..1b9df9722b 100644
--- a/crates/ide/src/view_memory_layout.rs
+++ b/crates/ide/src/view_memory_layout.rs
@@ -108,7 +108,7 @@ pub(crate) fn view_memory_layout(
nodes: &mut Vec<MemoryLayoutNode>,
db: &RootDatabase,
ty: &Type<'_>,
- layout: &Layout,
+ layout: &Layout<'_>,
parent_idx: usize,
display_target: DisplayTarget,
) {
diff --git a/crates/intern/Cargo.toml b/crates/intern/Cargo.toml
index 39320ebd1c..1e6e8ff32f 100644
--- a/crates/intern/Cargo.toml
+++ b/crates/intern/Cargo.toml
@@ -19,6 +19,7 @@ hashbrown.workspace = true
rustc-hash.workspace = true
triomphe.workspace = true
rayon.workspace = true
+arrayvec.workspace = true
[lints]
workspace = true
diff --git a/crates/intern/src/symbol.rs b/crates/intern/src/symbol.rs
index 8b2d6e8717..72d32d1017 100644
--- a/crates/intern/src/symbol.rs
+++ b/crates/intern/src/symbol.rs
@@ -73,10 +73,9 @@ impl TaggedArcPtr {
///
/// You can only drop the `Arc` if the instance is dropped.
#[inline]
- pub(crate) unsafe fn try_as_arc_owned(self) -> Option<ManuallyDrop<Arc<Box<str>>>> {
+ unsafe fn try_as_arc_owned(self) -> Option<ManuallyDrop<Arc<Box<str>>>> {
// Unpack the tag from the alignment niche
- let tag = self.packed.as_ptr().addr() & Self::BOOL_BITS;
- if tag != 0 {
+ if self.is_arc() {
// Safety: We checked that the tag is non-zero -> true, so we are pointing to the data offset of an `Arc`
Some(ManuallyDrop::new(unsafe {
Arc::from_raw(self.pointer().as_ptr().cast::<Box<str>>())
@@ -87,6 +86,11 @@ impl TaggedArcPtr {
}
#[inline]
+ fn is_arc(&self) -> bool {
+ (self.packed.as_ptr().addr() & Self::BOOL_BITS) != 0
+ }
+
+ #[inline]
fn pack_arc(ptr: NonNull<*const str>) -> NonNull<*const str> {
let packed_tag = true as usize;
@@ -161,28 +165,6 @@ impl Symbol {
unsafe { bucket.as_ref().0.clone() }
}
- pub fn integer(i: usize) -> Self {
- match i {
- 0 => symbols::INTEGER_0,
- 1 => symbols::INTEGER_1,
- 2 => symbols::INTEGER_2,
- 3 => symbols::INTEGER_3,
- 4 => symbols::INTEGER_4,
- 5 => symbols::INTEGER_5,
- 6 => symbols::INTEGER_6,
- 7 => symbols::INTEGER_7,
- 8 => symbols::INTEGER_8,
- 9 => symbols::INTEGER_9,
- 10 => symbols::INTEGER_10,
- 11 => symbols::INTEGER_11,
- 12 => symbols::INTEGER_12,
- 13 => symbols::INTEGER_13,
- 14 => symbols::INTEGER_14,
- 15 => symbols::INTEGER_15,
- i => Symbol::intern(&format!("{i}")),
- }
- }
-
pub fn empty() -> Self {
symbols::__empty
}
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 614411598b..ac6daaf006 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -1,15 +1,78 @@
//! Module defining all known symbols required by the rest of rust-analyzer.
#![allow(non_upper_case_globals)]
-use std::hash::{BuildHasher, BuildHasherDefault};
+use std::{
+ hash::{BuildHasher, BuildHasherDefault},
+ ptr::NonNull,
+};
+use arrayvec::ArrayString;
use dashmap::{DashMap, SharedValue};
use rustc_hash::FxHasher;
use crate::{Symbol, symbol::TaggedArcPtr};
+macro_rules! last {
+ ( $($elems:literal,)+ ) => {
+ *[ $($elems,)* ].last().unwrap()
+ };
+}
+
+impl Integer {
+ #[inline]
+ pub fn as_uint(sym: &Symbol) -> Option<usize> {
+ if !sym.repr.is_arc() {
+ let elem_ref = sym.repr.pointer();
+ // SAFETY: The types have the same layout.
+ let elem_ref = unsafe { std::mem::transmute::<NonNull<*const str>, &&str>(elem_ref) };
+ Self::LIST.element_offset(elem_ref)
+ } else {
+ Self::as_uint_cold(sym)
+ }
+ }
+
+ #[cold]
+ fn as_uint_cold(sym: &Symbol) -> Option<usize> {
+ sym.as_str().parse().ok()
+ }
+}
+
macro_rules! define_symbols {
- (@WITH_NAME: $($alias:ident = $value:literal,)* @PLAIN: $($name:ident,)*) => {
+ (
+ @LISTS: $($list_type_name:ident = $list_prefix:literal + [ $($list_idx:literal,)+ ],)*
+ @WITH_NAME: $($alias:ident = $value:literal,)*
+ @PLAIN: $($name:ident,)*
+ ) => {
+ $(
+ pub enum $list_type_name {}
+ impl $list_type_name {
+ // Ensure we covered all numbers.
+ const LIST: &[&str; last!($($list_idx,)+) + 1] = {
+ static LIST: [&str; last!($($list_idx,)+) + 1] = [ $( concat!($list_prefix, $list_idx), )* ];
+ &LIST
+ };
+
+ #[cold]
+ #[inline(never)]
+ fn create(idx: usize) -> Symbol {
+ use std::fmt::Write;
+ const MAX_LEN: usize = $list_prefix.len() + u64::MAX.ilog10() as usize + 1;
+ let mut s = ArrayString::<MAX_LEN>::new();
+ s.push_str($list_prefix);
+ _ = write!(s, "{idx}");
+ Symbol::intern(&s)
+ }
+
+ #[inline]
+ pub fn get(idx: usize) -> Symbol {
+ match Self::LIST.get(idx) {
+ Option::Some(s) => Symbol { repr: TaggedArcPtr::non_arc(s) },
+ Option::None => Self::create(idx),
+ }
+ }
+ }
+ )*
+
// The strings should be in `static`s so that symbol equality holds.
$(
pub const $name: Symbol = {
@@ -32,6 +95,14 @@ macro_rules! define_symbols {
let hash_one = |it_: &str| hasher_.hash_one(it_);
{
$(
+ for s in $list_type_name::LIST {
+ let hash_ = hash_one(s);
+ let shard_idx_ = dashmap_.determine_shard(hash_ as usize);
+ let symbol = Symbol { repr: TaggedArcPtr::non_arc(s) };
+ dashmap_.shards_mut()[shard_idx_].get_mut().insert(hash_, (symbol, SharedValue::new(())), |(x, _)| hash_one(x.as_str()));
+ }
+ )*
+ $(
let s = stringify!($name);
let hash_ = hash_one(s);
let shard_idx_ = dashmap_.determine_shard(hash_ as usize);
@@ -49,25 +120,37 @@ macro_rules! define_symbols {
};
}
define_symbols! {
+ @LISTS:
+ Integer = "" + [
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
+ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
+ 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225,
+ 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250,
+ 251, 252, 253, 254, 255,
+ ],
+ RaGeneratedName = "<ra@gennew>" + [
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,
+ 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100,
+ 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150,
+ 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200,
+ 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225,
+ 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250,
+ 251, 252, 253, 254, 255,
+ ],
+
@WITH_NAME:
dotdotdot = "...",
- INTEGER_0 = "0",
- INTEGER_1 = "1",
- INTEGER_2 = "2",
- INTEGER_3 = "3",
- INTEGER_4 = "4",
- INTEGER_5 = "5",
- INTEGER_6 = "6",
- INTEGER_7 = "7",
- INTEGER_8 = "8",
- INTEGER_9 = "9",
- INTEGER_10 = "10",
- INTEGER_11 = "11",
- INTEGER_12 = "12",
- INTEGER_13 = "13",
- INTEGER_14 = "14",
- INTEGER_15 = "15",
__empty = "",
unsafe_ = "unsafe",
in_ = "in",
@@ -128,6 +211,9 @@ define_symbols! {
as_str,
asm,
assert,
+ async_iter,
+ async_iterator,
+ AsyncIterator,
attr,
attributes,
begin_panic,
@@ -194,6 +280,7 @@ define_symbols! {
Default,
deprecated,
deref_mut,
+ deref_pure,
deref_target,
deref,
derive_const,
@@ -303,7 +390,6 @@ define_symbols! {
Iterator,
iterator,
fused_iterator,
- async_iterator,
keyword,
lang,
lang_items,
@@ -329,6 +415,7 @@ define_symbols! {
module_path,
mul_assign,
mul,
+ must_use,
naked_asm,
ne,
neg,
@@ -578,4 +665,9 @@ define_symbols! {
field,
field_base,
field_type,
+ ref_pat_eat_one_layer_2024,
+ ref_pat_eat_one_layer_2024_structural,
+ deref_patterns,
+ mut_ref,
+ type_changing_struct_update,
}
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index 839df18159..801eaeaea9 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -657,7 +657,7 @@ impl ProcMacroExpander for Expander {
let mut current_ctx = span.ctx;
while let Some(macro_call_id) = current_ctx.outer_expn(db) {
- let macro_call_loc = db.lookup_intern_macro_call(macro_call_id.into());
+ let macro_call_loc = hir_expand::MacroCallId::from(macro_call_id).loc(db);
let call_site_file = macro_call_loc.kind.file_id();
@@ -701,7 +701,7 @@ impl ProcMacroExpander for Expander {
};
if let Some(macro_call_id) = span.ctx.outer_expn(db) {
- let macro_call_loc = db.lookup_intern_macro_call(macro_call_id.into());
+ let macro_call_loc = hir_expand::MacroCallId::from(macro_call_id).loc(db);
let call_site_file = macro_call_loc.kind.file_id();
let call_site_ast_id = macro_call_loc.kind.erased_ast_id();
diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs
index de8c3f2e55..9088efeca4 100644
--- a/crates/macros/src/lib.rs
+++ b/crates/macros/src/lib.rs
@@ -5,7 +5,7 @@ use syn::parse_quote;
use synstructure::decl_derive;
decl_derive!(
- [TypeFoldable, attributes(type_foldable)] =>
+ [TypeFoldable, attributes(type_foldable, type_visitable)] =>
/// Derives `TypeFoldable` for the annotated `struct` or `enum` (`union` is not supported).
///
/// The fold will produce a value of the same struct or enum variant as the input, with
@@ -102,8 +102,10 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
vi.construct(|_, index| {
let bind = &bindings[index];
- // retain value of fields with #[type_foldable(identity)]
- if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") {
+ // retain value of fields with #[type_foldable(identity)] or #[type_visitable(ignore)]
+ if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity")
+ || has_ignore_attr(&bind.ast().attrs, "type_visitable", "ignore")
+ {
bind.to_token_stream()
} else {
quote! {
@@ -118,8 +120,10 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
vi.construct(|_, index| {
let bind = &bindings[index];
- // retain value of fields with #[type_foldable(identity)]
- if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") {
+ // retain value of fields with #[type_foldable(identity)] or #[type_visitable(ignore)]
+ if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity")
+ || has_ignore_attr(&bind.ast().attrs, "type_visitable", "ignore")
+ {
bind.to_token_stream()
} else {
quote! {
diff --git a/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs
index e8e7928c26..e135291d89 100644
--- a/crates/mbe/src/expander/transcriber.rs
+++ b/crates/mbe/src/expander/transcriber.rs
@@ -222,7 +222,7 @@ fn expand_subtree(
let index =
ctx.nesting.get(ctx.nesting.len() - 1 - depth).map_or(0, |nest| nest.idx);
builder.push(tt::Leaf::Literal(tt::Literal {
- text_and_suffix: Symbol::integer(index),
+ text_and_suffix: sym::Integer::get(index),
span: ctx.call_site,
kind: tt::LitKind::Integer,
suffix_len: 0,
@@ -234,7 +234,7 @@ fn expand_subtree(
0
});
builder.push(tt::Leaf::Literal(tt::Literal {
- text_and_suffix: Symbol::integer(length),
+ text_and_suffix: sym::Integer::get(length),
span: ctx.call_site,
kind: tt::LitKind::Integer,
suffix_len: 0,
@@ -278,7 +278,7 @@ fn expand_subtree(
let res = count(binding, 0, depth.unwrap_or(0));
builder.push(tt::Leaf::Literal(tt::Literal {
- text_and_suffix: Symbol::integer(res),
+ text_and_suffix: sym::Integer::get(res),
span: ctx.call_site,
suffix_len: 0,
kind: tt::LitKind::Integer,
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 936cf178f0..76fdac097f 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -374,6 +374,13 @@ impl<T, E> ValueResult<T, E> {
ValueResult { value: f(self.value), err: self.err }
}
+ pub fn as_ref(&self) -> ValueResult<&T, E>
+ where
+ E: Clone,
+ {
+ ValueResult { value: &self.value, err: self.err.clone() }
+ }
+
pub fn map_err<E2>(self, f: impl FnOnce(E) -> E2) -> ValueResult<T, E2> {
ValueResult { value: self.value, err: self.err.map(f) }
}
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 4a1af31656..271dfc877b 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -237,7 +237,7 @@ fn expr_2021() {
PUNCH ; [alone] 0:Root[0000, 0]@39..40#ROOT2024
_;
- (const {
+ (const {
1
});"#]],
);
diff --git a/crates/parser/src/event.rs b/crates/parser/src/event.rs
index 5be9cb2a24..001adfb780 100644
--- a/crates/parser/src/event.rs
+++ b/crates/parser/src/event.rs
@@ -2,7 +2,7 @@
//! It is intended to be completely decoupled from the
//! parser, so as to allow to evolve the tree representation
//! and the parser algorithm independently.
-use std::mem;
+use std::{mem, num::NonZeroU32};
use crate::{
SyntaxKind::{self, *},
@@ -12,7 +12,13 @@ use crate::{
/// `Parser` produces a flat list of `Event`s.
/// They are converted to a tree-structure in
/// a separate pass, via `TreeBuilder`.
-#[derive(Debug, PartialEq)]
+///
+/// Kept to 8 bytes: error messages live in a side table on the `Parser`
+/// (the `errors` vec) and `Event::Error` only stores an index into it.
+/// `forward_parent` uses `NonZeroU32` so `Option` is niche-optimised away
+/// (the offset is always ≥ 1 because the forward parent sits later in the
+/// event stream).
+#[derive(Debug, PartialEq, Clone, Copy)]
pub(crate) enum Event {
/// This event signifies the start of the node.
/// It should be either abandoned (in which case the
@@ -53,10 +59,7 @@ pub(crate) enum Event {
/// ```
///
/// See also `CompletedMarker::precede`.
- Start {
- kind: SyntaxKind,
- forward_parent: Option<u32>,
- },
+ Start { kind: SyntaxKind, forward_parent: Option<NonZeroU32> },
/// Complete the previous `Start` event
Finish,
@@ -65,20 +68,14 @@ pub(crate) enum Event {
/// `n_raw_tokens` is used to glue complex contextual tokens.
/// For example, lexer tokenizes `>>` as `>`, `>`, and
/// `n_raw_tokens = 2` is used to produced a single `>>`.
- Token {
- kind: SyntaxKind,
- n_raw_tokens: u8,
- },
+ Token { kind: SyntaxKind, n_raw_tokens: u8 },
/// When we parse `foo.0.0` or `foo. 0. 0` the lexer will hand us a float literal
/// instead of an integer literal followed by a dot as the lexer has no contextual knowledge.
/// This event instructs whatever consumes the events to split the float literal into
/// the corresponding parts.
- FloatSplitHack {
- ends_in_dot: bool,
- },
- Error {
- msg: String,
- },
+ FloatSplitHack { ends_in_dot: bool },
+ /// Index into the parser's side `errors` vec.
+ Error { err: u32 },
}
impl Event {
@@ -87,13 +84,16 @@ impl Event {
}
}
-/// Generate the syntax tree with the control of events.
-pub(super) fn process(mut events: Vec<Event>) -> Output {
- let mut res = Output::default();
+/// Generate the syntax tree with the control of events. `errors` is the
+/// side table of error messages built up alongside the `events` stream.
+pub(super) fn process(mut events: Vec<Event>, mut errors: Vec<String>) -> Output {
+ // Each event becomes roughly one u32 in Output, so preallocate to avoid
+ // the amortized grow-one churn we used to see in Output::enter_node.
+ let mut res = Output::with_event_capacity(events.len());
let mut forward_parents = Vec::new();
for i in 0..events.len() {
- match mem::replace(&mut events[i], Event::tombstone()) {
+ match events[i] {
Event::Start { kind, forward_parent } => {
// For events[A, B, C], B is A's forward_parent, C is B's forward_parent,
// in the normal control flow, the parent-child relation: `A -> B -> C`,
@@ -104,7 +104,7 @@ pub(super) fn process(mut events: Vec<Event>) -> Output {
let mut idx = i;
let mut fp = forward_parent;
while let Some(fwd) = fp {
- idx += fwd as usize;
+ idx += fwd.get() as usize;
// append `A`'s forward_parent `B`
fp = match mem::replace(&mut events[idx], Event::tombstone()) {
Event::Start { kind, forward_parent } => {
@@ -131,7 +131,13 @@ pub(super) fn process(mut events: Vec<Event>) -> Output {
let ev = mem::replace(&mut events[i + 1], Event::tombstone());
assert!(matches!(ev, Event::Finish), "{ev:?}");
}
- Event::Error { msg } => res.error(msg),
+ Event::Error { err } => {
+ // Move the string out of the side table; each index is visited
+ // exactly once, so swapping with an empty String is cheap and
+ // avoids any clone.
+ let msg = mem::take(&mut errors[err as usize]);
+ res.error(msg);
+ }
}
}
diff --git a/crates/parser/src/frontmatter.rs b/crates/parser/src/frontmatter.rs
index 2747db4327..1edcad64cf 100644
--- a/crates/parser/src/frontmatter.rs
+++ b/crates/parser/src/frontmatter.rs
@@ -263,7 +263,7 @@ pub fn strip_ws_lines(input: &str) -> Option<usize> {
/// True if `c` is considered a whitespace according to Rust language definition.
/// See [Rust language reference](https://doc.rust-lang.org/reference/whitespace.html)
/// for definitions of these classes.
-fn is_whitespace(c: char) -> bool {
+pub(crate) fn is_whitespace(c: char) -> bool {
// This is Pattern_White_Space.
//
// Note that this set is stable (ie, it doesn't change with different
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 76d26c1ecd..3f341c2ab8 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -1,7 +1,5 @@
mod atom;
-use crate::grammar::attributes::ATTRIBUTE_FIRST;
-
use super::*;
pub(super) use atom::{EXPR_RECOVERY_SET, LITERAL_FIRST, literal, parse_asm_expr};
@@ -324,7 +322,7 @@ fn expr_bp(
}
const LHS_FIRST: TokenSet =
- atom::ATOM_EXPR_FIRST.union(TokenSet::new(&[T![&], T![*], T![!], T![.], T![-], T![_]]));
+ atom::ATOM_EXPR_FIRST.union(TokenSet::new(&[T![&], T![*], T![!], T![.], T![-], T![_], T![#]]));
fn lhs(p: &mut Parser<'_>, r: Restrictions) -> Option<(CompletedMarker, BlockLike)> {
let m;
@@ -653,7 +651,7 @@ fn arg_list(p: &mut Parser<'_>) {
T![')'],
T![,],
|| "expected expression".into(),
- EXPR_FIRST.union(ATTRIBUTE_FIRST),
+ EXPR_FIRST,
|p| expr(p).is_some(),
);
m.complete(p, ARG_LIST);
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index 3214fd90f2..cabdd74b09 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -216,16 +216,19 @@ 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![')']) {
+ // test_err tuple_expr_empty_expr
+ // fn foo() {
+ // (,);
+ // (a, , b);
+ // }
+ if p.current() == T![,] {
+ p.error("expected expression");
+ p.bump(T![,]);
+ saw_comma = true;
+ continue;
+ }
+
saw_expr = true;
// test tuple_attrs
@@ -888,6 +891,10 @@ fn return_expr(p: &mut Parser<'_>) -> CompletedMarker {
let m = p.start();
p.bump(T![return]);
if p.at_ts(EXPR_FIRST) {
+ // test return_attr
+ // fn foo() {
+ // return #[attr] 1;
+ // }
expr(p);
}
m.complete(p, RETURN_EXPR)
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index c5c6e04dd4..88a4397232 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -119,6 +119,25 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Res
let mut has_mods = false;
let mut has_extern = false;
+ if p.at(T![impl])
+ && p.nth(1) == T!['(']
+ && ((matches!(p.nth(2), T![crate] | T![super] | T![self]) && p.nth(3) == T![')'])
+ || p.nth(2) == T![in])
+ {
+ // test impl_restrictions
+ // pub impl(crate) unsafe trait Foo {}
+ // impl(in super::bar) trait Bar {}
+ // impl () {}
+ // impl (i32) {}
+ let m = p.start();
+ p.bump(T![impl]);
+ if !opt_visibility_inner(p, false) {
+ p.error("expected an impl restriction");
+ }
+ m.complete(p, IMPL_RESTRICTION);
+ has_mods = true;
+ }
+
// modifiers
if p.at(T![const]) && p.nth(1) != T!['{'] {
p.eat(T![const]);
@@ -167,25 +186,6 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Res
has_mods = true;
}
- if p.at(T![impl])
- && p.nth(1) == T!['(']
- && ((matches!(p.nth(2), T![crate] | T![super] | T![self]) && p.nth(3) == T![')'])
- || p.nth(2) == T![in])
- {
- // test impl_restrictions
- // pub unsafe impl(crate) trait Foo {}
- // impl(in super::bar) trait Bar {}
- // impl () {}
- // impl (i32) {}
- let m = p.start();
- p.bump(T![impl]);
- if !opt_visibility_inner(p, false) {
- p.error("expected an impl restriction");
- }
- m.complete(p, IMPL_RESTRICTION);
- has_mods = true;
- }
-
// test default_item
// default impl T for Foo {}
if p.at_contextual_kw(T![default]) {
diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs
index 4dd44c030f..5726f085a0 100644
--- a/crates/parser/src/grammar/patterns.rs
+++ b/crates/parser/src/grammar/patterns.rs
@@ -199,6 +199,23 @@ fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
}
}
+fn builtin_pat(p: &mut Parser<'_>) -> Option<CompletedMarker> {
+ let m = p.start();
+ p.bump_remap(T![builtin]);
+ p.bump(T![#]);
+ if p.eat_contextual_kw(T![deref]) {
+ // test deref_pat
+ // fn foo() { let builtin # deref(_) = 1; }
+ p.expect(T!['(']);
+ pattern(p);
+ p.expect(T![')']);
+ Some(m.complete(p, DEREF_PAT))
+ } else {
+ m.abandon(p);
+ None
+ }
+}
+
const PAT_RECOVERY_SET: TokenSet = TokenSet::new(&[
T![let],
T![if],
@@ -214,6 +231,10 @@ const PAT_RECOVERY_SET: TokenSet = TokenSet::new(&[
]);
fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option<CompletedMarker> {
+ if p.at_contextual_kw(T![builtin]) && p.nth_at(1, T![#]) {
+ return builtin_pat(p);
+ }
+
let m = match p.current() {
T![box] => box_pat(p),
T![ref] | T![mut] => ident_pat(p, true),
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs
index 4478bf4e37..5900d7cfed 100644
--- a/crates/parser/src/lib.rs
+++ b/crates/parser/src/lib.rs
@@ -56,6 +56,13 @@ pub use crate::{
syntax_kind::SyntaxKind,
};
+/// True if `c` is whitespace in Rust source (Unicode Pattern_White_Space as in the language reference).
+///
+/// See <https://doc.rust-lang.org/reference/whitespace.html>.
+pub fn is_rust_whitespace(c: char) -> bool {
+ frontmatter::is_whitespace(c)
+}
+
/// Parse the whole of the input as a given syntactic construct.
///
/// This covers two main use-cases:
@@ -104,8 +111,8 @@ impl TopEntryPoint {
};
let mut p = parser::Parser::new(input);
entry_point(&mut p);
- let events = p.finish();
- let res = event::process(events);
+ let (events, errors) = p.finish();
+ let res = event::process(events, errors);
if cfg!(debug_assertions) {
let mut depth = 0;
@@ -169,8 +176,8 @@ impl PrefixEntryPoint {
};
let mut p = parser::Parser::new(input);
entry_point(&mut p);
- let events = p.finish();
- event::process(events)
+ let (events, errors) = p.finish();
+ event::process(events, errors)
}
}
@@ -195,7 +202,7 @@ impl Reparser {
let Reparser(r) = self;
let mut p = parser::Parser::new(tokens);
r(&mut p);
- let events = p.finish();
- event::process(events)
+ let (events, errors) = p.finish();
+ event::process(events, errors)
}
}
diff --git a/crates/parser/src/output.rs b/crates/parser/src/output.rs
index 0ea15a656c..2f09b11218 100644
--- a/crates/parser/src/output.rs
+++ b/crates/parser/src/output.rs
@@ -33,6 +33,14 @@ pub enum Step<'a> {
}
impl Output {
+ /// Preallocate the event buffer. Each `Event` in the input stream maps to
+ /// roughly one `u32` in the output, so passing the event count as the
+ /// initial capacity avoids most of the amortized `Vec::grow` allocations
+ /// during `event::process`.
+ pub(crate) fn with_event_capacity(cap: usize) -> Self {
+ Output { event: Vec::with_capacity(cap), error: Vec::new() }
+ }
+
const EVENT_MASK: u32 = 0b1;
const TAG_MASK: u32 = 0x0000_00F0;
const N_INPUT_TOKEN_MASK: u32 = 0x0000_FF00;
diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs
index 4557078de9..57c501eda2 100644
--- a/crates/parser/src/parser.rs
+++ b/crates/parser/src/parser.rs
@@ -1,6 +1,6 @@
//! See [`Parser`].
-use std::cell::Cell;
+use std::{cell::Cell, num::NonZeroU32};
use drop_bomb::DropBomb;
@@ -12,6 +12,14 @@ use crate::{
input::Input,
};
+/// Build a forward-parent offset. The offset is always ≥ 1 because the
+/// forward-parent event is created *after* the event it forwards to, so
+/// `NonZeroU32` is always valid here. Panics only on a parser bug.
+#[inline]
+fn fwd_parent(offset: u32) -> NonZeroU32 {
+ NonZeroU32::new(offset).expect("forward-parent offset must be non-zero")
+}
+
/// `Parser` struct provides the low-level API for
/// navigating through the stream of tokens and
/// constructing the parse tree. The actual parsing
@@ -25,6 +33,9 @@ pub(crate) struct Parser<'t> {
inp: &'t Input,
pos: usize,
events: Vec<Event>,
+ /// Side table of error messages. `Event::Error { err }` carries an index
+ /// into this vec, keeping `Event` itself a flat 8-byte enum.
+ errors: Vec<String>,
steps: Cell<u32>,
}
@@ -32,11 +43,17 @@ const PARSER_STEP_LIMIT: usize = if cfg!(debug_assertions) { 150_000 } else { 15
impl<'t> Parser<'t> {
pub(super) fn new(inp: &'t Input) -> Parser<'t> {
- Parser { inp, pos: 0, events: Vec::with_capacity(2 * inp.len()), steps: Cell::new(0) }
+ Parser {
+ inp,
+ pos: 0,
+ events: Vec::with_capacity(2 * inp.len()),
+ errors: Vec::new(),
+ steps: Cell::new(0),
+ }
}
- pub(crate) fn finish(self) -> Vec<Event> {
- self.events
+ pub(crate) fn finish(self) -> (Vec<Event>, Vec<String>) {
+ (self.events, self.errors)
}
/// Returns the kind of the current token.
@@ -206,7 +223,7 @@ impl<'t> Parser<'t> {
match &mut self.events[idx] {
Event::Start { forward_parent, kind } => {
*kind = SyntaxKind::FIELD_EXPR;
- *forward_parent = Some(new_marker.pos - marker.pos);
+ *forward_parent = Some(fwd_parent(new_marker.pos - marker.pos));
}
_ => unreachable!(),
}
@@ -237,8 +254,9 @@ impl<'t> Parser<'t> {
/// structured errors with spans and notes, like rustc
/// does.
pub(crate) fn error<T: Into<String>>(&mut self, message: T) {
- let msg = message.into();
- self.push_event(Event::Error { msg });
+ let err = self.errors.len() as u32;
+ self.errors.push(message.into());
+ self.push_event(Event::Error { err });
}
/// Consume the next token if it is `kind` or emit an error
@@ -366,7 +384,7 @@ impl CompletedMarker {
let idx = self.start_pos as usize;
match &mut p.events[idx] {
Event::Start { forward_parent, .. } => {
- *forward_parent = Some(new_pos.pos - self.start_pos);
+ *forward_parent = Some(fwd_parent(new_pos.pos - self.start_pos));
}
_ => unreachable!(),
}
@@ -379,7 +397,7 @@ impl CompletedMarker {
let idx = m.pos as usize;
match &mut p.events[idx] {
Event::Start { forward_parent, .. } => {
- *forward_parent = Some(self.start_pos - m.pos);
+ *forward_parent = Some(fwd_parent(self.start_pos - m.pos));
}
_ => unreachable!(),
}
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 59fa3ee773..b1867275ce 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -120,6 +120,7 @@ pub enum SyntaxKind {
CFG_KW,
CLOBBER_ABI_KW,
DEFAULT_KW,
+ DEREF_KW,
DYN_KW,
FORMAT_ARGS_KW,
GEN_KW,
@@ -198,6 +199,7 @@ pub enum SyntaxKind {
CONST_BLOCK_PAT,
CONST_PARAM,
CONTINUE_EXPR,
+ DEREF_PAT,
DYN_TRAIT_TYPE,
ENUM,
EXPR_STMT,
@@ -382,6 +384,7 @@ impl SyntaxKind {
| CONST_BLOCK_PAT
| CONST_PARAM
| CONTINUE_EXPR
+ | DEREF_PAT
| DYN_TRAIT_TYPE
| ENUM
| EXPR_STMT
@@ -627,6 +630,7 @@ impl SyntaxKind {
CFG_ATTR_KW => "cfg_attr",
CLOBBER_ABI_KW => "clobber_abi",
DEFAULT_KW => "default",
+ DEREF_KW => "deref",
DYN_KW => "dyn",
FORMAT_ARGS_KW => "format_args",
GLOBAL_ASM_KW => "global_asm",
@@ -732,6 +736,7 @@ impl SyntaxKind {
CFG_ATTR_KW => true,
CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
+ DEREF_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
FORMAT_ARGS_KW => true,
GLOBAL_ASM_KW => true,
@@ -825,6 +830,7 @@ impl SyntaxKind {
CFG_ATTR_KW => true,
CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
+ DEREF_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
FORMAT_ARGS_KW => true,
GLOBAL_ASM_KW => true,
@@ -981,6 +987,7 @@ impl SyntaxKind {
"cfg_attr" => CFG_ATTR_KW,
"clobber_abi" => CLOBBER_ABI_KW,
"default" => DEFAULT_KW,
+ "deref" => DEREF_KW,
"dyn" if edition < Edition::Edition2018 => DYN_KW,
"format_args" => FORMAT_ARGS_KW,
"global_asm" => GLOBAL_ASM_KW,
@@ -1155,6 +1162,7 @@ macro_rules ! T_ {
[cfg_attr] => { $ crate :: SyntaxKind :: CFG_ATTR_KW };
[clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW };
[default] => { $ crate :: SyntaxKind :: DEFAULT_KW };
+ [deref] => { $ crate :: SyntaxKind :: DEREF_KW };
[dyn] => { $ crate :: SyntaxKind :: DYN_KW };
[format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW };
[global_asm] => { $ crate :: SyntaxKind :: GLOBAL_ASM_KW };
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs
index 6dfb78b128..7aaf270a77 100644
--- a/crates/parser/test_data/generated/runner.rs
+++ b/crates/parser/test_data/generated/runner.rs
@@ -195,6 +195,8 @@ mod ok {
run_and_expect_no_errors("test_data/parser/inline/ok/default_unsafe_item.rs");
}
#[test]
+ fn deref_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/deref_pat.rs"); }
+ #[test]
fn destructuring_assignment_struct_rest_pattern() {
run_and_expect_no_errors(
"test_data/parser/inline/ok/destructuring_assignment_struct_rest_pattern.rs",
@@ -584,6 +586,8 @@ mod ok {
run_and_expect_no_errors("test_data/parser/inline/ok/reference_type.rs");
}
#[test]
+ fn return_attr() { run_and_expect_no_errors("test_data/parser/inline/ok/return_attr.rs"); }
+ #[test]
fn return_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/return_expr.rs"); }
#[test]
fn return_type_syntax_in_path() {
@@ -956,8 +960,8 @@ mod err {
#[test]
fn top_level_let() { run_and_expect_errors("test_data/parser/inline/err/top_level_let.rs"); }
#[test]
- fn tuple_expr_leading_comma() {
- run_and_expect_errors("test_data/parser/inline/err/tuple_expr_leading_comma.rs");
+ fn tuple_expr_empty_expr() {
+ run_and_expect_errors("test_data/parser/inline/err/tuple_expr_empty_expr.rs");
}
#[test]
fn tuple_field_list_recovery() {
diff --git a/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rast b/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rast
new file mode 100644
index 0000000000..2ccd04c321
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rast
@@ -0,0 +1,45 @@
+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 "
+ EXPR_STMT
+ TUPLE_EXPR
+ L_PAREN "("
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ COMMA ","
+ WHITESPACE " "
+ COMMA ","
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
+error 16: expected expression
+error 27: expected expression
diff --git a/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs b/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rs
index 12fab59a77..37f756ffa6 100644
--- a/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rs
+++ b/crates/parser/test_data/parser/inline/err/tuple_expr_empty_expr.rs
@@ -1,3 +1,4 @@
fn foo() {
(,);
+ (a, , b);
}
diff --git a/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast b/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast
deleted file mode 100644
index 3fbc0da400..0000000000
--- a/crates/parser/test_data/parser/inline/err/tuple_expr_leading_comma.rast
+++ /dev/null
@@ -1,24 +0,0 @@
-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/crates/parser/test_data/parser/inline/ok/deref_pat.rast b/crates/parser/test_data/parser/inline/ok/deref_pat.rast
new file mode 100644
index 0000000000..ab4073c081
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/deref_pat.rast
@@ -0,0 +1,36 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ DEREF_PAT
+ BUILTIN_KW "builtin"
+ WHITESPACE " "
+ POUND "#"
+ WHITESPACE " "
+ DEREF_KW "deref"
+ L_PAREN "("
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "1"
+ SEMICOLON ";"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/deref_pat.rs b/crates/parser/test_data/parser/inline/ok/deref_pat.rs
new file mode 100644
index 0000000000..c5c9d9f059
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/deref_pat.rs
@@ -0,0 +1 @@
+fn foo() { let builtin # deref(_) = 1; }
diff --git a/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast
index 5f2680cbaa..bf7b5c5a34 100644
--- a/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast
+++ b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast
@@ -3,8 +3,6 @@ SOURCE_FILE
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
- UNSAFE_KW "unsafe"
- WHITESPACE " "
IMPL_RESTRICTION
IMPL_KW "impl"
VISIBILITY_INNER
@@ -15,6 +13,8 @@ SOURCE_FILE
CRATE_KW "crate"
R_PAREN ")"
WHITESPACE " "
+ UNSAFE_KW "unsafe"
+ WHITESPACE " "
TRAIT_KW "trait"
WHITESPACE " "
NAME
diff --git a/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs
index 0a46b158af..429ac93ad5 100644
--- a/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs
+++ b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs
@@ -1,4 +1,4 @@
-pub unsafe impl(crate) trait Foo {}
+pub impl(crate) unsafe trait Foo {}
impl(in super::bar) trait Bar {}
impl () {}
impl (i32) {}
diff --git a/crates/parser/test_data/parser/inline/ok/return_attr.rast b/crates/parser/test_data/parser/inline/ok/return_attr.rast
new file mode 100644
index 0000000000..d5dcdf0447
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/return_attr.rast
@@ -0,0 +1,34 @@
+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
+ RETURN_EXPR
+ RETURN_KW "return"
+ WHITESPACE " "
+ LITERAL
+ ATTR
+ POUND "#"
+ L_BRACK "["
+ PATH_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "attr"
+ R_BRACK "]"
+ WHITESPACE " "
+ INT_NUMBER "1"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/return_attr.rs b/crates/parser/test_data/parser/inline/ok/return_attr.rs
new file mode 100644
index 0000000000..dc8bd1a797
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/return_attr.rs
@@ -0,0 +1,3 @@
+fn foo() {
+ return #[attr] 1;
+}
diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml
index 8377e94c8d..048d3776ca 100644
--- a/crates/profile/Cargo.toml
+++ b/crates/profile/Cargo.toml
@@ -23,7 +23,7 @@ perf-event = "=0.4.8"
libc.workspace = true
[target.'cfg(windows)'.dependencies]
-windows-sys = { version = "0.60", features = [
+windows-sys = { version = "0.61", features = [
"Win32_System_Threading",
"Win32_System_ProcessStatus",
] }
diff --git a/crates/project-model/src/build_dependencies.rs b/crates/project-model/src/build_dependencies.rs
index aff5391697..e44af96f36 100644
--- a/crates/project-model/src/build_dependencies.rs
+++ b/crates/project-model/src/build_dependencies.rs
@@ -382,7 +382,6 @@ impl WorkspaceBuildScripts {
}
Message::CompilerArtifact(message) => {
with_output_for(&message.package_id, &mut |name, data| {
- progress(format!("proc-macro {name} built"));
if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt {
data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro;
}
@@ -392,6 +391,7 @@ impl WorkspaceBuildScripts {
.kind
.contains(&cargo_metadata::TargetKind::ProcMacro)
{
+ progress(format!("proc-macro {name} built"));
data.proc_macro_dylib_path =
match message.filenames.iter().find(|file| is_dylib(file)) {
Some(filename) => {
diff --git a/crates/project-model/src/cargo_config_file.rs b/crates/project-model/src/cargo_config_file.rs
index 9ce88484f7..ae36deb71f 100644
--- a/crates/project-model/src/cargo_config_file.rs
+++ b/crates/project-model/src/cargo_config_file.rs
@@ -158,15 +158,14 @@ pub(crate) fn make_lockfile_copy(
build: semver::BuildMetadata::EMPTY,
};
- // TODO: turn this into a const and remove pre once 1.95 is stable
- #[allow(non_snake_case)]
- let MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV: semver::Version = semver::Version {
- major: 1,
- minor: 95,
- patch: 0,
- pre: semver::Prerelease::new("beta").unwrap(),
- build: semver::BuildMetadata::EMPTY,
- };
+ const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV: semver::Version =
+ semver::Version {
+ major: 1,
+ minor: 95,
+ patch: 0,
+ pre: semver::Prerelease::EMPTY,
+ build: semver::BuildMetadata::EMPTY,
+ };
let usage = if *toolchain_version >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH_ENV {
LockfileUsage::WithEnvVar
diff --git a/crates/query-group-macro/src/lib.rs b/crates/query-group-macro/src/lib.rs
index 277cc0b269..6e89e0875e 100644
--- a/crates/query-group-macro/src/lib.rs
+++ b/crates/query-group-macro/src/lib.rs
@@ -6,18 +6,15 @@ use std::vec;
use proc_macro::TokenStream;
use proc_macro2::Span;
use queries::{
- GeneratedInputStruct, InputQuery, InputSetter, InputSetterWithDurability, Intern, Lookup,
- Queries, SetterKind, TrackedQuery, Transparent,
+ GeneratedInputStruct, InputQuery, InputSetter, InputSetterWithDurability, Queries, SetterKind,
+ TrackedQuery, Transparent,
};
use quote::{ToTokens, format_ident, quote};
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::visit_mut::VisitMut;
-use syn::{
- Attribute, FnArg, ItemTrait, Path, Token, TraitItem, TraitItemFn, parse_quote,
- parse_quote_spanned,
-};
+use syn::{Attribute, FnArg, ItemTrait, Path, Token, TraitItem, parse_quote, parse_quote_spanned};
mod queries;
@@ -106,7 +103,6 @@ enum QueryKind {
Tracked,
TrackedWithSalsaStruct,
Transparent,
- Interned,
}
#[derive(Default, Debug, Clone)]
@@ -190,7 +186,6 @@ pub(crate) fn query_group_impl(
let mut trait_methods = vec![];
let mut setter_trait_methods = vec![];
let mut lookup_signatures = vec![];
- let mut lookup_methods = vec![];
for item in &mut item_trait.items {
if let syn::TraitItem::Fn(method) = item {
@@ -202,7 +197,6 @@ pub(crate) fn query_group_impl(
let mut query_kind = QueryKind::TrackedWithSalsaStruct;
let mut invoke = None;
let mut cycle = None;
- let mut interned_struct_path = None;
let mut lru = None;
let params: Vec<FnArg> = signature.inputs.clone().into_iter().collect();
@@ -230,22 +224,6 @@ pub(crate) fn query_group_impl(
}
query_kind = QueryKind::Input;
}
- "interned" => {
- let syn::ReturnType::Type(_, ty) = &signature.output else {
- return Err(syn::Error::new(
- span,
- "interned queries must have return type",
- ));
- };
- let syn::Type::Path(path) = &**ty else {
- return Err(syn::Error::new(
- span,
- "interned queries must have return type",
- ));
- };
- interned_struct_path = Some(path.path.clone());
- query_kind = QueryKind::Interned;
- }
"invoke_interned" => {
let path = syn::parse::<Parenthesized<Path>>(tts)?;
invoke = Some(path.0.clone());
@@ -317,28 +295,6 @@ pub(crate) fn query_group_impl(
};
setter_trait_methods.push(SetterKind::WithDurability(setter));
}
- (QueryKind::Interned, None) => {
- let interned_struct_path = interned_struct_path.unwrap();
- let method = Intern {
- signature: signature.clone(),
- pat_and_tys: pat_and_tys.clone(),
- interned_struct_path: interned_struct_path.clone(),
- };
-
- trait_methods.push(Queries::Intern(method));
-
- let mut method = Lookup {
- signature: signature.clone(),
- pat_and_tys: pat_and_tys.clone(),
- return_ty: *return_ty,
- interned_struct_path,
- };
- method.prepare_signature();
-
- lookup_signatures
- .push(TraitItem::Fn(make_trait_method(method.signature.clone())));
- lookup_methods.push(method);
- }
// tracked function. it might have an invoke, or might not.
(QueryKind::Tracked, invoke) => {
let method = TrackedQuery {
@@ -380,13 +336,6 @@ pub(crate) fn query_group_impl(
};
trait_methods.push(Queries::Transparent(method));
}
- // error/invalid constructions
- (QueryKind::Interned, Some(path)) => {
- return Err(syn::Error::new(
- path.span(),
- "Interned queries cannot be used with an `#[invoke]`".to_string(),
- ));
- }
(QueryKind::Input, Some(path)) => {
return Err(syn::Error::new(
path.span(),
@@ -451,8 +400,6 @@ pub(crate) fn query_group_impl(
#(#trait_methods)*
#(#setter_methods)*
-
- #(#lookup_methods)*
}
};
RemoveAttrsFromTraitMethods.visit_item_trait_mut(&mut item_trait);
@@ -485,15 +432,6 @@ where
}
}
-fn make_trait_method(sig: syn::Signature) -> TraitItemFn {
- TraitItemFn {
- attrs: vec![],
- sig: sig.clone(),
- semi_token: Some(syn::Token![;](sig.span())),
- default: None,
- }
-}
-
struct RemoveAttrsFromTraitMethods;
impl VisitMut for RemoveAttrsFromTraitMethods {
diff --git a/crates/query-group-macro/src/queries.rs b/crates/query-group-macro/src/queries.rs
index 83ce8902d0..4221068828 100644
--- a/crates/query-group-macro/src/queries.rs
+++ b/crates/query-group-macro/src/queries.rs
@@ -1,7 +1,7 @@
//! The IR of the `#[query_group]` macro.
use quote::{ToTokens, format_ident, quote, quote_spanned};
-use syn::{FnArg, Ident, PatType, Path, Receiver, ReturnType, Type, parse_quote, spanned::Spanned};
+use syn::{FnArg, Ident, PatType, Path, Receiver, ReturnType, parse_quote, spanned::Spanned};
use crate::Cycle;
@@ -266,80 +266,11 @@ impl ToTokens for Transparent {
method.to_tokens(tokens);
}
}
-pub(crate) struct Intern {
- pub(crate) signature: syn::Signature,
- pub(crate) pat_and_tys: Vec<PatType>,
- pub(crate) interned_struct_path: Path,
-}
-
-impl ToTokens for Intern {
- fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
- let sig = &self.signature;
-
- let ty = self.pat_and_tys.to_vec();
-
- let interned_pat = ty.first().expect("at least one pat; this is a bug");
- let interned_pat = &interned_pat.pat;
-
- let wrapper_struct = self.interned_struct_path.to_token_stream();
-
- let method = quote! {
- #sig {
- #wrapper_struct::new(self, #interned_pat)
- }
- };
-
- method.to_tokens(tokens);
- }
-}
-
-pub(crate) struct Lookup {
- pub(crate) signature: syn::Signature,
- pub(crate) pat_and_tys: Vec<PatType>,
- pub(crate) return_ty: Type,
- pub(crate) interned_struct_path: Path,
-}
-
-impl Lookup {
- pub(crate) fn prepare_signature(&mut self) {
- let sig = &self.signature;
-
- let ident = format_ident!("lookup_{}", sig.ident);
-
- let ty = self.pat_and_tys.to_vec();
-
- let interned_key = &self.return_ty;
-
- let interned_pat = ty.first().expect("at least one pat; this is a bug");
- let interned_return_ty = &interned_pat.ty;
-
- self.signature = parse_quote!(
- fn #ident(&self, id: #interned_key) -> #interned_return_ty
- );
- }
-}
-
-impl ToTokens for Lookup {
- fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
- let sig = &self.signature;
-
- let wrapper_struct = self.interned_struct_path.to_token_stream();
- let method = quote! {
- #sig {
- let zalsa = self.zalsa();
- #wrapper_struct::ingredient(zalsa).data(zalsa, id.as_id()).0.clone()
- }
- };
-
- method.to_tokens(tokens);
- }
-}
#[allow(clippy::large_enum_variant)]
pub(crate) enum Queries {
TrackedQuery(TrackedQuery),
InputQuery(InputQuery),
- Intern(Intern),
Transparent(Transparent),
}
@@ -349,7 +280,6 @@ impl ToTokens for Queries {
Queries::TrackedQuery(tracked_query) => tracked_query.to_tokens(tokens),
Queries::InputQuery(input_query) => input_query.to_tokens(tokens),
Queries::Transparent(transparent) => transparent.to_tokens(tokens),
- Queries::Intern(intern) => intern.to_tokens(tokens),
}
}
}
diff --git a/crates/query-group-macro/tests/interned.rs b/crates/query-group-macro/tests/interned.rs
deleted file mode 100644
index f738185b1f..0000000000
--- a/crates/query-group-macro/tests/interned.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use query_group_macro::query_group;
-
-use expect_test::expect;
-use salsa::plumbing::AsId;
-
-mod logger_db;
-use logger_db::LoggerDb;
-
-#[salsa_macros::interned(no_lifetime)]
-pub struct InternedString {
- data: String,
-}
-
-#[query_group]
-pub trait InternedDB: salsa::Database {
- #[salsa::interned]
- fn intern_string(&self, data: String) -> InternedString;
-
- fn interned_len(&self, id: InternedString) -> usize;
-}
-
-fn interned_len(db: &dyn InternedDB, id: InternedString) -> usize {
- db.lookup_intern_string(id).len()
-}
-
-#[test]
-fn intern_round_trip() {
- let db = LoggerDb::default();
-
- let id = db.intern_string(String::from("Hello, world!"));
- let s = db.lookup_intern_string(id);
-
- assert_eq!(s.len(), 13);
- db.assert_logs(expect![[r#"[]"#]]);
-}
-
-#[test]
-fn intern_with_query() {
- let db = LoggerDb::default();
-
- let id = db.intern_string(String::from("Hello, world!"));
- let len = db.interned_len(id);
-
- assert_eq!(len, 13);
- db.assert_logs(expect![[r#"
- [
- "salsa_event(WillCheckCancellation)",
- "salsa_event(WillExecute { database_key: interned_len_shim(Id(0)) })",
- ]"#]]);
-}
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index da2ec74019..27d9576e29 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -78,7 +78,7 @@ paths.workspace = true
ra-ap-rustc_type_ir.workspace = true
[target.'cfg(windows)'.dependencies]
-windows-sys = { version = "0.60", features = [
+windows-sys = { version = "0.61", features = [
"Win32_System_Diagnostics_Debug",
"Win32_System_Threading",
] }
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index bf9a66bf3f..61e0b1e611 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -2,6 +2,7 @@
//! errors.
use std::{
+ cell::LazyCell,
env, fmt,
ops::AddAssign,
panic::{AssertUnwindSafe, catch_unwind},
@@ -11,7 +12,7 @@ use std::{
use cfg::{CfgAtom, CfgDiff};
use hir::{
Adt, AssocItem, Crate, DefWithBody, FindPathConfig, GenericDef, HasCrate, HasSource,
- HirDisplay, ModuleDef, Name, Variant, VariantId, crate_lang_items,
+ HirDisplay, ModuleDef, Name, Variant, crate_lang_items,
db::{DefDatabase, ExpandDatabase, HirDatabase},
next_solver::{DbInterner, GenericArgs},
};
@@ -588,8 +589,8 @@ impl flags::AnalysisStats {
continue;
};
- fn trim(s: &str) -> String {
- s.chars().filter(|c| !c.is_whitespace()).collect()
+ fn drop_whitespace(s: &str) -> String {
+ s.chars().filter(|c| !parser::is_rust_whitespace(*c)).collect()
}
let todo = syntax::ast::make::ext::expr_todo().to_string();
@@ -609,7 +610,8 @@ impl flags::AnalysisStats {
display_target,
)
.unwrap();
- syntax_hit_found |= trim(&original_text) == trim(&generated);
+ syntax_hit_found |=
+ drop_whitespace(&original_text) == drop_whitespace(&generated);
// Validate if type-checks
let mut txt = file_txt.text(db).to_string();
@@ -663,7 +665,7 @@ impl flags::AnalysisStats {
let msg = move || {
format!(
"processing: {:<50}",
- trim(&original_text).chars().take(50).collect::<String>()
+ drop_whitespace(&original_text).chars().take(50).collect::<String>()
)
};
if verbosity.is_spammy() {
@@ -744,10 +746,8 @@ impl flags::AnalysisStats {
}
all += 1;
- let Ok(body_id) = body.try_into() else {
- continue;
- };
- let Err(e) = db.mir_body(body_id) else {
+ #[expect(deprecated)]
+ let Err(e) = body.run_mir_body(db) else {
continue;
};
if verbosity.is_spammy() {
@@ -778,7 +778,7 @@ impl flags::AnalysisStats {
vfs: &Vfs,
bodies: &[DefWithBody],
signatures: &[GenericDef],
- variants: &[Variant],
+ _variants: &[Variant],
verbosity: Verbosity,
) {
let mut bar = match verbosity {
@@ -799,23 +799,24 @@ impl flags::AnalysisStats {
InferenceResult::of(snap, body);
})
.count();
- let signatures = signatures
+ let _signatures = signatures
.iter()
.filter_map(|&signatures| signatures.try_into().ok())
.collect::<Vec<GenericDefId>>();
- signatures
- .par_iter()
- .map_with(db.clone(), |snap, &signatures| {
- InferenceResult::of(snap, signatures);
- })
- .count();
- let variants = variants.iter().copied().map(Into::into).collect::<Vec<VariantId>>();
- variants
- .par_iter()
- .map_with(db.clone(), |snap, &variants| {
- InferenceResult::of(snap, variants);
- })
- .count();
+ // FIXME: We don't have access to the necessary types here (nor we should have).
+ // signatures
+ // .par_iter()
+ // .map_with(db.clone(), |snap, &signatures| {
+ // InferenceResult::of(snap, signatures);
+ // })
+ // .count();
+ // let variants = variants.iter().copied().map(Into::into).collect::<Vec<VariantId>>();
+ // variants
+ // .par_iter()
+ // .map_with(db.clone(), |snap, &variants| {
+ // InferenceResult::of(snap, variants);
+ // })
+ // .count();
eprintln!("{:<20} {}", "Parallel Inference:", inference_sw.elapsed());
}
@@ -907,6 +908,18 @@ impl flags::AnalysisStats {
// region:expressions
let (previous_exprs, previous_unknown, previous_partially_unknown) =
(num_exprs, num_exprs_unknown, num_exprs_partially_unknown);
+ let type_mismatch_for_node = LazyCell::new(|| {
+ inference_result
+ .diagnostics()
+ .iter()
+ .filter_map(|diag| match diag {
+ hir_ty::InferenceDiagnostic::TypeMismatch { node, expected, found } => {
+ Some((*node, (expected.as_ref(), found.as_ref())))
+ }
+ _ => None,
+ })
+ .collect::<FxHashMap<_, _>>()
+ });
for (expr_id, _) in body.exprs() {
let ty = inference_result.expr_ty(expr_id);
num_exprs += 1;
@@ -963,9 +976,10 @@ impl flags::AnalysisStats {
ty.display(db, display_target)
);
}
- if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr_id) {
+ if inference_result.expr_has_type_mismatch(expr_id) {
num_expr_type_mismatches += 1;
if verbosity.is_verbose() {
+ let (expected, actual) = type_mismatch_for_node[&expr_id.into()];
if let Some((path, start, end)) = expr_syntax_range(db, vfs, sm(), expr_id)
{
bar.println(format!(
@@ -975,24 +989,25 @@ impl flags::AnalysisStats {
start.col,
end.line + 1,
end.col,
- mismatch.expected.as_ref().display(db, display_target),
- mismatch.actual.as_ref().display(db, display_target)
+ expected.display(db, display_target),
+ actual.display(db, display_target)
));
} else {
bar.println(format!(
"{}: Expected {}, got {}",
name.display(db, Edition::LATEST),
- mismatch.expected.as_ref().display(db, display_target),
- mismatch.actual.as_ref().display(db, display_target)
+ expected.display(db, display_target),
+ actual.display(db, display_target)
));
}
}
if self.output == Some(OutputFormat::Csv) {
+ let (expected, actual) = type_mismatch_for_node[&expr_id.into()];
println!(
r#"{},mismatch,"{}","{}""#,
location_csv_expr(db, vfs, sm(), expr_id),
- mismatch.expected.as_ref().display(db, display_target),
- mismatch.actual.as_ref().display(db, display_target)
+ expected.display(db, display_target),
+ actual.display(db, display_target)
);
}
}
@@ -1066,9 +1081,10 @@ impl flags::AnalysisStats {
ty.display(db, display_target)
);
}
- if let Some(mismatch) = inference_result.type_mismatch_for_pat(pat_id) {
+ if inference_result.pat_has_type_mismatch(pat_id) {
num_pat_type_mismatches += 1;
if verbosity.is_verbose() {
+ let (expected, actual) = type_mismatch_for_node[&pat_id.into()];
if let Some((path, start, end)) = pat_syntax_range(db, vfs, sm(), pat_id) {
bar.println(format!(
"{} {}:{}-{}:{}: Expected {}, got {}",
@@ -1077,24 +1093,25 @@ impl flags::AnalysisStats {
start.col,
end.line + 1,
end.col,
- mismatch.expected.as_ref().display(db, display_target),
- mismatch.actual.as_ref().display(db, display_target)
+ expected.display(db, display_target),
+ actual.display(db, display_target)
));
} else {
bar.println(format!(
"{}: Expected {}, got {}",
name.display(db, Edition::LATEST),
- mismatch.expected.as_ref().display(db, display_target),
- mismatch.actual.as_ref().display(db, display_target)
+ expected.display(db, display_target),
+ actual.display(db, display_target)
));
}
}
if self.output == Some(OutputFormat::Csv) {
+ let (expected, actual) = type_mismatch_for_node[&pat_id.into()];
println!(
r#"{},mismatch,"{}","{}""#,
location_csv_pat(db, vfs, sm(), pat_id),
- mismatch.expected.as_ref().display(db, display_target),
- mismatch.actual.as_ref().display(db, display_target)
+ expected.display(db, display_target),
+ actual.display(db, display_target)
);
}
}
@@ -1647,5 +1664,5 @@ impl fmt::Display for PrettyItemStats {
// fn syntax_len(node: SyntaxNode) -> usize {
// // Macro expanded code doesn't contain whitespace, so erase *all* whitespace
// // to make macro and non-macro code comparable.
-// node.to_string().replace(|it: char| it.is_ascii_whitespace(), "").len()
+// drop_whitespace(&node.to_string()).len()
// }
diff --git a/crates/rust-analyzer/src/cli/highlight.rs b/crates/rust-analyzer/src/cli/highlight.rs
index 84607b9fd5..5aba532f57 100644
--- a/crates/rust-analyzer/src/cli/highlight.rs
+++ b/crates/rust-analyzer/src/cli/highlight.rs
@@ -1,12 +1,15 @@
//! Read Rust code on stdin, print HTML highlighted version to stdout.
use ide::Analysis;
+use ide_db::base_db::AbsPathBuf;
+use triomphe::Arc;
use crate::cli::{flags, read_stdin};
impl flags::Highlight {
pub fn run(self) -> anyhow::Result<()> {
- let (analysis, file_id) = Analysis::from_single_file(read_stdin()?);
+ let cwd = AbsPathBuf::assert_utf8(std::env::current_dir()?);
+ let (analysis, file_id) = Analysis::from_single_file(read_stdin()?, Arc::new(cwd));
let html = analysis.highlight_as_html(file_id, self.rainbow).unwrap();
println!("{html}");
Ok(())
diff --git a/crates/rust-analyzer/src/cli/symbols.rs b/crates/rust-analyzer/src/cli/symbols.rs
index d7af56d3e1..3d7d712326 100644
--- a/crates/rust-analyzer/src/cli/symbols.rs
+++ b/crates/rust-analyzer/src/cli/symbols.rs
@@ -1,12 +1,15 @@
//! Read Rust code on stdin, print syntax tree on stdout.
use ide::{Analysis, FileStructureConfig};
+use ide_db::base_db::AbsPathBuf;
+use triomphe::Arc;
use crate::cli::{flags, read_stdin};
impl flags::Symbols {
pub fn run(self) -> anyhow::Result<()> {
let text = read_stdin()?;
- let (analysis, file_id) = Analysis::from_single_file(text);
+ let cwd = AbsPathBuf::assert_utf8(std::env::current_dir()?);
+ let (analysis, file_id) = Analysis::from_single_file(text, Arc::new(cwd));
let structure = analysis
// The default setting in config.rs (document_symbol_search_excludeLocals) is to exclude
// locals because it is unlikely that users want document search to return the names of
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 3a88a8fe84..f4c3f24ce6 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -627,6 +627,11 @@ config_data! {
/// Term search fuel in "units of work" for assists (Defaults to 1800).
assist_termSearch_fuel: usize = 1800,
+ /// Automatically add `::` when completing the module.
+ ///
+ /// Will not be completed in `use`.
+ completion_addColonsToModule: bool = true,
+
/// Automatically add a semicolon when completing unit-returning functions.
///
/// In `match` arms it completes a comma instead.
@@ -1901,6 +1906,7 @@ impl Config {
CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses),
CallableCompletionDef::None => None,
},
+ add_colons_to_module: *self.completion_addColonsToModule(source_root),
add_semicolon_to_unit: *self.completion_addSemicolonToUnit(source_root),
snippet_cap: SnippetCap::new(self.completion_snippet()),
insert_use: self.insert_use_config(source_root),
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 0c1c067ffa..ca6a2e70b0 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -1049,7 +1049,6 @@ pub(crate) fn handle_runnables(
all_targets = if all_targets { " --all-targets" } else { "" }
),
location: None,
- kind: lsp_ext::RunnableKind::Cargo,
args: lsp_ext::RunnableArgs::Cargo(lsp_ext::CargoRunnableArgs {
workspace_root: Some(spec.workspace_root.clone().into()),
cwd: cwd.into(),
@@ -1076,7 +1075,6 @@ pub(crate) fn handle_runnables(
res.push(lsp_ext::Runnable {
label: "cargo check --workspace".to_owned(),
location: None,
- kind: lsp_ext::RunnableKind::Cargo,
args: lsp_ext::RunnableArgs::Cargo(lsp_ext::CargoRunnableArgs {
workspace_root: None,
cwd: path.as_path().unwrap().to_path_buf().into(),
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index af449c473a..bd3574ae0a 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -162,36 +162,7 @@ fn integrated_completion_benchmark() {
{
let _span = profile::cpu_span();
let analysis = host.analysis();
- let config = CompletionConfig {
- enable_postfix_completions: true,
- enable_imports_on_the_fly: true,
- enable_self_on_the_fly: true,
- enable_private_editable: true,
- enable_term_search: true,
- term_search_fuel: 200,
- full_function_signatures: false,
- callable: Some(CallableSnippets::FillArguments),
- snippet_cap: SnippetCap::new(true),
- insert_use: InsertUseConfig {
- granularity: ImportGranularity::Crate,
- prefix_kind: hir::PrefixKind::ByCrate,
- enforce_granularity: true,
- group: true,
- skip_glob_imports: true,
- },
- prefer_no_std: false,
- prefer_prelude: true,
- prefer_absolute: false,
- snippets: Vec::new(),
- limit: None,
- add_semicolon_to_unit: true,
- fields_to_resolve: CompletionFieldsToResolve::empty(),
- exclude_flyimport: vec![],
- exclude_traits: &[],
- enable_auto_await: true,
- enable_auto_iter: true,
- ra_fixture: RaFixtureConfig::default(),
- };
+ let config = completion_config();
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
analysis.completions(&config, position, None).unwrap();
@@ -217,36 +188,7 @@ fn integrated_completion_benchmark() {
let _p = tracing::info_span!("unqualified path completion").entered();
let _span = profile::cpu_span();
let analysis = host.analysis();
- let config = CompletionConfig {
- enable_postfix_completions: true,
- enable_imports_on_the_fly: true,
- enable_self_on_the_fly: true,
- enable_private_editable: true,
- enable_term_search: true,
- term_search_fuel: 200,
- full_function_signatures: false,
- callable: Some(CallableSnippets::FillArguments),
- snippet_cap: SnippetCap::new(true),
- insert_use: InsertUseConfig {
- granularity: ImportGranularity::Crate,
- prefix_kind: hir::PrefixKind::ByCrate,
- enforce_granularity: true,
- group: true,
- skip_glob_imports: true,
- },
- prefer_no_std: false,
- prefer_prelude: true,
- prefer_absolute: false,
- snippets: Vec::new(),
- limit: None,
- add_semicolon_to_unit: true,
- fields_to_resolve: CompletionFieldsToResolve::empty(),
- exclude_flyimport: vec![],
- exclude_traits: &[],
- enable_auto_await: true,
- enable_auto_iter: true,
- ra_fixture: RaFixtureConfig::default(),
- };
+ let config = completion_config();
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
analysis.completions(&config, position, None).unwrap();
@@ -270,36 +212,7 @@ fn integrated_completion_benchmark() {
let _p = tracing::info_span!("dot completion").entered();
let _span = profile::cpu_span();
let analysis = host.analysis();
- let config = CompletionConfig {
- enable_postfix_completions: true,
- enable_imports_on_the_fly: true,
- enable_self_on_the_fly: true,
- enable_private_editable: true,
- enable_term_search: true,
- term_search_fuel: 200,
- full_function_signatures: false,
- callable: Some(CallableSnippets::FillArguments),
- snippet_cap: SnippetCap::new(true),
- insert_use: InsertUseConfig {
- granularity: ImportGranularity::Crate,
- prefix_kind: hir::PrefixKind::ByCrate,
- enforce_granularity: true,
- group: true,
- skip_glob_imports: true,
- },
- prefer_no_std: false,
- prefer_prelude: true,
- prefer_absolute: false,
- snippets: Vec::new(),
- limit: None,
- add_semicolon_to_unit: true,
- fields_to_resolve: CompletionFieldsToResolve::empty(),
- exclude_flyimport: vec![],
- exclude_traits: &[],
- enable_auto_await: true,
- enable_auto_iter: true,
- ra_fixture: RaFixtureConfig::default(),
- };
+ let config = completion_config();
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
analysis.completions(&config, position, None).unwrap();
@@ -400,3 +313,37 @@ fn patch(what: &mut String, from: &str, to: &str) -> usize {
*what = what.replacen(from, to, 1);
idx
}
+
+fn completion_config() -> CompletionConfig<'static> {
+ CompletionConfig {
+ enable_postfix_completions: true,
+ enable_imports_on_the_fly: true,
+ enable_self_on_the_fly: true,
+ enable_private_editable: true,
+ enable_term_search: true,
+ term_search_fuel: 200,
+ full_function_signatures: false,
+ callable: Some(CallableSnippets::FillArguments),
+ snippet_cap: SnippetCap::new(true),
+ insert_use: InsertUseConfig {
+ granularity: ImportGranularity::Crate,
+ prefix_kind: hir::PrefixKind::ByCrate,
+ enforce_granularity: true,
+ group: true,
+ skip_glob_imports: true,
+ },
+ prefer_no_std: false,
+ prefer_prelude: true,
+ prefer_absolute: false,
+ snippets: Vec::new(),
+ limit: None,
+ add_colons_to_module: true,
+ add_semicolon_to_unit: true,
+ fields_to_resolve: CompletionFieldsToResolve::empty(),
+ exclude_flyimport: vec![],
+ exclude_traits: &[],
+ enable_auto_await: true,
+ enable_auto_iter: true,
+ ra_fixture: RaFixtureConfig::default(),
+ }
+}
diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs
index 5d0d9209de..754d6e65fe 100644
--- a/crates/rust-analyzer/src/lsp/ext.rs
+++ b/crates/rust-analyzer/src/lsp/ext.rs
@@ -451,25 +451,17 @@ pub struct Runnable {
pub label: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<lsp_types::LocationLink>,
- pub kind: RunnableKind,
+ #[serde(flatten)]
pub args: RunnableArgs,
}
#[derive(Deserialize, Serialize, Debug, Clone)]
-#[serde(rename_all = "camelCase")]
-#[serde(untagged)]
+#[serde(tag = "kind", content = "args", rename_all = "lowercase")]
pub enum RunnableArgs {
Cargo(CargoRunnableArgs),
Shell(ShellRunnableArgs),
}
-#[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "lowercase")]
-pub enum RunnableKind {
- Cargo,
- Shell,
-}
-
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CargoRunnableArgs {
@@ -858,6 +850,7 @@ pub struct InlayHintResolveData {
#[derive(Debug, Serialize, Deserialize)]
pub struct CompletionImport {
pub full_import_path: String,
+ #[serde(default)]
pub as_underscore: bool,
}
@@ -880,3 +873,94 @@ impl Request for GetFailedObligations {
type Result = String;
const METHOD: &'static str = "rust-analyzer/getFailedObligations";
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use serde_json::json;
+
+ #[test]
+ fn cargo_runnable_round_trips() {
+ let runnable = Runnable {
+ label: "cargo test -p my-crate".to_owned(),
+ location: None,
+ args: RunnableArgs::Cargo(CargoRunnableArgs {
+ environment: [("RUSTC_TOOLCHAIN".to_owned(), "/toolchain".to_owned())]
+ .into_iter()
+ .collect(),
+ cwd: "/project".into(),
+ override_cargo: None,
+ workspace_root: Some("/project".into()),
+ cargo_args: vec![
+ "test".into(),
+ "--package".into(),
+ "my-crate".into(),
+ "--lib".into(),
+ ],
+ executable_args: vec!["my_test".into(), "--exact".into()],
+ }),
+ };
+ let expected = json!({
+ "label": "cargo test -p my-crate",
+ "kind": "cargo",
+ "args": {
+ "environment": {"RUSTC_TOOLCHAIN": "/toolchain"},
+ "cwd": "/project",
+ "overrideCargo": null,
+ "workspaceRoot": "/project",
+ "cargoArgs": ["test", "--package", "my-crate", "--lib"],
+ "executableArgs": ["my_test", "--exact"],
+ }
+ });
+
+ let serialized = serde_json::to_value(&runnable).expect("serialized runnable");
+ assert_eq!(serialized, expected);
+
+ let deserialized: Runnable =
+ serde_json::from_value(expected).expect("cargo runnable should deserialize");
+ let RunnableArgs::Cargo(cargo) = &deserialized.args else {
+ panic!("expected Cargo variant, got {:?}", deserialized.args);
+ };
+ assert_eq!(cargo.cargo_args, vec!["test", "--package", "my-crate", "--lib"]);
+ assert_eq!(cargo.executable_args, vec!["my_test", "--exact"]);
+ }
+
+ #[test]
+ fn shell_runnable_round_trips() {
+ let runnable = Runnable {
+ label: "nextest test_one".to_owned(),
+ location: None,
+ args: RunnableArgs::Shell(ShellRunnableArgs {
+ environment: [("RUSTC_TOOLCHAIN".to_owned(), "/toolchain".to_owned())]
+ .into_iter()
+ .collect(),
+ cwd: "/project".into(),
+ program: "cargo".into(),
+ args: vec!["nextest".into(), "run".into(), "--package".into(), "my-crate".into()],
+ }),
+ };
+ let expected = json!({
+ "label": "nextest test_one",
+ "kind": "shell",
+ "args": {
+ "environment": {"RUSTC_TOOLCHAIN": "/toolchain"},
+ "cwd": "/project",
+ "program": "cargo",
+ "args": ["nextest", "run", "--package", "my-crate"],
+ }
+ });
+
+ let serialized = serde_json::to_value(&runnable).expect("serialized runnable");
+ assert_eq!(serialized, expected);
+
+ // Every shell runnable is a structurally valid cargo runnable if the `kind` tag isn't
+ // used. This test ensures that the `kind` tag is used.
+ let deserialized: Runnable =
+ serde_json::from_value(expected).expect("shell runnable should deserialize");
+ let RunnableArgs::Shell(shell) = &deserialized.args else {
+ panic!("expected Shell variant, got {:?}", deserialized.args);
+ };
+ assert_eq!(shell.program, "cargo");
+ assert_eq!(shell.args, vec!["nextest", "run", "--package", "my-crate"]);
+ }
+}
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index d857f23b67..eff5477969 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -1608,7 +1608,6 @@ pub(crate) fn runnable(
Some((program, args)) => Some(lsp_ext::Runnable {
label,
location: Some(location),
- kind: lsp_ext::RunnableKind::Shell,
args: lsp_ext::RunnableArgs::Shell(lsp_ext::ShellRunnableArgs {
environment,
cwd: cwd.into(),
@@ -1621,7 +1620,6 @@ pub(crate) fn runnable(
None => Some(lsp_ext::Runnable {
label,
location: Some(location),
- kind: lsp_ext::RunnableKind::Cargo,
args: lsp_ext::RunnableArgs::Cargo(lsp_ext::CargoRunnableArgs {
workspace_root: Some(workspace_root.into()),
override_cargo: config.override_cargo,
@@ -1648,7 +1646,6 @@ pub(crate) fn runnable(
Ok(Some(lsp_ext::Runnable {
label,
location: Some(location),
- kind: lsp_ext::RunnableKind::Shell,
args: lsp_ext::RunnableArgs::Shell(runnable_args),
}))
}
@@ -1668,7 +1665,6 @@ pub(crate) fn runnable(
Ok(Some(lsp_ext::Runnable {
label,
location: Some(location),
- kind: lsp_ext::RunnableKind::Cargo,
args: lsp_ext::RunnableArgs::Cargo(lsp_ext::CargoRunnableArgs {
workspace_root: None,
override_cargo: config.override_cargo,
@@ -2028,6 +2024,7 @@ pub(crate) fn rename_error(err: RenameError) -> LspError {
mod tests {
use expect_test::{Expect, expect};
use ide::{Analysis, FilePosition};
+ use ide_db::base_db::AbsPathBuf;
use ide_db::source_change::Snippet;
use test_utils::extract_offset;
use triomphe::Arc;
@@ -2048,7 +2045,10 @@ fn main() {
}
}"#;
- let (analysis, file_id) = Analysis::from_single_file(text.to_owned());
+ let (analysis, file_id) = Analysis::from_single_file(
+ text.to_owned(),
+ Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
+ );
let folds = analysis.folding_ranges(file_id, true).unwrap();
assert_eq!(folds.len(), 5);
@@ -2084,7 +2084,10 @@ fn bar(_: usize) {}
"#;
let (offset, text) = extract_offset(text);
- let (analysis, file_id) = Analysis::from_single_file(text);
+ let (analysis, file_id) = Analysis::from_single_file(
+ text,
+ Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap())),
+ );
let help = signature_help(
analysis.signature_help(FilePosition { file_id, offset }).unwrap().unwrap(),
CallInfoConfig { params_only: false, docs: true },
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index f5b3658ea9..a9ce6f728b 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -660,8 +660,20 @@ impl GlobalState {
let subscriptions = subscriptions.clone();
// Do not fetch semantic diagnostics (and populate query results) if we haven't even
// loaded the initial workspace yet.
- let fetch_semantic =
- self.vfs_done && self.fetch_workspaces_queue.last_op_result().is_some();
+ //
+ // Only fetch semantic diagnostics when
+ // - we have fully populated the VFS
+ // - have a workspace
+ // - have finished fetching the build data once
+ // - and have finished loading the proc-macros once
+ let fetch_semantic = self.vfs_done
+ && self.fetch_workspaces_queue.last_op_result().is_some()
+ && (!self.config.run_build_scripts(None)
+ || (self.fetch_build_data_queue.last_op_result().is_none()
+ && !self.fetch_build_data_queue.op_in_progress()))
+ && (!self.config.expand_proc_macros()
+ || (self.fetch_proc_macros_queue.last_op_result().is_none()
+ && !self.fetch_proc_macros_queue.op_in_progress()));
move |sender| {
// We aren't observing the semantics token cache here
let snapshot = AssertUnwindSafe(&snapshot);
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 1832275eb3..74fd0e6533 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -440,7 +440,7 @@ impl GlobalState {
let expansion_res = match client {
Some(Ok(client)) => match res {
Ok((crate_name, path)) => {
- progress(format!("loading proc-macros: {path}"));
+ progress(path.to_string());
let ignored_proc_macros = ignored_proc_macros
.iter()
.find_map(|(name, macros)| {
diff --git a/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/crates/rust-analyzer/tests/slow-tests/flycheck.rs
index e5d4d7c88e..386edb82f4 100644
--- a/crates/rust-analyzer/tests/slow-tests/flycheck.rs
+++ b/crates/rust-analyzer/tests/slow-tests/flycheck.rs
@@ -76,6 +76,7 @@ fn main() {
}
#[test]
+#[ignore = "this test tends to stuck, FIXME: investigate that"]
fn test_flycheck_diagnostic_with_override_command() {
if skip_slow_tests() {
return;
diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs
index 0a81cef52e..f475de93e0 100644
--- a/crates/span/src/hygiene.rs
+++ b/crates/span/src/hygiene.rs
@@ -156,6 +156,8 @@ const _: () = {
impl zalsa_::SalsaStructInDb for SyntaxContext {
type MemoIngredientMap = salsa::plumbing::MemoIngredientSingletonIndex;
+ const LEAF_TYPE_IDS: &[salsa::plumbing::ConstTypeId] =
+ &[salsa::plumbing::ConstTypeId::of::<SyntaxContext>()];
fn lookup_ingredient_index(aux: &zalsa_::Zalsa) -> salsa::plumbing::IngredientIndices {
aux.lookup_jar_by_type::<zalsa_struct_::JarImpl<SyntaxContext>>().into()
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml
index 2c19f00f08..a9d19a1347 100644
--- a/crates/stdx/Cargo.toml
+++ b/crates/stdx/Cargo.toml
@@ -26,7 +26,7 @@ libc.workspace = true
[target.'cfg(windows)'.dependencies]
miow = "0.6.0"
-windows-sys = { version = "0.60", features = ["Win32_Foundation"] }
+windows-sys = { version = "0.61", features = ["Win32_Foundation"] }
[features]
# Uncomment to enable for the whole crate graph
diff --git a/crates/syntax-bridge/Cargo.toml b/crates/syntax-bridge/Cargo.toml
index b0fd40ff59..c928f23e02 100644
--- a/crates/syntax-bridge/Cargo.toml
+++ b/crates/syntax-bridge/Cargo.toml
@@ -25,6 +25,7 @@ span = { path = "../span", version = "0.0.0", default-features = false}
intern.workspace = true
[dev-dependencies]
+expect-test = "1.5.1"
test-utils.workspace = true
[features]
diff --git a/crates/syntax-bridge/src/prettify_macro_expansion.rs b/crates/syntax-bridge/src/prettify_macro_expansion.rs
index 2f932e0458..648119ed70 100644
--- a/crates/syntax-bridge/src/prettify_macro_expansion.rs
+++ b/crates/syntax-bridge/src/prettify_macro_expansion.rs
@@ -3,8 +3,7 @@ use syntax::{
NodeOrToken,
SyntaxKind::{self, *},
SyntaxNode, SyntaxToken, T, WalkEvent,
- ast::make,
- ted::{self, Position},
+ syntax_editor::{Position, SyntaxEditor},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -29,7 +28,7 @@ pub fn prettify_macro_expansion(
let mut last: Option<SyntaxKind> = None;
let mut mods = Vec::new();
let mut dollar_crate_replacements = Vec::new();
- let syn = syn.clone_subtree().clone_for_update();
+ let (editor, syn) = SyntaxEditor::new(syn);
let before = Position::before;
let after = Position::after;
@@ -45,17 +44,21 @@ pub fn prettify_macro_expansion(
for event in syn.preorder_with_tokens() {
let token = match event {
WalkEvent::Enter(NodeOrToken::Token(token)) => token,
- WalkEvent::Leave(NodeOrToken::Node(node))
- if matches!(
- node.kind(),
- ATTR | MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL | MACRO_RULES
- ) =>
- {
- if indent > 0 {
+ WalkEvent::Leave(NodeOrToken::Node(node)) => {
+ let is_last_child =
+ node.parent().is_some_and(|parent| parent.last_child().as_ref() == Some(&node));
+ let is_always_newline = matches!(node.kind(), ATTR);
+ let is_non_last_newline = match node.kind() {
+ MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL | MACRO_RULES | EXTERN_BLOCK
+ | EXTERN_CRATE | MODULE => true,
+ EXPR_STMT if Some(R_CURLY) == node.last_token().map(|it| it.kind()) => true,
+ _ => false,
+ };
+ if (!is_last_child && is_non_last_newline) || is_always_newline {
mods.push((Position::after(node.clone()), PrettifyWsKind::Indent(indent)));
- }
- if node.parent().is_some() {
- mods.push((Position::after(node), PrettifyWsKind::Newline));
+ if node.parent().is_some() {
+ mods.push((Position::after(node), PrettifyWsKind::Newline));
+ }
}
continue;
}
@@ -77,55 +80,69 @@ pub fn prettify_macro_expansion(
match tok.kind() {
k if is_text(k)
- && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#]), false) =>
+ && is_next(|it| !it.is_punct() || matches!(it, T![_] | T![#] | L_CURLY), false) =>
{
mods.push(do_ws(after, tok));
}
L_CURLY if is_next(|it| it != R_CURLY, true) => {
indent += 1;
- if is_last(is_text, false) {
- mods.push(do_ws(before, tok));
- }
-
mods.push(do_indent(after, tok, indent));
mods.push(do_nl(after, tok));
}
R_CURLY if is_last(|it| it != L_CURLY, true) => {
indent = indent.saturating_sub(1);
- if indent > 0 {
- mods.push(do_indent(before, tok, indent));
- }
+ mods.push(do_indent(before, tok, indent));
mods.push(do_nl(before, tok));
}
- R_CURLY => {
- if indent > 0 {
- mods.push(do_indent(after, tok, indent));
- }
- mods.push(do_nl(after, tok));
+ R_CURLY if is_next(|it| it == T![else], false) => {
+ mods.push(do_indent(before, tok, indent));
+ mods.push(do_nl(before, tok));
}
LIFETIME_IDENT if is_next(is_text, true) => {
mods.push(do_ws(after, tok));
}
- AS_KW | DYN_KW | IMPL_KW | CONST_KW | MUT_KW => {
+ AS_KW | DYN_KW | IMPL_KW | CONST_KW | MUT_KW | LET_KW | MATCH_KW => {
mods.push(do_ws(after, tok));
}
T![;] if is_next(|it| it != R_CURLY, true) => {
- if indent > 0 {
- mods.push(do_indent(after, tok, indent));
+ mods.push(do_indent(after, tok, indent));
+ if tok.text_range().end() != syn.text_range().end() {
+ mods.push(do_nl(after, tok));
}
- mods.push(do_nl(after, tok));
}
- T![=] if is_next(|it| it == T![>], false) => {
+ T![=] if let Some((last, next)) = last.zip(tok.next_token()) => {
// FIXME: this branch is for `=>` in macro_rules!, which is currently parsed as
// two separate symbols.
- mods.push(do_ws(before, tok));
- mods.push(do_ws(after, &tok.next_token().unwrap()));
+ match (last, next.kind()) {
+ (T![=], _) | (_, T![=]) => (),
+ // catch ..= += etc
+ #[rustfmt::skip]
+ (
+ T![!] | T![%] | T![&] | T![*] | T![+] | T![-] |
+ T![/] | T![<] | T![>] | T![^] | T![|] | T![.],
+ _,
+ ) => (),
+ (_, T![>]) => {
+ mods.push(do_ws(before, tok));
+ mods.push(do_ws(after, &next));
+ }
+ _ => {
+ mods.push(do_ws(before, tok));
+ mods.push(do_ws(after, tok));
+ }
+ }
}
- T![->] | T![=] | T![=>] => {
+ T![->] | T![=>] => {
mods.push(do_ws(before, tok));
mods.push(do_ws(after, tok));
}
+ T![:] if is_next(|it| it != T![:], false) && is_last(|it| it != T![:], false) => {
+ // XXX: Why input included WHITESPACE?
+ if is_next(|it| it != SyntaxKind::WHITESPACE, false) {
+ mods.push(do_ws(after, tok));
+ }
+ }
T![!] if is_last(|it| it == MACRO_RULES_KW, false) && is_next(is_text, false) => {
mods.push(do_ws(after, tok));
}
@@ -137,27 +154,319 @@ pub fn prettify_macro_expansion(
inspect_mods(&mods);
for (pos, insert) in mods {
- ted::insert_raw(
+ editor.insert(
pos,
match insert {
- PrettifyWsKind::Space => make::tokens::single_space(),
- PrettifyWsKind::Indent(indent) => make::tokens::whitespace(&" ".repeat(4 * indent)),
- PrettifyWsKind::Newline => make::tokens::single_newline(),
+ PrettifyWsKind::Space => editor.make().whitespace(" "),
+ PrettifyWsKind::Indent(0) => continue,
+ PrettifyWsKind::Indent(indent) => editor.make().whitespace(&" ".repeat(4 * indent)),
+ PrettifyWsKind::Newline => editor.make().whitespace("\n"),
},
);
}
for (old, new) in dollar_crate_replacements {
- ted::replace(old, new);
+ editor.replace(old, new);
}
if let Some(it) = syn.last_token().filter(|it| it.kind() == SyntaxKind::WHITESPACE) {
- ted::remove(it);
+ editor.delete(it);
}
- syn
+ editor.finish().new_root().clone()
}
fn is_text(k: SyntaxKind) -> bool {
// Consider all keywords in all editions.
k.is_any_identifier() || k.is_literal() || k == UNDERSCORE
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use expect_test::{Expect, expect};
+
+ #[expect(deprecated)]
+ fn check_pretty(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
+ let ra_fixture = stdx::trim_indent(ra_fixture);
+ let source_file = syntax::ast::SourceFile::parse(&ra_fixture, span::Edition::CURRENT);
+ let syn = remove_whitespaces(&source_file.syntax_node());
+
+ let pretty = prettify_macro_expansion(syn, &mut |_| None, |_| ());
+ let mut pretty = pretty.to_string();
+ if pretty.contains('\n') {
+ pretty.push('\n');
+ }
+ expect.assert_eq(&pretty);
+
+ fn remove_whitespaces(node: &SyntaxNode) -> SyntaxNode {
+ let (editor, node) = SyntaxEditor::new(node.clone());
+ node.preorder_with_tokens().for_each(|it| match it {
+ WalkEvent::Enter(NodeOrToken::Token(tok)) if tok.kind().is_trivia() => {
+ editor.delete(tok);
+ }
+ _ => (),
+ });
+ editor.finish().new_root().clone()
+ }
+ }
+
+ #[test]
+ fn test_in_macro() {
+ check_pretty(
+ r#"
+ const X: i32 = x::y::z;
+ macro_rules! foo {
+ () => {
+ $crate::foo::bar!();
+ (1..2, 1..=2);
+ (a==b, a!=b, a<=b, a>=b, x+=2, x<<=2);
+ };
+ }
+ "#,
+ expect![[r#"
+ const X: i32 = x::y::z;
+ macro_rules! foo {
+ () => {
+ $crate::foo::bar!();
+ (1..2,1..=2);
+ (a==b,a!=b,a<=b,a>=b,x+=2,x<<=2);
+ };
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_curly_indent() {
+ check_pretty(
+ r#"
+ const _: () = {
+ {
+ 2;
+ 3
+ }
+ };
+ "#,
+ expect![[r#"
+ const _: () = {
+ {
+ 2;
+ 3
+ }
+ };
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_pats() {
+ check_pretty(
+ r#"
+ const _: () = {
+ let x = 2;
+ let mut y = 3;
+ let ref mut z @ 0..5 = 4;
+ let ref mut t @ 0..=5 = 4;
+ let (x, ref y) = (5, 6);
+ let (Foo { x, y }, Bar(z, t));
+ let (&mut x, (y | y));
+ match () {}
+ };
+ "#,
+ expect![[r#"
+ const _: () = {
+ let x = 2;
+ let mut y = 3;
+ let ref mut [email protected] = 4;
+ let ref mut t@0..=5 = 4;
+ let (x,ref y) = (5,6);
+ let (Foo {
+ x,y
+ },Bar(z,t));
+ let (&mut x,(y|y));
+ match (){}
+ };
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_attrs() {
+ check_pretty(
+ r#"
+ #[attr1]
+ #[attr2]
+ const _: () = {};
+ #[attr1]
+ const _: () = {
+ #[attr2]
+ {}
+ };
+ "#,
+ expect![[r#"
+ #[attr1]
+ #[attr2]
+ const _: () = {};
+ #[attr1]
+ const _: () = {
+ #[attr2]
+ {}
+ };
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_items() {
+ check_pretty(
+ r#"
+ fn foo() {}
+ struct Foo {}
+ struct Foo;
+ enum Foo {}
+ impl Foo {}
+ const _: () = {};
+ static S: () = {};
+ extern {}
+ mod x {}
+ mod x;
+ type X = 2;
+ use a;
+ use b::{c, d};
+ macro_rules! foo { () => {}; }
+ "#,
+ expect![[r#"
+ fn foo(){}
+ struct Foo {}
+ struct Foo;
+
+ enum Foo {}
+ impl Foo {}
+ const _: () = {};
+ static S: () = {};
+ extern {}
+ mod x {}
+ mod x;
+
+ type X = 2;
+ use a;
+ use b::{
+ c,d
+ };
+ macro_rules! foo {
+ () => {};
+ }
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_exprs() {
+ check_pretty(
+ r#"
+ const _: () = {
+ let _ = 1+2;
+ let _ = !true && false;
+ let _ = foo() + !bar() + dbg!(2) + *x;
+ let _ = async move || {};
+ let _ = async move {};
+ let _ = x.await;
+ let _ = (1..2, 1..=2);
+ 'lab: for _ in 0..5 {
+ loop { }
+ break 'lab expr;
+ if let pat = expr {
+ foo()
+ } else if true {
+ bar()
+ } else {}
+ if true {} else if true {} else {}
+ fun()
+ }
+ };
+ "#,
+ expect![[r#"
+ const _: () = {
+ let _ = 1+2;
+ let _ = !true&&false;
+ let _ = foo()+!bar()+dbg!(2)+*x;
+ let _ = async move||{};
+ let _ = async move {};
+ let _ = x.await;
+ let _ = (1..2,1..=2);
+ 'lab: for _ in 0..5 {
+ loop {}
+ break 'lab expr;
+ if let pat = expr {
+ foo()
+ }else if true {
+ bar()
+ }else {}
+ if true {
+ }else if true {
+ }else {}
+ fun()
+ }
+ };
+ "#]],
+ );
+ }
+
+ #[test]
+ fn test_match_arm() {
+ check_pretty(
+ r#"
+ const _: () = {
+ match 2 {
+ tmp => foo!(),
+ };
+ };
+ "#,
+ expect![[r#"
+ const _: () = {
+ match 2 {
+ tmp => foo!(),
+ };
+ };
+ "#]],
+ );
+
+ check_pretty(
+ r#"
+ const _: () = {
+ match 2 {
+ tmp => {}
+ };
+ };
+ "#,
+ expect![[r#"
+ const _: () = {
+ match 2 {
+ tmp => {}
+ };
+ };
+ "#]],
+ );
+
+ check_pretty(
+ r#"
+ const _: () = {
+ match 2 {
+ 1 => {}
+ 2 => foo(),
+ _ => {},
+ };
+ };
+ "#,
+ expect![[r#"
+ const _: () = {
+ match 2 {
+ 1 => {}
+ 2 => foo(),
+ _ => {},
+ };
+ };
+ "#]],
+ );
+ }
+}
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 768cf2013d..6bcf8ba743 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -748,6 +748,10 @@ Pat =
| TuplePat
| TupleStructPat
| ConstBlockPat
+| DerefPat
+
+DerefPat =
+ 'builtin' '#' 'deref' '(' Pat ')'
LiteralPat =
'-'? Literal
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index b20aa90d06..2e3a4016ee 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -2,15 +2,18 @@
//! immutable, all function here return a fresh copy of the tree, instead of
//! doing an in-place modification.
use parser::T;
-use std::{fmt, iter, ops};
+use std::{
+ fmt,
+ iter::{self, once},
+ ops,
+};
use crate::{
AstToken, NodeOrToken, SyntaxElement,
- SyntaxKind::WHITESPACE,
+ SyntaxKind::{ATTR, COMMENT, WHITESPACE},
SyntaxNode, SyntaxToken,
ast::{self, AstNode, HasName, make},
syntax_editor::{Position, SyntaxEditor, SyntaxMappingBuilder},
- ted,
};
use super::syntax_factory::SyntaxFactory;
@@ -84,29 +87,6 @@ impl IndentLevel {
IndentLevel(0)
}
- /// XXX: this intentionally doesn't change the indent of the very first token.
- /// For example, in something like:
- /// ```
- /// fn foo() -> i32 {
- /// 92
- /// }
- /// ```
- /// if you indent the block, the `{` token would stay put.
- pub(super) fn increase_indent(self, node: &SyntaxNode) {
- let tokens = node.preorder_with_tokens().filter_map(|event| match event {
- rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
- _ => None,
- });
- for token in tokens {
- if let Some(ws) = ast::Whitespace::cast(token)
- && ws.text().contains('\n')
- {
- let new_ws = make::tokens::whitespace(&format!("{}{self}", ws.syntax()));
- ted::replace(ws.syntax(), &new_ws);
- }
- }
- }
-
pub(super) fn clone_increase_indent(self, node: &SyntaxNode) -> SyntaxNode {
let (editor, node) = SyntaxEditor::new(node.clone());
let tokens = node
@@ -124,23 +104,6 @@ impl IndentLevel {
editor.finish().new_root().clone()
}
- pub(super) fn decrease_indent(self, node: &SyntaxNode) {
- let tokens = node.preorder_with_tokens().filter_map(|event| match event {
- rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it),
- _ => None,
- });
- for token in tokens {
- if let Some(ws) = ast::Whitespace::cast(token)
- && ws.text().contains('\n')
- {
- let new_ws = make::tokens::whitespace(
- &ws.syntax().text().replace(&format!("\n{self}"), "\n"),
- );
- ted::replace(ws.syntax(), &new_ws);
- }
- }
- }
-
pub(super) fn clone_decrease_indent(self, node: &SyntaxNode) -> SyntaxNode {
let (editor, node) = SyntaxEditor::new(node.clone());
let tokens = node
@@ -197,6 +160,28 @@ pub trait AstNodeEdit: AstNode + Clone + Sized {
impl<N: AstNode + Clone> AstNodeEdit for N {}
+pub trait AttrsOwnerEdit: ast::HasAttrs {
+ fn remove_attrs_and_docs(&self, editor: &SyntaxEditor) {
+ let mut remove_next_ws = false;
+ for child in self.syntax().children_with_tokens() {
+ match child.kind() {
+ ATTR | COMMENT => {
+ remove_next_ws = true;
+ editor.delete(child);
+ continue;
+ }
+ WHITESPACE if remove_next_ws => {
+ editor.delete(child);
+ }
+ _ => (),
+ }
+ remove_next_ws = false;
+ }
+ }
+}
+
+impl<T: ast::HasAttrs> AttrsOwnerEdit for T {}
+
impl ast::IdentPat {
pub fn set_pat(&self, pat: Option<ast::Pat>, editor: &SyntaxEditor) -> ast::IdentPat {
let make = editor.make();
@@ -252,6 +237,32 @@ impl ast::IdentPat {
}
}
+impl ast::UseTree {
+ pub fn wrap_in_tree_list_with_editor(&self) -> Option<ast::UseTree> {
+ if self.use_tree_list().is_some()
+ && self.path().is_none()
+ && self.star_token().is_none()
+ && self.rename().is_none()
+ {
+ return None;
+ }
+
+ let (editor, use_tree) = SyntaxEditor::with_ast_node(self);
+ let make = editor.make();
+ let first_child = use_tree.syntax().first_child_or_token()?;
+ let last_child = use_tree.syntax().last_child_or_token()?;
+ let use_tree_list = make.use_tree_list(once(self.clone()));
+ editor.replace_all(first_child..=last_child, vec![use_tree_list.syntax().clone().into()]);
+
+ let edit = editor.finish();
+ ast::UseTree::cast(edit.new_root().clone())
+ }
+}
+
+pub fn indent(node: &SyntaxNode, level: IndentLevel) -> SyntaxNode {
+ level.clone_increase_indent(node)
+}
+
#[test]
fn test_increase_indent() {
let arm_list = {
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index 46ea4daba8..4a8c9d450c 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -2,120 +2,31 @@
use std::iter::{empty, once, successors};
-use parser::{SyntaxKind, T};
+use parser::T;
use crate::{
- AstNode, AstToken, Direction, SyntaxElement,
- SyntaxKind::{ATTR, COMMENT, WHITESPACE},
- SyntaxNode, SyntaxToken,
+ AstNode, AstToken, Direction,
algo::{self, neighbor},
- ast::{self, edit::IndentLevel, make},
+ ast::{self, make, syntax_factory::SyntaxFactory},
+ syntax_editor::SyntaxEditor,
ted,
};
-use super::{GenericParam, HasName};
-
-pub trait AttrsOwnerEdit: ast::HasAttrs {
- fn remove_attrs_and_docs(&self) {
- remove_attrs_and_docs(self.syntax());
-
- fn remove_attrs_and_docs(node: &SyntaxNode) {
- let mut remove_next_ws = false;
- for child in node.children_with_tokens() {
- match child.kind() {
- ATTR | COMMENT => {
- remove_next_ws = true;
- child.detach();
- continue;
- }
- WHITESPACE if remove_next_ws => {
- child.detach();
- }
- _ => (),
- }
- remove_next_ws = false;
- }
- }
- }
-}
-
-impl<T: ast::HasAttrs> AttrsOwnerEdit for T {}
+use super::HasName;
impl ast::GenericParamList {
- pub fn add_generic_param(&self, generic_param: ast::GenericParam) {
- match self.generic_params().last() {
- Some(last_param) => {
- let position = ted::Position::after(last_param.syntax());
- let elements = vec![
- make::token(T![,]).into(),
- make::tokens::single_space().into(),
- generic_param.syntax().clone().into(),
- ];
- ted::insert_all(position, elements);
- }
- None => {
- let after_l_angle = ted::Position::after(self.l_angle_token().unwrap());
- ted::insert(after_l_angle, generic_param.syntax());
- }
- }
- }
-
- /// 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());
- }
- }
-
- /// Find the params corresponded to generic arg
- pub fn find_generic_arg(&self, generic_arg: &ast::GenericArg) -> Option<GenericParam> {
- self.generic_params().find_map(move |param| match (&param, &generic_arg) {
- (ast::GenericParam::LifetimeParam(a), ast::GenericArg::LifetimeArg(b)) => {
- (a.lifetime()?.lifetime_ident_token()?.text()
- == b.lifetime()?.lifetime_ident_token()?.text())
- .then_some(param)
- }
- (ast::GenericParam::TypeParam(a), ast::GenericArg::TypeArg(b)) => {
- debug_assert_eq!(b.syntax().first_token(), b.syntax().last_token());
- (a.name()?.text() == b.syntax().first_token()?.text()).then_some(param)
- }
- (ast::GenericParam::ConstParam(a), ast::GenericArg::TypeArg(b)) => {
- debug_assert_eq!(b.syntax().first_token(), b.syntax().last_token());
- (a.name()?.text() == b.syntax().first_token()?.text()).then_some(param)
- }
- _ => None,
- })
- }
-
- /// Removes the corresponding generic arg
- pub fn remove_generic_arg(&self, generic_arg: &ast::GenericArg) {
- let param_to_remove = self.find_generic_arg(generic_arg);
-
- if let Some(param) = &param_to_remove {
- self.remove_generic_param(param.clone());
- }
- }
-
/// Constructs a matching [`ast::GenericArgList`]
- pub fn to_generic_args(&self) -> ast::GenericArgList {
+ pub fn to_generic_args(&self, make: &SyntaxFactory) -> ast::GenericArgList {
let args = self.generic_params().filter_map(|param| match param {
ast::GenericParam::LifetimeParam(it) => {
- Some(ast::GenericArg::LifetimeArg(make::lifetime_arg(it.lifetime()?)))
+ Some(ast::GenericArg::LifetimeArg(make.lifetime_arg(it.lifetime()?)))
}
ast::GenericParam::TypeParam(it) => {
- Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
+ Some(ast::GenericArg::TypeArg(make.type_arg(make.ty_name(it.name()?))))
}
ast::GenericParam::ConstParam(it) => {
// Name-only const params get parsed as `TypeArg`s
- Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
+ Some(ast::GenericArg::TypeArg(make.type_arg(make.ty_name(it.name()?))))
}
});
@@ -123,44 +34,10 @@ impl ast::GenericParamList {
}
}
-impl ast::WhereClause {
- pub fn add_predicate(&self, predicate: ast::WherePred) {
- if let Some(pred) = self.predicates().last()
- && !pred.syntax().siblings_with_tokens(Direction::Next).any(|it| it.kind() == T![,])
- {
- ted::append_child_raw(self.syntax(), make::token(T![,]));
- }
- ted::append_child(self.syntax(), predicate.syntax());
- }
-
- pub fn remove_predicate(&self, predicate: ast::WherePred) {
- if let Some(previous) = predicate.syntax().prev_sibling() {
- if let Some(next_token) = previous.next_sibling_or_token() {
- ted::remove_all(next_token..=predicate.syntax().clone().into());
- }
- } else if let Some(next) = predicate.syntax().next_sibling() {
- if let Some(next_token) = next.prev_sibling_or_token() {
- ted::remove_all(predicate.syntax().clone().into()..=next_token);
- }
- } else {
- ted::remove(predicate.syntax());
- }
- }
-}
-
pub trait Removable: AstNode {
fn remove(&self);
}
-impl Removable for ast::TypeBoundList {
- fn remove(&self) {
- match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) {
- Some(colon) => ted::remove_all(colon..=self.syntax().clone().into()),
- None => ted::remove(self.syntax()),
- }
- }
-}
-
impl Removable for ast::UseTree {
fn remove(&self) {
for dir in [Direction::Next, Direction::Prev] {
@@ -179,24 +56,41 @@ impl Removable for ast::UseTree {
}
impl ast::UseTree {
+ /// Editor variant of UseTree remove
+ fn remove_with_editor(&self, editor: &SyntaxEditor) {
+ for dir in [Direction::Next, Direction::Prev] {
+ if let Some(next_use_tree) = neighbor(self, dir) {
+ let separators = self
+ .syntax()
+ .siblings_with_tokens(dir)
+ .skip(1)
+ .take_while(|it| it.as_node() != Some(next_use_tree.syntax()));
+ for separator in separators {
+ editor.delete(separator);
+ }
+ break;
+ }
+ }
+ editor.delete(self.syntax());
+ }
+
/// Deletes the usetree node represented by the input. Recursively removes parents, including use nodes that become empty.
- pub fn remove_recursive(self) {
+ pub fn remove_recursive(self, editor: &SyntaxEditor) {
let parent = self.syntax().parent();
- self.remove();
-
if let Some(u) = parent.clone().and_then(ast::Use::cast) {
- if u.use_tree().is_none() {
- u.remove();
- }
+ u.remove(editor);
} else if let Some(u) = parent.and_then(ast::UseTreeList::cast) {
- if u.use_trees().next().is_none() {
- let parent = u.syntax().parent().and_then(ast::UseTree::cast);
- if let Some(u) = parent {
- u.remove_recursive();
- }
+ if u.use_trees().nth(1).is_none()
+ || u.use_trees().all(|use_tree| {
+ use_tree.syntax() == self.syntax() || editor.deleted(use_tree.syntax())
+ })
+ {
+ u.parent_use_tree().remove_recursive(editor);
+ return;
}
- u.remove_unnecessary_braces();
+ self.remove_with_editor(editor);
+ u.remove_unnecessary_braces(editor);
}
}
@@ -270,6 +164,35 @@ impl ast::UseTree {
}
}
+ /// Editor variant of `split_prefix`
+ pub fn split_prefix_with_editor(&self, editor: &SyntaxEditor, prefix: &ast::Path) {
+ debug_assert_eq!(self.path(), Some(prefix.top_path()));
+
+ let make = editor.make();
+ let path = self.path().unwrap();
+ let suffix = if path == *prefix && self.use_tree_list().is_none() {
+ if self.star_token().is_some() {
+ make.use_tree_glob()
+ } else {
+ let self_path = make.path_unqualified(make.path_segment_self());
+ make.use_tree(self_path, None, None, false)
+ }
+ } else {
+ let suffix_segments = path.segments().skip(prefix.segments().count());
+ let suffix_path = make.path_from_segments(suffix_segments, false);
+ make.use_tree(
+ suffix_path,
+ self.use_tree_list(),
+ self.rename(),
+ self.star_token().is_some(),
+ )
+ };
+ let use_tree_list = make.use_tree_list(once(suffix));
+ let new_use_tree = make.use_tree(prefix.clone(), Some(use_tree_list), None, false);
+
+ editor.replace(self.syntax(), new_use_tree.syntax());
+ }
+
/// Wraps the use tree in use tree list with no top level path (if it isn't already).
///
/// # Examples
@@ -318,8 +241,9 @@ impl ast::UseTreeList {
}
}
-impl Removable for ast::Use {
- fn remove(&self) {
+impl ast::Use {
+ fn remove(&self, editor: &SyntaxEditor) {
+ let make = editor.make();
let next_ws = self
.syntax()
.next_sibling_or_token()
@@ -328,10 +252,17 @@ impl Removable for ast::Use {
if let Some(next_ws) = next_ws {
let ws_text = next_ws.syntax().text();
if let Some(rest) = ws_text.strip_prefix('\n') {
- if rest.is_empty() {
- ted::remove(next_ws.syntax());
+ let next_use_removed = next_ws
+ .syntax()
+ .next_sibling_or_token()
+ .and_then(|it| it.into_node())
+ .and_then(ast::Use::cast)
+ .and_then(|use_| use_.use_tree())
+ .is_some_and(|use_tree| editor.deleted(use_tree.syntax()));
+ if rest.is_empty() || next_use_removed {
+ editor.delete(next_ws.syntax());
} else {
- ted::replace(next_ws.syntax(), make::tokens::whitespace(rest));
+ editor.replace(next_ws.syntax(), make.whitespace(rest));
}
}
}
@@ -345,13 +276,13 @@ impl Removable for ast::Use {
let prev_newline = ws_text.rfind('\n').map(|x| x + 1).unwrap_or(0);
let rest = &ws_text[0..prev_newline];
if rest.is_empty() {
- ted::remove(prev_ws.syntax());
+ editor.delete(prev_ws.syntax());
} else {
- ted::replace(prev_ws.syntax(), make::tokens::whitespace(rest));
+ editor.replace(prev_ws.syntax(), make.whitespace(rest));
}
}
- ted::remove(self.syntax());
+ editor.delete(self.syntax());
}
}
@@ -365,216 +296,23 @@ impl ast::Impl {
}
}
-impl ast::AssocItemList {
- /// Adds a new associated item after all of the existing associated items.
- ///
- /// 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) => (
- IndentLevel::from_node(last_item.syntax()),
- ted::Position::after(last_item.syntax()),
- "\n\n",
- ),
- None => match self.l_curly_token() {
- Some(l_curly) => {
- normalize_ws_between_braces(self.syntax());
- (IndentLevel::from_token(&l_curly) + 1, ted::Position::after(&l_curly), "\n")
- }
- None => (IndentLevel::zero(), ted::Position::last_child_of(self.syntax()), "\n"),
- },
- };
- let elements: Vec<SyntaxElement> = vec![
- make::tokens::whitespace(&format!("{whitespace}{indent}")).into(),
- item.syntax().clone().into(),
- ];
- ted::insert_all(position, elements);
- }
-}
-
-impl ast::RecordExprFieldList {
- pub fn add_field(&self, field: ast::RecordExprField) {
- let is_multiline = self.syntax().text().contains_char('\n');
- let whitespace = if is_multiline {
- let indent = IndentLevel::from_node(self.syntax()) + 1;
- make::tokens::whitespace(&format!("\n{indent}"))
- } else {
- make::tokens::single_space()
- };
-
- if is_multiline {
- normalize_ws_between_braces(self.syntax());
- }
-
- let position = match self.fields().last() {
- Some(last_field) => {
- let comma = get_or_insert_comma_after(last_field.syntax());
- ted::Position::after(comma)
- }
- None => match self.l_curly_token() {
- Some(it) => ted::Position::after(it),
- None => ted::Position::last_child_of(self.syntax()),
- },
- };
-
- ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]);
- if is_multiline {
- ted::insert(ted::Position::after(field.syntax()), ast::make::token(T![,]));
- }
- }
-}
-
impl ast::RecordExprField {
/// This will either replace the initializer, or in the case that this is a shorthand convert
/// the initializer into the name ref and insert the expr as the new initializer.
- pub fn replace_expr(&self, expr: ast::Expr) {
+ pub fn replace_expr(&self, editor: &SyntaxEditor, expr: ast::Expr) {
if self.name_ref().is_some() {
- match self.expr() {
- Some(prev) => ted::replace(prev.syntax(), expr.syntax()),
- None => ted::append_child(self.syntax(), expr.syntax()),
+ if let Some(prev) = self.expr() {
+ editor.replace(prev.syntax(), expr.syntax());
}
- return;
- }
- // this is a shorthand
- if let Some(ast::Expr::PathExpr(path_expr)) = self.expr()
+ } else if let Some(ast::Expr::PathExpr(path_expr)) = self.expr()
&& let Some(path) = path_expr.path()
&& let Some(name_ref) = path.as_single_name_ref()
{
- path_expr.syntax().detach();
- let children = vec![
- name_ref.syntax().clone().into(),
- ast::make::token(T![:]).into(),
- ast::make::tokens::single_space().into(),
- expr.syntax().clone().into(),
- ];
- ted::insert_all_raw(ted::Position::last_child_of(self.syntax()), children);
- }
- }
-}
-
-impl ast::RecordPatFieldList {
- pub fn add_field(&self, field: ast::RecordPatField) {
- let is_multiline = self.syntax().text().contains_char('\n');
- let whitespace = if is_multiline {
- let indent = IndentLevel::from_node(self.syntax()) + 1;
- make::tokens::whitespace(&format!("\n{indent}"))
- } else {
- make::tokens::single_space()
- };
-
- if is_multiline {
- normalize_ws_between_braces(self.syntax());
+ // shorthand `{ x }` → expand to `{ x: expr }`
+ let new_field = editor
+ .make()
+ .record_expr_field(editor.make().name_ref(&name_ref.text()), Some(expr));
+ editor.replace(self.syntax(), new_field.syntax());
}
-
- let position = match self.fields().last() {
- Some(last_field) => {
- let syntax = last_field.syntax();
- let comma = get_or_insert_comma_after(syntax);
- ted::Position::after(comma)
- }
- None => match self.l_curly_token() {
- Some(it) => ted::Position::after(it),
- None => ted::Position::last_child_of(self.syntax()),
- },
- };
-
- ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]);
- if is_multiline {
- ted::insert(ted::Position::after(field.syntax()), ast::make::token(T![,]));
- }
- }
-}
-
-fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken {
- match syntax
- .siblings_with_tokens(Direction::Next)
- .filter_map(|it| it.into_token())
- .find(|it| it.kind() == T![,])
- {
- Some(it) => it,
- None => {
- let comma = ast::make::token(T![,]);
- ted::insert(ted::Position::after(syntax), &comma);
- comma
- }
- }
-}
-
-fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
- let l = node
- .children_with_tokens()
- .filter_map(|it| it.into_token())
- .find(|it| it.kind() == T!['{'])?;
- let r = node
- .children_with_tokens()
- .filter_map(|it| it.into_token())
- .find(|it| it.kind() == T!['}'])?;
-
- let indent = IndentLevel::from_node(node);
-
- match l.next_sibling_or_token() {
- Some(ws)
- if ws.kind() == SyntaxKind::WHITESPACE
- && ws.next_sibling_or_token()?.into_token()? == r =>
- {
- ted::replace(ws, make::tokens::whitespace(&format!("\n{indent}")));
- }
- Some(ws) if ws.kind() == T!['}'] => {
- ted::insert(ted::Position::after(l), make::tokens::whitespace(&format!("\n{indent}")));
- }
- _ => (),
- }
- Some(())
-}
-
-pub trait Indent: AstNode + Clone + Sized {
- fn indent_level(&self) -> IndentLevel {
- IndentLevel::from_node(self.syntax())
- }
- fn indent(&self, by: IndentLevel) {
- by.increase_indent(self.syntax());
- }
- fn dedent(&self, by: IndentLevel) {
- by.decrease_indent(self.syntax());
- }
- fn reindent_to(&self, target_level: IndentLevel) {
- let current_level = IndentLevel::from_node(self.syntax());
- self.dedent(current_level);
- self.indent(target_level);
- }
-}
-
-impl<N: AstNode + Clone> Indent for N {}
-
-#[cfg(test)]
-mod tests {
- use parser::Edition;
-
- use crate::SourceFile;
-
- use super::*;
-
- fn ast_mut_from_text<N: AstNode>(text: &str) -> N {
- let parse = SourceFile::parse(text, Edition::CURRENT);
- parse.tree().syntax().descendants().find_map(N::cast).unwrap().clone_for_update()
- }
-
- #[test]
- fn test_increase_indent() {
- let arm_list = ast_mut_from_text::<ast::Fn>(
- "fn foo() {
- ;
- ;
-}",
- );
- arm_list.indent(IndentLevel(2));
- assert_eq!(
- arm_list.to_string(),
- "fn foo() {
- ;
- ;
- }",
- );
}
}
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 9a2bba9ebf..e3e5c499d4 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -532,6 +532,23 @@ impl ContinueExpr {
support::token(&self.syntax, T![continue])
}
}
+pub struct DerefPat {
+ pub(crate) syntax: SyntaxNode,
+}
+impl DerefPat {
+ #[inline]
+ pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
+ #[inline]
+ pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) }
+ #[inline]
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ #[inline]
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+ #[inline]
+ pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
+ #[inline]
+ pub fn deref_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![deref]) }
+}
pub struct DynTraitType {
pub(crate) syntax: SyntaxNode,
}
@@ -2254,6 +2271,7 @@ pub enum Meta {
pub enum Pat {
BoxPat(BoxPat),
ConstBlockPat(ConstBlockPat),
+ DerefPat(DerefPat),
IdentPat(IdentPat),
LiteralPat(LiteralPat),
MacroPat(MacroPat),
@@ -3585,6 +3603,38 @@ impl fmt::Debug for ContinueExpr {
f.debug_struct("ContinueExpr").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for DerefPat {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ DEREF_PAT
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == DEREF_PAT }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for DerefPat {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for DerefPat {}
+impl PartialEq for DerefPat {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for DerefPat {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for DerefPat {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("DerefPat").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for DynTraitType {
#[inline]
fn kind() -> SyntaxKind
@@ -8515,6 +8565,10 @@ impl From<ConstBlockPat> for Pat {
#[inline]
fn from(node: ConstBlockPat) -> Pat { Pat::ConstBlockPat(node) }
}
+impl From<DerefPat> for Pat {
+ #[inline]
+ fn from(node: DerefPat) -> Pat { Pat::DerefPat(node) }
+}
impl From<IdentPat> for Pat {
#[inline]
fn from(node: IdentPat) -> Pat { Pat::IdentPat(node) }
@@ -8578,6 +8632,7 @@ impl AstNode for Pat {
kind,
BOX_PAT
| CONST_BLOCK_PAT
+ | DEREF_PAT
| IDENT_PAT
| LITERAL_PAT
| MACRO_PAT
@@ -8599,6 +8654,7 @@ impl AstNode for Pat {
let res = match syntax.kind() {
BOX_PAT => Pat::BoxPat(BoxPat { syntax }),
CONST_BLOCK_PAT => Pat::ConstBlockPat(ConstBlockPat { syntax }),
+ DEREF_PAT => Pat::DerefPat(DerefPat { syntax }),
IDENT_PAT => Pat::IdentPat(IdentPat { syntax }),
LITERAL_PAT => Pat::LiteralPat(LiteralPat { syntax }),
MACRO_PAT => Pat::MacroPat(MacroPat { syntax }),
@@ -8622,6 +8678,7 @@ impl AstNode for Pat {
match self {
Pat::BoxPat(it) => &it.syntax,
Pat::ConstBlockPat(it) => &it.syntax,
+ Pat::DerefPat(it) => &it.syntax,
Pat::IdentPat(it) => &it.syntax,
Pat::LiteralPat(it) => &it.syntax,
Pat::MacroPat(it) => &it.syntax,
@@ -10121,6 +10178,11 @@ impl std::fmt::Display for ContinueExpr {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for DerefPat {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for DynTraitType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index ac02cc9e43..95ff3aebd8 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -294,12 +294,7 @@ fn merge_where_clause(
(None, None) => None,
(None, Some(bs)) => Some(bs),
(Some(ps), None) => Some(ps),
- (Some(ps), Some(bs)) => {
- let preds = where_clause(std::iter::empty()).clone_for_update();
- ps.predicates().for_each(|p| preds.add_predicate(p));
- bs.predicates().for_each(|p| preds.add_predicate(p));
- Some(preds)
- }
+ (Some(ps), Some(bs)) => Some(where_clause(ps.predicates().chain(bs.predicates()))),
}
}
@@ -541,9 +536,10 @@ pub fn block_expr(
quote! {
BlockExpr {
StmtList {
- ['{'] "\n"
- #(" " #stmts "\n")*
- #(" " #tail_expr "\n")*
+ ['{']
+ #("\n " #stmts)*
+ #("\n " #tail_expr)*
+ "\n"
['}']
}
}
@@ -877,6 +873,10 @@ pub fn box_pat(pat: ast::Pat) -> ast::BoxPat {
ast_from_text(&format!("fn f(box {pat}: ())"))
}
+pub fn deref_pat(pat: ast::Pat) -> ast::Pat {
+ ast_from_text(&format!("fn f(deref!({pat}): ())"))
+}
+
pub fn paren_pat(pat: ast::Pat) -> ast::ParenPat {
ast_from_text(&format!("fn f(({pat}): ())"))
}
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 751f8d7e1c..1eb658f4b8 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -16,7 +16,7 @@ use crate::{
self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName,
HasTypeBounds, SyntaxNode, support,
},
- ted,
+ syntax_editor::SyntaxEditor,
};
use super::{GenericParam, RangeItem, RangeOp};
@@ -454,11 +454,12 @@ impl ast::UseTreeList {
}
/// Remove the unnecessary braces in current `UseTreeList`
- pub fn remove_unnecessary_braces(mut self) {
+ pub fn remove_unnecessary_braces(mut self, editor: &SyntaxEditor) {
// Returns true iff there is a single subtree and it is not the self keyword. The braces in
// `use x::{self};` are necessary and so we should not remove them.
let has_single_subtree_that_is_not_self = |u: &ast::UseTreeList| {
- if let Some((single_subtree,)) = u.use_trees().collect_tuple() {
+ let use_trees = u.use_trees().filter(|use_tree| !editor.deleted(use_tree.syntax()));
+ if let Some((single_subtree,)) = use_trees.collect_tuple() {
// We have a single subtree, check whether it is self.
let is_self = single_subtree.path().as_ref().is_some_and(|path| {
@@ -476,12 +477,12 @@ impl ast::UseTreeList {
let remove_brace_in_use_tree_list = |u: &ast::UseTreeList| {
if has_single_subtree_that_is_not_self(u) {
if let Some(a) = u.l_curly_token() {
- ted::remove(a)
+ editor.delete(a)
}
if let Some(a) = u.r_curly_token() {
- ted::remove(a)
+ editor.delete(a)
}
- u.comma().for_each(ted::remove);
+ u.comma().for_each(|u| editor.delete(u));
}
};
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs
index 0f3b3d301c..1070af65e7 100644
--- a/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -2,7 +2,7 @@
use either::Either;
use crate::{
- AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken,
+ AstNode, Edition, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken,
ast::{
self, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasName,
HasTypeBounds, HasVisibility, Lifetime, Param, RangeItem, make,
@@ -33,6 +33,10 @@ impl SyntaxFactory {
make::ext::expr_self().clone_for_update()
}
+ pub fn expr_const_value(&self, text: &str) -> ast::ConstArg {
+ make::expr_const_value(text).clone_for_update()
+ }
+
pub fn lifetime(&self, text: &str) -> ast::Lifetime {
make::lifetime(text).clone_for_update()
}
@@ -63,6 +67,14 @@ impl SyntaxFactory {
ast
}
+ pub fn ty_path_from_segments(
+ &self,
+ segments: impl IntoIterator<Item = ast::PathSegment>,
+ is_abs: bool,
+ ) -> ast::Type {
+ ast::Type::PathType(self.ty_path(self.path_from_segments(segments, is_abs)))
+ }
+
pub fn type_bound(&self, bound: ast::Type) -> ast::TypeBound {
make::type_bound(bound).clone_for_update()
}
@@ -131,6 +143,10 @@ impl SyntaxFactory {
make::path_from_text(text).clone_for_update()
}
+ pub fn path_from_text_with_edition(&self, text: &str, edition: Edition) -> ast::Path {
+ make::path_from_text_with_edition(text, edition).clone_for_update()
+ }
+
pub fn path_concat(&self, first: ast::Path, second: ast::Path) -> ast::Path {
make::path_concat(first, second).clone_for_update()
}
@@ -491,6 +507,18 @@ impl SyntaxFactory {
ast
}
+ pub fn path_segment_self(&self) -> ast::PathSegment {
+ make::path_segment_self().clone_for_update()
+ }
+
+ pub fn path_segment_super(&self) -> ast::PathSegment {
+ make::path_segment_super().clone_for_update()
+ }
+
+ pub fn path_segment_crate(&self) -> ast::PathSegment {
+ make::path_segment_crate().clone_for_update()
+ }
+
pub fn generic_ty_path_segment(
&self,
name_ref: ast::NameRef,
@@ -551,6 +579,25 @@ impl SyntaxFactory {
make::ty_placeholder().clone_for_update()
}
+ pub fn ty_unit(&self) -> ast::Type {
+ make::ty_unit().clone_for_update()
+ }
+
+ pub fn ty_tuple(&self, types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
+ let (types, input) = iterator_input(types);
+ let ast = make::ty_tuple(types).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings()
+ && let ast::Type::TupleType(tuple_ty) = &ast
+ {
+ let mut builder = SyntaxMappingBuilder::new(tuple_ty.syntax().clone());
+ builder.map_children(input, tuple_ty.fields().map(|ty| ty.syntax().clone()));
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn path_segment_generics(
&self,
name_ref: ast::NameRef,
@@ -628,6 +675,10 @@ impl SyntaxFactory {
ast
}
+ pub fn use_tree_glob(&self) -> ast::UseTree {
+ make::use_tree_glob().clone_for_update()
+ }
+
pub fn path_unqualified(&self, segment: ast::PathSegment) -> ast::Path {
let ast = make::path_unqualified(segment.clone()).clone_for_update();
@@ -640,6 +691,23 @@ impl SyntaxFactory {
ast
}
+ pub fn path_qualified(&self, qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
+ let ast = make::path_qualified(qual.clone(), segment.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ if let Some(out_qual) = ast.qualifier() {
+ builder.map_node(qual.syntax().clone(), out_qual.syntax().clone());
+ }
+ if let Some(out_segment) = ast.segment() {
+ builder.map_node(segment.syntax().clone(), out_segment.syntax().clone());
+ }
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn path_from_segments(
&self,
segments: impl IntoIterator<Item = ast::PathSegment>,
@@ -676,6 +744,18 @@ impl SyntaxFactory {
ast
}
+ pub fn simple_ident_pat(&self, name: ast::Name) -> ast::IdentPat {
+ let ast = make::ext::simple_ident_pat(name.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn wildcard_pat(&self) -> ast::WildcardPat {
make::wildcard_pat().clone_for_update()
}
@@ -851,6 +931,10 @@ impl SyntaxFactory {
ast
}
+ pub fn deref_pat(&self, pat: ast::Pat) -> ast::Pat {
+ make::deref_pat(pat.clone()).clone_for_update()
+ }
+
pub fn paren_pat(&self, pat: ast::Pat) -> ast::ParenPat {
let ast = make::paren_pat(pat.clone()).clone_for_update();
@@ -925,6 +1009,37 @@ impl SyntaxFactory {
ast
}
+ pub fn async_move_block_expr(
+ &self,
+ statements: impl IntoIterator<Item = ast::Stmt>,
+ tail_expr: Option<ast::Expr>,
+ ) -> ast::BlockExpr {
+ let (statements, mut input) = iterator_input(statements);
+
+ let ast = make::async_move_block_expr(statements, tail_expr.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let stmt_list = ast.stmt_list().unwrap();
+ let mut builder = SyntaxMappingBuilder::new(stmt_list.syntax().clone());
+
+ if let Some(input) = tail_expr {
+ builder.map_node(
+ input.syntax().clone(),
+ stmt_list.tail_expr().unwrap().syntax().clone(),
+ );
+ } else if let Some(ast_tail) = stmt_list.tail_expr() {
+ let last_stmt = input.pop().unwrap();
+ builder.map_node(last_stmt, ast_tail.syntax().clone());
+ }
+
+ builder.map_children(input, stmt_list.statements().map(|it| it.syntax().clone()));
+
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn expr_empty_block(&self) -> ast::BlockExpr {
make::expr_empty_block().clone_for_update()
}
@@ -1075,6 +1190,27 @@ impl SyntaxFactory {
ast.into()
}
+ pub fn expr_reborrow(&self, expr: ast::Expr) -> ast::Expr {
+ let ast::Expr::RefExpr(ast) = make::expr_reborrow(expr.clone()).clone_for_update() else {
+ unreachable!()
+ };
+
+ if let Some(mut mapping) = self.mappings() {
+ // Layout: RefExpr(&mut, PrefixExpr(*, expr)). Map `expr` to the
+ // inner expr inside the synthesized PrefixExpr.
+ let prefix = match ast.expr() {
+ Some(ast::Expr::PrefixExpr(p)) => p,
+ _ => unreachable!("expr_reborrow always produces `&mut *expr`"),
+ };
+ let inner = prefix.expr().unwrap();
+ let mut builder = SyntaxMappingBuilder::new(prefix.syntax().clone());
+ builder.map_node(expr.syntax().clone(), inner.syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast.into()
+ }
+
pub fn expr_raw_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr {
let ast::Expr::RefExpr(ast) =
make::expr_raw_ref(expr.clone(), exclusive).clone_for_update()
@@ -2113,6 +2249,21 @@ impl SyntaxFactory {
make::ext::field_from_idents(parts)
}
+ pub fn ty_name(&self, name: ast::Name) -> ast::Type {
+ let ast = make::ext::ty_name(name.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings()
+ && let ast::Type::PathType(path_ty) = &ast
+ && let Some(name_ref) = path_ty.path().and_then(|path| path.segment()?.name_ref())
+ {
+ let mut builder = SyntaxMappingBuilder::new(name_ref.syntax().parent().unwrap());
+ builder.map_node(name.syntax().clone(), name_ref.syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn expr_await(&self, expr: ast::Expr) -> ast::AwaitExpr {
let ast::Expr::AwaitExpr(ast) = make::expr_await(expr.clone()).clone_for_update() else {
unreachable!()
@@ -2127,6 +2278,53 @@ impl SyntaxFactory {
ast
}
+ pub fn expr_try(&self, expr: ast::Expr) -> ast::Expr {
+ let ast = make::expr_try(expr.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ if let ast::Expr::TryExpr(try_expr) = &ast
+ && let Some(inner) = try_expr.expr()
+ {
+ builder.map_node(expr.syntax().clone(), inner.syntax().clone());
+ }
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn hacky_block_expr(
+ &self,
+ elements: impl IntoIterator<Item = SyntaxElement>,
+ tail_expr: Option<ast::Expr>,
+ ) -> ast::BlockExpr {
+ let elements = elements.into_iter().collect::<Vec<_>>();
+ let ast =
+ make::hacky_block_expr(elements.iter().cloned(), tail_expr.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings()
+ && let Some(stmt_list) = ast.stmt_list()
+ {
+ let mut builder = SyntaxMappingBuilder::new(stmt_list.syntax().clone());
+ builder.map_children(
+ elements.into_iter().filter_map(|node_or_token| match node_or_token {
+ NodeOrToken::Node(node) => Some(node),
+ NodeOrToken::Token(_) => None,
+ }),
+ stmt_list.syntax().children(),
+ );
+ if let Some(tail_expr) = tail_expr
+ && let Some(output_tail_expr) = stmt_list.tail_expr()
+ {
+ builder.map_node(tail_expr.syntax().clone(), output_tail_expr.syntax().clone());
+ }
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn expr_break(&self, label: Option<Lifetime>, expr: Option<ast::Expr>) -> ast::BreakExpr {
let ast::Expr::BreakExpr(ast) =
make::expr_break(label.clone(), expr.clone()).clone_for_update()
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs
index c510b2831e..cda3e69b7c 100644
--- a/crates/syntax/src/lib.rs
+++ b/crates/syntax/src/lib.rs
@@ -10,8 +10,8 @@
//! the [Swift] one.
//!
//! The most interesting modules here are `syntax_node` (which defines concrete
-//! syntax tree) and `ast` (which defines abstract syntax tree on top of the
-//! CST). The actual parser live in a separate `parser` crate, though the
+//! syntax tree) and [`ast`] (which defines abstract syntax tree on top of the
+//! CST). The actual parser live in a separate [`parser`] crate, though the
//! lexer lives in this crate.
//!
//! See `api_walkthrough` test in this file for a quick API tour!
diff --git a/crates/syntax/src/ptr.rs b/crates/syntax/src/ptr.rs
index c4979b8e3a..ed8a68fb8a 100644
--- a/crates/syntax/src/ptr.rs
+++ b/crates/syntax/src/ptr.rs
@@ -21,7 +21,7 @@ use crate::{AstNode, SyntaxNode, syntax_node::RustLanguage};
/// A "pointer" to a [`SyntaxNode`], via location in the source code.
pub type SyntaxNodePtr = rowan::ast::SyntaxNodePtr<RustLanguage>;
-/// Like `SyntaxNodePtr`, but remembers the type of node.
+/// Like [`SyntaxNodePtr`], but remembers the type of node.
pub struct AstPtr<N: AstNode> {
raw: SyntaxNodePtr,
_ty: PhantomData<fn() -> N>,
@@ -90,7 +90,7 @@ impl<N: AstNode> AstPtr<N> {
AstPtr { raw: self.raw, _ty: PhantomData }
}
- /// Like `SyntaxNodePtr::cast` but the trait bounds work out.
+ /// Like [`SyntaxNodePtr::cast`] but the trait bounds work out.
pub fn try_from_raw(raw: SyntaxNodePtr) -> Option<AstPtr<N>> {
N::can_cast(raw.kind()).then_some(AstPtr { raw, _ty: PhantomData })
}
diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs
index edd063ffd4..7d15195c6f 100644
--- a/crates/syntax/src/syntax_editor.rs
+++ b/crates/syntax/src/syntax_editor.rs
@@ -133,7 +133,19 @@ impl SyntaxEditor {
!matches!(&element, SyntaxElement::Node(node) if node == &self.root),
"should not delete root node"
);
- self.changes.borrow_mut().push(Change::Replace(element.syntax_element(), None));
+ let mut changes = self.changes.borrow_mut();
+ for change in changes.iter_mut() {
+ if let Change::Replace(existing, replacement) = change
+ && *existing == element
+ {
+ if replacement.is_none() {
+ return;
+ }
+ *replacement = None;
+ return;
+ }
+ }
+ changes.push(Change::Replace(element, None));
}
pub fn delete_all(&self, range: RangeInclusive<SyntaxElement>) {
@@ -149,9 +161,23 @@ impl SyntaxEditor {
pub fn replace(&self, old: impl Element, new: impl Element) {
let old = old.syntax_element();
debug_assert!(is_ancestor_or_self_of_element(&old, &self.root));
- self.changes
- .borrow_mut()
- .push(Change::Replace(old.syntax_element(), Some(new.syntax_element())));
+ let new = new.syntax_element();
+ let mut changes = self.changes.borrow_mut();
+ for change in changes.iter_mut() {
+ if let Change::Replace(existing, replacement) = change
+ && *existing == old
+ {
+ match replacement {
+ None => return,
+ Some(existing_new) if *existing_new == new => return,
+ Some(existing_new) => {
+ *existing_new = new;
+ return;
+ }
+ }
+ }
+ }
+ changes.push(Change::Replace(old, Some(new)));
}
pub fn replace_with_many(&self, old: impl Element, new: Vec<SyntaxElement>) {
@@ -177,6 +203,14 @@ impl SyntaxEditor {
pub fn finish(self) -> SyntaxEdit {
edit_algo::apply_edits(self)
}
+
+ pub fn deleted(&self, element: impl Element) -> bool {
+ let element = element.syntax_element();
+ self.changes
+ .borrow()
+ .iter()
+ .any(|change| matches!(change, Change::Replace(existing, None) if *existing == element))
+ }
}
/// Represents a completed [`SyntaxEditor`] operation.
@@ -216,6 +250,17 @@ impl SyntaxEdit {
pub fn find_annotation(&self, annotation: SyntaxAnnotation) -> &[SyntaxElement] {
self.annotations.get(&annotation).as_ref().map_or(&[], |it| it.as_slice())
}
+
+ pub fn find_element(&self, old_node: &SyntaxNode) -> Option<SyntaxNode> {
+ let old_root_start = self.old_root.text_range().start();
+ let old_start = old_node.text_range().start() - old_root_start;
+ let new_root_start = self.new_root.text_range().start();
+ let kind = old_node.kind();
+
+ self.new_root
+ .descendants()
+ .find(|it| it.kind() == kind && it.text_range().start() - new_root_start == old_start)
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -702,6 +747,62 @@ mod tests {
}
#[test]
+ fn test_dependent_change_prefers_nearest_changed_ancestor() {
+ let root = make::block_expr(
+ [],
+ Some(
+ make::block_expr(
+ [make::let_stmt(
+ make::ext::simple_ident_pat(make::name("second")).into(),
+ None,
+ Some(make::expr_literal("2").into()),
+ )
+ .into()],
+ None,
+ )
+ .into(),
+ ),
+ );
+
+ let (editor, root) = SyntaxEditor::with_ast_node(&root);
+ let make = editor.make();
+
+ let inner_block =
+ root.syntax().descendants().flat_map(ast::BlockExpr::cast).nth(1).unwrap();
+
+ let outer_replacement = make.block_expr([], Some(ast::Expr::BlockExpr(root.clone())));
+ let inner_replacement =
+ make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone())));
+
+ let first_let = make.let_stmt(
+ make::ext::simple_ident_pat(make::name("first")).into(),
+ None,
+ Some(make::expr_literal("1").into()),
+ );
+
+ editor.insert(
+ Position::first_child_of(inner_block.stmt_list().unwrap().syntax()),
+ first_let.syntax(),
+ );
+ editor.replace(inner_block.syntax(), inner_replacement.syntax());
+ editor.replace(root.syntax(), outer_replacement.syntax());
+
+ let edit = editor.finish();
+
+ let expect = expect![[r#"
+ {
+ {
+ {
+ let first = 1;{
+ let second = 2;
+ }
+ }
+ }
+ }"#]];
+ expect.assert_eq(&edit.new_root.to_string());
+ }
+
+ #[test]
fn test_replace_root_with_dependent() {
let root = make::block_expr(
[make::let_stmt(
diff --git a/crates/syntax/src/syntax_editor/edit_algo.rs b/crates/syntax/src/syntax_editor/edit_algo.rs
index 27ea03ec09..36f50e3918 100644
--- a/crates/syntax/src/syntax_editor/edit_algo.rs
+++ b/crates/syntax/src/syntax_editor/edit_algo.rs
@@ -111,8 +111,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
// Check if this change is dependent on another change (i.e. it's contained within another range)
if let Some(index) = changed_ancestors
.iter()
- .rev()
- .position(|ancestor| ancestor.affected_range().contains_range(change.target_range()))
+ .rposition(|ancestor| ancestor.affected_range().contains_range(change.target_range()))
{
// Pop off any ancestors that aren't applicable
changed_ancestors.drain((index + 1)..);
@@ -284,7 +283,7 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
}
}
- for DependentChange { parent, child } in dependent_changes.into_iter() {
+ for DependentChange { parent, child } in dependent_changes.into_iter().rev() {
let (input_ancestor, output_ancestor) = match &changes[parent as usize] {
// No change will depend on an insert since changes can only depend on nodes in the root tree
Change::Insert(_, _) | Change::InsertAll(_, _) => unreachable!(),
diff --git a/crates/syntax/src/syntax_editor/edits.rs b/crates/syntax/src/syntax_editor/edits.rs
index 28e8ceed70..0338d976b0 100644
--- a/crates/syntax/src/syntax_editor/edits.rs
+++ b/crates/syntax/src/syntax_editor/edits.rs
@@ -3,7 +3,7 @@
use crate::{
AstToken, Direction, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T,
algo::neighbor,
- ast::{self, AstNode, Fn, GenericParam, HasGenericParams, HasName, edit::IndentLevel, make},
+ ast::{self, AstNode, HasGenericParams, HasName, edit::IndentLevel, make},
syntax_editor::{Position, SyntaxEditor},
};
@@ -109,64 +109,79 @@ impl GetOrCreateWhereClause for ast::Enum {
}
impl SyntaxEditor {
- /// Adds a new generic param to the function using `SyntaxEditor`
- pub fn add_generic_param(&self, function: &Fn, new_param: GenericParam) {
- match function.generic_param_list() {
- Some(generic_param_list) => match generic_param_list.generic_params().last() {
- Some(last_param) => {
- // There exists a generic param list and it's not empty
- let position = generic_param_list.r_angle_token().map_or_else(
- || Position::last_child_of(function.syntax()),
- Position::before,
- );
-
- if last_param
- .syntax()
- .next_sibling_or_token()
- .is_some_and(|it| it.kind() == SyntaxKind::COMMA)
- {
- self.insert(
- Position::after(last_param.syntax()),
- new_param.syntax().clone(),
- );
- self.insert(
- Position::after(last_param.syntax()),
- make::token(SyntaxKind::WHITESPACE),
- );
- self.insert(
- Position::after(last_param.syntax()),
- make::token(SyntaxKind::COMMA),
- );
+ /// Adds a new generic param to the node using `SyntaxEditor`
+ pub fn add_generic_param(
+ &self,
+ node: &impl ast::HasGenericParams,
+ new_param: ast::GenericParam,
+ ) {
+ let make = self.make();
+ match node.generic_param_list() {
+ Some(generic_param_list) => {
+ let is_lifetime = matches!(new_param, ast::GenericParam::LifetimeParam(_));
+
+ if let Some(first_param) = generic_param_list.generic_params().next() {
+ let last_lifetime = generic_param_list
+ .generic_params()
+ .filter(|p| matches!(p, ast::GenericParam::LifetimeParam(_)))
+ .last();
+
+ if is_lifetime {
+ if let Some(last_lt) = last_lifetime {
+ let elements = vec![
+ make.token(SyntaxKind::COMMA).into(),
+ make.token(SyntaxKind::WHITESPACE).into(),
+ new_param.syntax().clone().into(),
+ ];
+ self.insert_all(Position::after(last_lt.syntax()), elements);
+ } else {
+ // Insert before the first parameter
+ let elements = vec![
+ new_param.syntax().clone().into(),
+ make.token(SyntaxKind::COMMA).into(),
+ make.token(SyntaxKind::WHITESPACE).into(),
+ ];
+ self.insert_all(Position::before(first_param.syntax()), elements);
+ }
} else {
+ let last_param = generic_param_list.generic_params().last().unwrap();
let elements = vec![
- make::token(SyntaxKind::COMMA).into(),
- make::token(SyntaxKind::WHITESPACE).into(),
+ make.token(SyntaxKind::COMMA).into(),
+ make.token(SyntaxKind::WHITESPACE).into(),
new_param.syntax().clone().into(),
];
- self.insert_all(position, elements);
+ self.insert_all(Position::after(last_param.syntax()), elements);
+ }
+ } else {
+ if let Some(l_angle) = generic_param_list.l_angle_token() {
+ self.insert(Position::after(l_angle), new_param.syntax().clone());
}
}
- None => {
- // There exists a generic param list but it's empty
- let position = Position::after(generic_param_list.l_angle_token().unwrap());
- self.insert(position, new_param.syntax());
- }
- },
+ }
None => {
- // There was no generic param list
- let position = if let Some(name) = function.name() {
- Position::after(name.syntax)
- } else if let Some(fn_token) = function.fn_token() {
- Position::after(fn_token)
- } else if let Some(param_list) = function.param_list() {
- Position::before(param_list.syntax)
- } else {
- Position::last_child_of(function.syntax())
- };
+ let position =
+ if let Some(name) = node.syntax().children().find_map(ast::Name::cast) {
+ Position::after(name.syntax())
+ } else if let Some(impl_node) = ast::Impl::cast(node.syntax().clone()) {
+ impl_node
+ .impl_token()
+ .map_or_else(|| Position::last_child_of(node.syntax()), Position::after)
+ } else if let Some(fn_node) = ast::Fn::cast(node.syntax().clone()) {
+ if let Some(fn_token) = fn_node.fn_token() {
+ Position::after(fn_token)
+ } else if let Some(param_list) = fn_node.param_list() {
+ Position::before(param_list.syntax())
+ } else {
+ Position::last_child_of(node.syntax())
+ }
+ } else {
+ Position::last_child_of(node.syntax())
+ };
+
let elements = vec![
- make::token(SyntaxKind::L_ANGLE).into(),
+ make.token(SyntaxKind::L_ANGLE).into(),
new_param.syntax().clone().into(),
- make::token(SyntaxKind::R_ANGLE).into(),
+ make.token(SyntaxKind::R_ANGLE).into(),
];
self.insert_all(position, elements);
}
@@ -176,11 +191,7 @@ impl SyntaxEditor {
fn get_or_insert_comma_after(editor: &SyntaxEditor, syntax: &SyntaxNode) -> SyntaxToken {
let make = editor.make();
- match syntax
- .siblings_with_tokens(Direction::Next)
- .filter_map(|it| it.into_token())
- .find(|it| it.kind() == T![,])
- {
+ match comma_after(syntax) {
Some(it) => it,
None => {
let comma = make.token(T![,]);
@@ -226,6 +237,113 @@ impl ast::AssocItemList {
}
}
+impl ast::RecordExprFieldList {
+ pub fn add_fields(
+ &self,
+ editor: &SyntaxEditor,
+ fields: impl IntoIterator<Item = ast::RecordExprField>,
+ ) {
+ add_record_fields(
+ editor,
+ self.syntax(),
+ self.fields().last().map(|it| it.syntax().clone()),
+ self.l_curly_token(),
+ fields.into_iter().map(|it| it.syntax().clone().into()),
+ );
+ }
+}
+
+impl ast::RecordPatFieldList {
+ pub fn add_fields(
+ &self,
+ editor: &SyntaxEditor,
+ fields: impl IntoIterator<Item = ast::RecordPatField>,
+ ) {
+ add_record_fields(
+ editor,
+ self.syntax(),
+ self.fields().last().map(|it| it.syntax().clone()),
+ self.l_curly_token(),
+ fields.into_iter().map(|it| it.syntax().clone().into()),
+ );
+ }
+}
+
+fn add_record_fields(
+ editor: &SyntaxEditor,
+ field_list: &SyntaxNode,
+ last_field: Option<SyntaxNode>,
+ l_curly: Option<SyntaxToken>,
+ fields: impl Iterator<Item = SyntaxElement>,
+) {
+ let fields = fields.collect::<Vec<_>>();
+ if fields.is_empty() {
+ return;
+ }
+
+ let make = editor.make();
+ let is_multiline = field_list.text().contains_char('\n');
+ let whitespace = || {
+ if is_multiline {
+ let indent = IndentLevel::from_node(field_list) + 1;
+ make.whitespace(&format!("\n{indent}"))
+ } else {
+ make.whitespace(" ")
+ }
+ };
+
+ if is_multiline {
+ normalize_ws_between_braces(editor, field_list);
+ }
+
+ let mut elements = Vec::new();
+ let next_after_insert;
+ let position = match last_field {
+ Some(last_field) => match comma_after(&last_field) {
+ Some(comma) => {
+ next_after_insert = comma.next_sibling_or_token();
+ Position::after(comma)
+ }
+ None => {
+ next_after_insert = last_field.next_sibling_or_token();
+ elements.push(make.token(T![,]).into());
+ Position::after(last_field)
+ }
+ },
+ None => match l_curly {
+ Some(it) => {
+ next_after_insert = it.next_sibling_or_token();
+ Position::after(it)
+ }
+ None => {
+ next_after_insert = None;
+ Position::last_child_of(field_list)
+ }
+ },
+ };
+
+ let fields_len = fields.len();
+ for (idx, field) in fields.into_iter().enumerate() {
+ elements.push(whitespace().into());
+ elements.push(field);
+ if is_multiline || idx + 1 != fields_len {
+ elements.push(make.token(T![,]).into());
+ }
+ }
+ if !is_multiline && next_after_insert.is_some_and(|it| it.kind() != SyntaxKind::WHITESPACE) {
+ elements.push(make.whitespace(" ").into());
+ }
+
+ editor.insert_all(position, elements);
+}
+
+fn comma_after(syntax: &SyntaxNode) -> Option<SyntaxToken> {
+ syntax
+ .siblings_with_tokens(Direction::Next)
+ .filter_map(|it| it.into_token())
+ .find(|it| it.kind() == T![,])
+}
+
impl ast::Impl {
pub fn get_or_create_assoc_item_list_with_editor(
&self,
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index a51698aca8..8975fa56d7 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -11,6 +11,7 @@
//! add:
//! asm:
//! assert:
+//! async_iterator: option, future, pin
//! as_mut: sized
//! as_ref: sized
//! async_fn: fn, tuple, future, copy
@@ -27,12 +28,14 @@
//! default: sized
//! deref_mut: deref
//! deref: sized
+//! deref_pat: deref
//! derive:
//! discriminant:
//! drop: sized
//! env: option
//! eq: sized
//! error: fmt
+//! float_consts:
//! fmt: option, result, transmute, coerce_unsized, copy, clone, derive
//! fmt_before_1_93_0: fmt
//! fmt_before_1_89_0: fmt_before_1_93_0
@@ -50,6 +53,7 @@
//! iterator: option
//! iterators: iterator, fn
//! manually_drop: drop
+//! matches:
//! non_null:
//! non_zero:
//! option: panic
@@ -66,7 +70,7 @@
//! size_of: sized
//! sized:
//! slice:
-//! str:
+//! str: sized, result
//! sync: sized
//! transmute:
//! try: infallible
@@ -614,6 +618,15 @@ pub mod ops {
}
// endregion:deref_mut
+ // region:deref_pat
+ #[lang = "deref_pure"]
+ #[rustc_dyn_incompatible_trait]
+ pub unsafe trait DerefPure: PointeeSized {}
+
+ unsafe impl<T: ?Sized> DerefPure for &T {}
+ unsafe impl<T: ?Sized> DerefPure for &mut T {}
+ // endregion:deref_pat
+
// region:receiver
#[lang = "receiver"]
pub trait Receiver: PointeeSized {
@@ -631,8 +644,9 @@ pub mod ops {
}
pub use self::deref::{
Deref,
- DerefMut, // :deref_mut
- Receiver, // :receiver
+ DerefMut, // :deref_mut
+ DerefPure, // :deref_pat
+ Receiver, // :receiver
};
// endregion:deref
@@ -698,6 +712,37 @@ pub mod ops {
unsafe impl<T> SliceIndex<[T]> for usize {
type Output = T;
}
+
+ macro_rules! impl_index_range {
+ ( $($range:ty,)* ) => {
+ $(
+ unsafe impl<T> SliceIndex<[T]> for $range {
+ type Output = [T];
+ }
+ )*
+ }
+ }
+
+ // region:range
+ impl_index_range!(
+ crate::ops::RangeFull,
+ crate::ops::Range<usize>,
+ crate::ops::RangeFrom<usize>,
+ crate::ops::RangeTo<usize>,
+ crate::ops::RangeInclusive<usize>,
+ crate::ops::RangeToInclusive<usize>,
+ );
+ // endregion:range
+
+ // region:new_range
+ impl_index_range!(
+ crate::range::Range<usize>,
+ crate::range::RangeFrom<usize>,
+ crate::range::RangeInclusive<usize>,
+ crate::range::RangeToInclusive<usize>,
+ );
+ // endregion:new_range
+
// endregion:slice
}
pub use self::index::{Index, IndexMut};
@@ -735,6 +780,30 @@ pub mod ops {
pub struct RangeToInclusive<Idx> {
pub end: Idx,
}
+
+ // region:iterator
+ pub trait Step {}
+ macro_rules! impl_step {
+ ( $( $ty:ty ),* $(,)? ) => {
+ $(
+ impl Step for $ty {}
+ )*
+ };
+ }
+ impl_step!(i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize);
+
+ macro_rules! impl_iterator {
+ ( $( $range:ident ),* $(,)? ) => {
+ $(
+ impl<Idx: Step> Iterator for $range<Idx> {
+ type Item = Idx;
+ fn next(&mut self) -> Option<Self::Item> { loop {} }
+ }
+ )*
+ };
+ }
+ impl_iterator!(Range, RangeFrom, RangeTo, RangeInclusive, RangeToInclusive);
+ // endregion:iterator
}
pub use self::range::{Range, RangeFrom, RangeFull, RangeTo};
pub use self::range::{RangeInclusive, RangeToInclusive};
@@ -1289,6 +1358,38 @@ pub mod fmt {
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
}
+ impl<T: ?Sized + Debug> Debug for &T {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ T::fmt(&**self, f)
+ }
+ }
+ impl<T: ?Sized + Display> Display for &T {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+ T::fmt(&**self, f)
+ }
+ }
+
+ macro_rules! impl_fmt_traits {
+ ( $($ty:ty),* $(,)? ) => {
+ $(
+ impl Debug for $ty {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result { loop {} }
+ }
+ impl Display for $ty {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result { loop {} }
+ }
+ )*
+ }
+ }
+
+ impl_fmt_traits!(str);
+
+ // region:builtin_impls
+ impl_fmt_traits!(
+ i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, char,
+ );
+ // endregion:builtin_impls
+
mod rt {
use super::*;
@@ -1529,6 +1630,7 @@ pub mod slice {
// region:option
pub mod option {
+ #[lang = "Option"]
pub enum Option<T> {
#[lang = "None"]
None,
@@ -1678,6 +1780,7 @@ pub mod future {
}
}
pub mod task {
+ #[lang = "Poll"]
pub enum Poll<T> {
#[lang = "Ready"]
Ready(T),
@@ -1691,6 +1794,22 @@ pub mod task {
}
// endregion:future
+// region:async_iterator
+pub mod async_iter {
+ use crate::{
+ pin::Pin,
+ task::{Context, Poll},
+ };
+
+ #[lang = "async_iterator"]
+ pub trait AsyncIterator {
+ type Item;
+
+ fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
+ }
+}
+// endregion:async_iterator
+
// region:iterator
pub mod iter {
// region:iterators
@@ -1725,6 +1844,22 @@ pub mod iter {
}
}
+ pub struct Map<I, F> {
+ iter: I,
+ f: F,
+ }
+ impl<B, I: Iterator, F> Iterator for Map<I, F>
+ where
+ F: FnMut(I::Item) -> B,
+ {
+ type Item = B;
+
+ #[inline]
+ fn next(&mut self) -> B {
+ loop {}
+ }
+ }
+
pub struct FilterMap<I, F> {
iter: I,
f: F,
@@ -1799,6 +1934,13 @@ pub mod iter {
{
loop {}
}
+ fn map<B, F>(self, _f: F) -> crate::iter::Map<Self, F>
+ where
+ Self: Sized,
+ F: FnMut(Self::Item) -> B,
+ {
+ loop {}
+ }
fn filter_map<B, F>(self, _f: F) -> crate::iter::FilterMap<Self, F>
where
Self: Sized,
@@ -1861,7 +2003,7 @@ pub mod iter {
pub struct Iter<'a, T> {
slice: &'a [T],
}
- impl<'a, T> IntoIterator for &'a [T; N] {
+ impl<'a, T, const N: usize> IntoIterator for &'a [T; N] {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
@@ -2127,6 +2269,13 @@ mod macros {
#[macro_export]
macro_rules! option_env {}
// endregion:env
+
+ // region:deref_pat
+ #[allow_internal_unstable(builtin_syntax)]
+ pub macro deref($pat:pat) {
+ builtin # deref($pat)
+ }
+ // endregion:deref_pat
}
// region:non_zero
@@ -2181,6 +2330,32 @@ pub mod error {
}
// endregion:error
+// region:float_consts
+impl f32 {
+ pub const INFINITY: f32 = 0.0;
+ pub const NEG_INFINITY: f32 = -0.0;
+}
+
+impl f64 {
+ pub const INFINITY: f64 = 0.0;
+ pub const NEG_INFINITY: f64 = -0.0;
+}
+
+pub mod f32 {
+ #[deprecated]
+ pub const INFINITY: f32 = 0.0;
+ #[deprecated]
+ pub const NEG_INFINITY: f32 = -0.0;
+}
+
+pub mod f64 {
+ #[deprecated]
+ pub const INFINITY: f64 = 0.0;
+ #[deprecated]
+ pub const NEG_INFINITY: f64 = -0.0;
+}
+// endregion:float_consts
+
// region:column
#[rustc_builtin_macro]
#[macro_export]
@@ -2189,6 +2364,20 @@ macro_rules! column {
}
// endregion:column
+// region:matches
+#[macro_export]
+#[allow_internal_unstable(non_exhaustive_omitted_patterns_lint, stmt_expr_attributes)]
+macro_rules! matches {
+ ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
+ #[allow(non_exhaustive_omitted_patterns)]
+ match $expression {
+ $pattern $(if $guard)? => true,
+ _ => false
+ }
+ };
+}
+// endregion:matches
+
pub mod prelude {
pub mod v1 {
pub use crate::{
@@ -2216,6 +2405,7 @@ pub mod prelude {
panic, // :panic
result::Result::{self, Err, Ok}, // :result
str::FromStr, // :str
+ macros::deref, // :deref_pat
};
}
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md
index da37fc1582..069c8211db 100644
--- a/docs/book/src/configuration_generated.md
+++ b/docs/book/src/configuration_generated.md
@@ -375,6 +375,15 @@ If false, `-p <package>` will be passed instead if applicable. In case it is not
check will be performed.
+## rust-analyzer.completion.addColonsToModule {#completion.addColonsToModule}
+
+Default: `true`
+
+Automatically add `::` when completing the module.
+
+Will not be completed in `use`.
+
+
## rust-analyzer.completion.addSemicolonToUnit {#completion.addSemicolonToUnit}
Default: `true`
diff --git a/docs/book/src/contributing/lsp-extensions.md b/docs/book/src/contributing/lsp-extensions.md
index 8ba6f6ab53..b74c40c422 100644
--- a/docs/book/src/contributing/lsp-extensions.md
+++ b/docs/book/src/contributing/lsp-extensions.md
@@ -1,5 +1,5 @@
<!---
-lsp/ext.rs hash: dc4ba5f417c74aa6
+lsp/ext.rs hash: 57ae57d2a5c65b14
If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue:
diff --git a/editors/code/package.json b/editors/code/package.json
index 67570cd067..14369e6f33 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -1277,6 +1277,16 @@
{
"title": "Completion",
"properties": {
+ "rust-analyzer.completion.addColonsToModule": {
+ "markdownDescription": "Automatically add `::` when completing the module.\n\nWill not be completed in `use`.",
+ "default": true,
+ "type": "boolean"
+ }
+ }
+ },
+ {
+ "title": "Completion",
+ "properties": {
"rust-analyzer.completion.addSemicolonToUnit": {
"markdownDescription": "Automatically add a semicolon when completing unit-returning functions.\n\nIn `match` arms it completes a comma instead.",
"default": true,
diff --git a/lib/smol_str/README.md b/lib/smol_str/README.md
index 56296fb53f..7b52a6c5b3 100644
--- a/lib/smol_str/README.md
+++ b/lib/smol_str/README.md
@@ -8,7 +8,7 @@
A `SmolStr` is a string type that has the following properties:
* `size_of::<SmolStr>() == 24` (therefore `== size_of::<String>()` on 64 bit platforms)
-* `Clone` is `O(1)`
+* `Clone` is `O(1)` (no allocation)
* Strings are stack-allocated if they are:
* Up to 23 bytes long
* Longer than 23 bytes, but substrings of `WS` (see `src/lib.rs`). Such strings consist
diff --git a/lib/smol_str/src/lib.rs b/lib/smol_str/src/lib.rs
index 55ede286c2..b76c1c7d14 100644
--- a/lib/smol_str/src/lib.rs
+++ b/lib/smol_str/src/lib.rs
@@ -15,8 +15,8 @@ use core::{
/// A `SmolStr` is a string type that has the following properties:
///
-/// * `size_of::<SmolStr>() == 24` (therefor `== size_of::<String>()` on 64 bit platforms)
-/// * `Clone` is `O(1)`
+/// * `size_of::<SmolStr>() == 24` (therefore `== size_of::<String>()` on 64 bit platforms)
+/// * `Clone` is `O(1)` (no allocation)
/// * Strings are stack-allocated if they are:
/// * Up to 23 bytes long
/// * Longer than 23 bytes, but substrings of `WS` (see below). Such strings consist
diff --git a/rust-version b/rust-version
index e9fc6c4cd0..1e8579c045 100644
--- a/rust-version
+++ b/rust-version
@@ -1 +1 @@
-e22c616e4e87914135c1db261a03e0437255335e
+8afb6a8b1b32fce2f8aa7520517833338dc36c5e
diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs
index a0abdf09d3..43462d1c6e 100644
--- a/xtask/src/codegen/grammar/ast_src.rs
+++ b/xtask/src/codegen/grammar/ast_src.rs
@@ -150,6 +150,7 @@ const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
// "raw",
"readonly",
"sym",
+ "deref",
];
// keywords that are keywords depending on the edition